🆕 simple version publish

This commit is contained in:
xujiang 2023-12-08 23:50:22 +08:00
parent 32f50a47b2
commit 217bc78a72
433 changed files with 48848 additions and 60576 deletions

BIN
.DS_Store vendored

Binary file not shown.

67
.umirc.ts Normal file
View File

@ -0,0 +1,67 @@
import path from "path";
import { defineConfig } from "umi";
export default defineConfig({
dynamicImport: {
loading: "@/components/LoadingCp"
},
dva: {
immer: true
},
devtool: "source-map",
antd: {},
title: "趣谈前端-h5-dooring",
// exportStatic: {},
base: "/",
publicPath: "/",
outputPath: "dist",
esbuild: {},
routes: [
{
exact: false,
path: "/",
component: "@/layouts/index",
routes: [
{
path: "/",
component: "../pages/home"
},
{
path: "/editor",
component: "../pages/editor"
},
{
path: "/ide",
component: "../pages/ide"
},
{
path: "/help",
component: "../pages/help"
},
{
path: "/login",
component: "../pages/login"
},
{
path: "/mobileTip",
component: "../pages/mobileTip"
},
{
path: "/preview",
component: "../pages/editor/preview"
}
]
}
],
theme: {
"primary-color": "#2F54EB"
// "btn-primary-bg": "#2F54EB"
},
extraBabelPlugins: [["import", { libraryName: "zarm", style: true }]],
// sass: {},
alias: {
components: path.resolve(__dirname, "src/components/"),
utils: path.resolve(__dirname, "src/utils/"),
assets: path.resolve(__dirname, "src/assets/")
}
});

179
doc/.vuepress/config.js Executable file
View File

@ -0,0 +1,179 @@
module.exports = {
base: "/doc/",
title: "h5-dooring",
dest: "./doc-dist",
themeConfig: {
search: false,
searchMaxSuggestions: 10,
lastUpdated: "Last Updated",
nav: [
{ text: "首页", link: "/" },
{ text: "文档", link: "/zh/guide/" },
// { text: '1.X', link: '/zh/guide/' },
// { text: '语言', link: '/zh/guide/' },
{ text: "体验", link: "http://h5.dooring.cn" },
{ text: "github", link: "https://github.com/MrXujiang/h5-Dooring" }
],
sidebar: [
{
title: "基本介绍",
path: "/zh/guide/",
collapsable: false,
sidebarDepth: 1
},
{
title: "doring如何工作",
path: "/zh/guide/introduced",
collapsable: false,
sidebarDepth: 1
},
{
title: "快速上手",
path: "/zh/guide/startedQuickly",
collapsable: false,
sidebarDepth: 1
},
{
title: "目录结构",
path: "/zh/guide/directoryStructure",
collapsable: false,
sidebarDepth: 1
},
{
title: "组件开发",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
name: "componentStructure",
title: "组件结构",
path: "/zh/guide/componentDev/componentStructure",
collapsable: false,
sidebarDepth: 2
},
{
name: "DSLAnalysis",
title: "DSL设计",
path: "/zh/guide/componentDev/DSLAnalysis",
collapsable: false,
sidebarDepth: 2
},
{
name: "dynamicLoading",
title: "动态加载",
path: "/zh/guide/componentDev/dynamicLoading",
collapsable: false,
sidebarDepth: 1
}
]
},
{
title: "功能实现",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
title: "模板库",
path: "/zh/guide/functionRealization/templateLibrary",
collapsable: false,
sidebarDepth: 1
},
{
title: "保存json",
path: "/zh/guide/functionRealization/saveJson",
collapsable: false,
sidebarDepth: 1
},
{
title: "下载源码",
path: "/zh/guide/functionRealization/download",
collapsable: false,
sidebarDepth: 1
},
{
title: "网页预览",
path: "/zh/guide/functionRealization/pagePreview",
collapsable: false,
sidebarDepth: 1
},
{
title: "真机预览",
path: "/zh/guide/functionRealization/machinePreview",
collapsable: false,
sidebarDepth: 1
},
{
title: "撤销/重做",
path: "/zh/guide/functionRealization/revocation",
collapsable: false,
sidebarDepth: 1
},
{
title: "截图功能",
path: "/zh/guide/functionRealization/screenshot",
collapsable: false,
sidebarDepth: 1
}
]
},
{
title: "私有化部署和二次开发",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
title: "私有化部署",
path: "/zh/guide/deployDev/deploy",
collapsable: false,
sidebarDepth: 1
},
{
title: "v6.dooring私有化部署(临时)",
path: "/zh/guide/deployDev/deploy_v6",
collapsable: false,
sidebarDepth: 1
},
{
title: "服务端数据说明",
path: "/zh/guide/deployDev/dir",
collapsable: false,
sidebarDepth: 1
},
{
title: "支持https",
path: "/zh/guide/deployDev/https",
collapsable: false,
sidebarDepth: 1
},
{
title: "接入第三方oss",
path: "/zh/guide/deployDev/oss",
collapsable: false,
sidebarDepth: 1
},
{
title: "获取Form组件的值数据",
path: "/zh/guide/deployDev/form",
collapsable: false,
sidebarDepth: 1
},
{
title: "API接口文档",
path: "/zh/guide/deployDev/api",
collapsable: false,
sidebarDepth: 1
},
{
title: "更新日志",
path: "/zh/guide/deployDev/log",
collapsable: false,
sidebarDepth: 1
}
]
}
]
}
};

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -1,4 +1,4 @@
import './styles/main.scss';
import "./styles/main.scss";
// 使用异步函数也是可以的
export default ({
@ -6,7 +6,7 @@ export default ({
options, // 附加到根实例的一些选项
router, // 当前应用的路由实例
siteData, // 站点元数据
isServer, // 当前应用配置是处于 服务端渲染 或 客户端
isServer // 当前应用配置是处于 服务端渲染 或 客户端
}) => {
// ...做一些其他的应用级别的优化
// console.log({ Vue, options, router, siteData, isServer });

View File

@ -2,10 +2,9 @@
<main class="home" :aria-labelledby="data.heroText !== null ? 'main-title' : null">
<header class="hero">
<h1 v-if="data.heroText !== null" id="main-title">
<img
src="http://cdn.dooring.cn/dr/logo.ff7fc6bb.png"
:alt="data.heroAlt || 'hero'"
/><span>{{ data.heroText || $title || "Hello" }}</span>
<img src="../imgs/common/logo.svg" :alt="data.heroAlt || 'hero'" /><span>{{
data.heroText || $title || "Hello"
}}</span>
</h1>
</header>
@ -72,11 +71,12 @@ $homePageWidth = 1080px
text-align center
img
width: 190px
height 50px
display inline-block
margin initial
margin-bottom 0
margin-right 20px
vertical-align -21px
vertical-align sub
h1
font-size 1.5rem
font-weight 600

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

3
doc/.vuepress/theme/index.js Executable file
View File

@ -0,0 +1,3 @@
module.exports = {
extend: "@vuepress/theme-default"
};

16
doc/README.md Normal file
View File

@ -0,0 +1,16 @@
---
content: Home
home: true
#heroImage: ../imgs/common/logo.svg
heroText: 一款所见即所得的H5编辑器
features:
- title: 简洁方便
details: 任何人只需傻瓜式拖拽或进行简单编辑即可生成精美的H5页面
- title: 插拔式体验
details: 产品以GPL协议开源, 授权后可植入任何系统,并支持二次开发
- title: 持续迭代,无限可能
details: 目前正在持续迭代中,后续可根据需求开发功能更强大的可视化系统
actionText: 快速上手 →
actionLink: /zh/guide/
footer: GPL Licensed | Copyright © 2020-present H5-Dooring
---

View File

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 280 KiB

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 404 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 662 KiB

After

Width:  |  Height:  |  Size: 662 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View File

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

View File

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 155 KiB

View File

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 259 KiB

View File

@ -1,4 +1,4 @@
<img src="http://cdn.dooring.cn/dr/logo.ff7fc6bb.png" alt="dooring" width=200>
<img src="../../img/common/logo.svg" alt="foo">
H5-Dooring 是一款功能强大,高可扩展的 H5 可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳实践。
@ -6,12 +6,12 @@ H5-Dooring 是一款功能强大,高可扩展的 H5 可视化页面配置解
🎉 **可扩展,** Dooring 实现了较为完整的业务闭环,并使其模块化,编辑器内部功能接口也全部可以对接不同服务端语言,实现了标准化接口。此外还支持自定义组件,二次开发,设计模板等能力,以满足功能和跨领域的分层需求。
📦 **开箱即用,** Dooring 内置了**表单渲染器、页面渲染器、动态加载内核**等,仅需一套源码即可上手开发。并且还提供针对 React 的定制插件,内涵丰富的功能,可满足日常 30%-60%的页面制作需求。
📦 **开箱即用,** Dooring 内置了**表单渲染器、页面渲染器、动态加载内核**等,仅需一套源码即可上手开发。并且还提供针对 React 的定制插件,内涵丰富的功能,可满足日常 80%的页面制作需求。
🚀 **大量自研,** 包含整个编辑器架构、组件设计、文档、请求库封装,后台管理系统等,满足日常项目的周边需求。
🚄 **与时俱进,** 在满足需求的同时,我们也不会停止对新技术的探索。比如更多**营销组件, 可视化组件, 功能组件**等等。
🚄 **与时俱进,** 在满足需求的同时,我们也不会停止对新技术的探索。比如更多**营销组件、业务功能后台管理可视化PC 页面编辑器,数据大屏定制**等等。
## 为什么选择 Dooring
目前**github开源版**已超过 5400+star上线 2 个月累计 1600+用户使用,解决完善了 100+问题,后续会持续迭代,更新,自研优秀,先进的 lowcode/nocode 解决方案。
目前**github**已超过 3000+star上线 2 个月累计 500+用户使用,解决完善了 100+问题,后续会持续迭代,更新,自研优秀,先进的 lowcode/nocode 解决方案。

View File

@ -1,2 +1 @@
正在建设中...

View File

@ -0,0 +1,65 @@
# DSL 设计
DSL 层主要约定了 Dooring 组件的数据协议,包括组件的可编辑属性、编辑类型、初始值等,之所以定义一致的协议层,主要是方便后期的组件扩展,配置后移,有助于不同后端语言开发和数据存储,接下来我们看看 header 组件的 schema。
1.editData 可编辑的属性类型 DSL
2.config 可编辑组件的默认属性
```js
const Header: IHeaderSchema = {
editData: [
{
key: "bgColor",
name: "背景色",
type: "Color"
},
{
key: "height",
name: "高度",
type: "Number"
},
{
key: "logo",
name: "logo",
type: "Upload",
isCrop: true,
cropRate: 1000 / 618
},
{
key: "logoText",
name: "logo文字",
type: "Text"
},
{
key: "color",
name: "文字颜色",
type: "Color"
},
{
key: "fontSize",
name: "文字大小",
type: "Number"
}
],
config: {
bgColor: "rgba(245,245,245,1)",
logo: [
{
uid: "001",
name: "image.png",
status: "done",
url: `${serverUrl}/uploads/3_1740be8a482.png`
}
],
logoText: "页头Header",
fontSize: 20,
color: "rgba(47,84,235,1)",
height: 50
}
};
```
由以上代码可知,我们可以在 editData 属性中给组件添加可编辑的属性,比如背景图,然后再 component 中接受属性从而设置样式。
在 config 属性中,我们可以设置组件默认属性值,和 editData 中每一项的 key 一一对应。

View File

@ -4,22 +4,22 @@
* @LastEditTime: 2021-01-17 19:42:42
* @FilePath: /github-h5-Dooring/doc/zh/guide/componentDev/componentStructure.md
-->
# 组件结构
dooring的组件设计包含以下3个部分组件
dooring 的组件设计包含以下 3 个部分组件:
1、component 组件主体
1、component 组件主体
2、schema 组件的DSL结构协议层
2、schema 组件的 DSL结构协议层
3、template 定义了组件的类型、外观、从属关系后期考虑纳入schema
接下来我会介绍一个基本的组件主体设计以为template设计在下一章会具体介绍schema部分。
3、template 定义了组件的类型、外观、从属关系,后期考虑纳入 schema
接下来我会介绍一个基本的组件主体设计,以为 template 设计,在下一章会具体介绍 schema 部分。
## 组件设计
我们这里拿基本的header组件来举例如下是header组件的代码
我们这里拿基本的 header 组件来举例,如下是 header 组件的代码:
```jsx
interface HeaderPropTypes extends IHeaderConfig {
@ -30,12 +30,12 @@ const Header = memo((props: HeaderPropTypes) => {
const { bgColor, logo, logoText, fontSize, color } = props;
return props.isTpl ? (
<div>
< img style={{width: '100%'}} src={logos} alt="" />
<img style={{ width: "100%" }} src={logos} alt="" />
</div>
) : (
<header className={styles.header} style={{ backgroundColor: bgColor }}>
<div className={styles.logo}>
< img src={logo && logo[0].url} alt={logoText} />
<img src={logo && logo[0].url} alt={logoText} />
</div>
<div className={styles.title} style={{ fontSize, color }}>
{logoText}
@ -45,27 +45,26 @@ const Header = memo((props: HeaderPropTypes) => {
});
```
我们只需要按照上面的方式编写组件即可props是DSL定义的数据层用来控制组件的shape也就是组件的表现。我们看看header对应的template。
我们只需要按照上面的方式编写组件即可props DSL 定义的数据层,用来控制组件的 shape也就是组件的表现。我们看看 header 对应的 template。
## template设计
## template 设计
```js
const template = {
type: 'Header',
type: "Header",
h: 28,
displayName: '页头组件'
displayName: "页头组件"
};
export default template;
```
以上就是我们template的结构type用来定义组件的类型方便渲染器动态查找h代表组件的初始化高度我们可以自由设置。displayName是组件的中文名用来在左侧组件面板中展示方便用户理解我们可以在template中自定义更多辅助信息方便使用者更高效的使用我们的编辑器。
以上就是我们 template 的结构type 用来定义组件的类型方便渲染器动态查找h 代表组件的初始化高度我们可以自由设置。displayName 是组件的中文名,用来在左侧组件面板中展示,方便用户理解,我们可以在 template 中自定义更多辅助信息,方便使用者更高效的使用我们的编辑器。
## schema 设计
## schema设计
开发一个自定义组件需要包含 3 部分, `Component`, `Schema``Template`. 接下来我们看一下 `Header` 组件的 `Schema`.
开发一个自定义组件需要包含3部分, `Component`, `Schema``Template`. 接下来我们看一下 `Header` 组件的 `Schema`.
``` js
```js
import {
IColorConfigType,
INumberConfigType,
@ -74,9 +73,9 @@ import {
TColorDefaultType,
TNumberDefaultType,
TTextDefaultType,
TUploadDefaultType,
} from '@/components/FormComponents/types';
import { baseConfig, baseDefault, ICommonBaseType } from '../../common';
TUploadDefaultType
} from "@/components/FormComponents/types";
import { baseConfig, baseDefault, ICommonBaseType } from "../../common";
export type THeaderEditData = Array<
IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType
@ -99,54 +98,54 @@ const Header: IHeaderSchema = {
editData: [
...baseConfig,
{
key: 'bgColor',
name: '背景色',
type: 'Color',
key: "bgColor",
name: "背景色",
type: "Color"
},
{
key: 'height',
name: '高度',
type: 'Number',
key: "height",
name: "高度",
type: "Number"
},
{
key: 'logo',
name: 'logo',
type: 'Upload',
key: "logo",
name: "logo",
type: "Upload",
isCrop: true,
cropRate: 1000 / 618,
cropRate: 1000 / 618
},
{
key: 'logoText',
name: 'logo文字',
type: 'Text',
key: "logoText",
name: "logo文字",
type: "Text"
},
{
key: 'color',
name: '文字颜色',
type: 'Color',
key: "color",
name: "文字颜色",
type: "Color"
},
{
key: 'fontSize',
name: '文字大小',
type: 'Number',
},
key: "fontSize",
name: "文字大小",
type: "Number"
}
],
config: {
bgColor: 'rgba(0,0,0,1)',
bgColor: "rgba(0,0,0,1)",
logo: [
{
uid: '001',
name: 'image.png',
status: 'done',
url: 'http://49.234.61.19/uploads/3_1740be8a482.png',
},
uid: "001",
name: "image.png",
status: "done",
url: "http://49.234.61.19/uploads/3_1740be8a482.png"
}
],
logoText: '页头Header',
logoText: "页头Header",
fontSize: 20,
color: 'rgba(255,255,255,1)',
color: "rgba(255,255,255,1)",
height: 50,
...baseDefault,
},
...baseDefault
}
};
export default Header;
@ -154,12 +153,11 @@ export default Header;
`editData`表示组件的可编辑属性, 我们可以自定义哪些组件可编辑. `config`为组件接收的属性, 和`editData`数组项中的`key`一一对应.
### 组件编辑区属性类型
`Dooring`组件编辑面板有如下对应编辑类型:
- Upload 上传组件
- Upload 上传组件
- Text 文本框
- RichText 富文本
- TextArea 多行文本
@ -176,4 +174,3 @@ export default Header;
- Table 表格编辑器
- Pos 坐标编辑器
- FormItems 表单设计器

View File

@ -0,0 +1,18 @@
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 19:42:53
* @FilePath: /github-h5-Dooring/doc/zh/guide/componentDev/dynamicLoading.md
-->
# 组件动态加载
目前 H5-Dooring 的组件都是通过动态加载的方式引入,好处是我们在页面中只会加载我们需要的组件,不需要的组件不会被加载,这样可以提高页面加载的速度,这样做也会出现一些问题,比如一个长页面,配置了很多组件,那么一个页面加载过程可以会触发多次请求,目前还没有遇到性能问题,但后续会逐渐优化这个问题。
## umi3 提供的 dynamic
目前组件的动态加载我们采用的 umi 的 dynamic 方案,基于它我们上层封装了一个组件动态加载器,原理如下:
<img src="../../../img/componentDev/dynamic.png" alt="foo">
具体代码可以参考 Dooring 的 Github 地址:[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)

View File

@ -0,0 +1,608 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**后端部分主要使用 `Nodejs` 开发, 为了满足更多定制化需求和服务的可移植性, 特意编写了 API 接口文档,
方便大家使用不同的后端语言实现服务接入.
- 注意: 接口统一前缀为`/api/v0`
## 用户相关
### 用户登录
用户登录接口
- `POST` /vip/check
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -----: |
| n | true | string | 用户名 |
| co | true | string | 密码 |
返回示例
```json
{
"result": {
"n": "test",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"rp": "AAAAA",
"maxage": 300000
}
}
```
### 注销登录
注销接口
- `POST` /vip/checkout
返回示例
```json
{
"result": null,
"msg": "退出成功"
}
```
### 权限控制
不同用户级别所访问的页面权限不同, 这块可结合服务端已有代码设计属于自己的权限字段, 地址为`server/src/router`
### 用户列表
获取用户列表接口
- `GET` /vip/all
获取用户列表需要账号满足以下条件:
- 已登录
- 为超级管理员
返回示例
```json
[
{
"id": "",
"n": "test",
"co": "123456",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"wx": "Mr_xuxiaoxi",
"rp": "AAAAA"
}
]
```
### 添加用户
添加用户接口
- `POST` /vip/add
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| -------- | :------: | :----: | -----: |
| nickname | true | string | 用户名 |
| wx | true | string | 微信号 |
| co | true | string | 密码 |
注: co 是由笔者写的`加密算法`实现, 不需要手动填写, 详情见`dooirng`后台管理/用户管理页面.
返回示例
```json
{
"id": "3422EF",
"n": "test",
"wx": "Mr_xuxiaoxi",
"co": "123456",
"od": [],
"h5": [],
"tpl": [],
"rp": "AAAAA",
"h5Num": 10,
"tplNum": 3
}
```
### 生成登录码
生成登录码接口
- `GET` /vip/gcode
先决条件:
- 用户已登陆
- 为超级管理员
注: 生成登录码是由笔者写的`加密算法`实现, 不需要手动实现, 如果有自定义需求, 可以自行二次开发实现.
返回示例
```json
{
"co": "1x2fgggteee3456_zdd4"
}
```
`说明:` 为了保护用户信息安全, 返回的登录码是加密后的密文, 会调用笔者写的`xib.xip`方法进行加密, 如果想看到原始密码, 需要调用`xib.uxip`进行解密.
### 获取用户真实密码
获取用户真实密码接口
- `GET` /vip/gcode/get
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -----------: |
| co | true | string | 加密后的密码 |
返回示例
```json
{
"co": "12345678"
}
```
### 修改用户信息
修改用户接口
- `POST` /vip/edit
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| -------- | :------: | :----: | ------: |
| id | false | string | 用户 ID |
| nickname | false | string | 用户名 |
| co | false | string | 登录码 |
| wx | false | string | 微信号 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "修改成功"
}
```
### 删除用户
删除用户接口
- `DELETE` /vip/del
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ------: |
| id | true | string | 用户 ID |
| wx | true | string | 微信号 |
| n | true | string | 用户名 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## H5 页面管理
### 获取 H5 数据
- `GET` /visible/h5/get
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ---------: |
| tid | true | string | H5 唯一 id |
返回示例
```json
{
"pageConfig": {},
"tpl": [
{
"id": "879742",
"item": {
"type": "Carousel",
"config": {
"direction": "left",
"swipeable": false,
"autoPlay": false,
"imgList": [
{
"id": "1",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/1_1740bd7c3dc.png"
}
]
},
{
"id": "2",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/2_1740bd8d525.png"
}
]
}
],
"tplImg": "http://io.nainor.com/uploads/carousal_17442e1420f.png"
},
"h": 82,
"editableEl": [
{
"key": "direction",
"name": "方向",
"type": "Radio",
"range": [
{
"key": "down",
"text": "从上到下"
},
{
"key": "left",
"text": "从左到右"
}
]
},
{
"key": "swipeable",
"name": "是否可拖拽",
"type": "Switch"
},
{
"key": "autoPlay",
"name": "是否自动播放",
"type": "Switch"
},
{
"key": "imgList",
"name": "图片列表",
"type": "DataList"
}
],
"category": "base"
},
"point": {
"i": "x-0",
"x": 0,
"y": 13,
"w": 24,
"h": 82,
"isBounded": true
},
"status": "inToCanvas"
},
{
"id": "481194",
"item": {
"type": "Form",
"config": {
"title": "表单定制组件",
"fontSize": 18,
"titColor": "rgba(60,60,60,1)",
"titWeight": "400",
"bgColor": "rgba(255,255,255,1)",
"btnColor": "rgba(20,54,226,100)",
"btnTextColor": "rgba(255,255,255,1)",
"api": "",
"formControls": [
{
"id": "1",
"type": "Text",
"label": "姓名",
"placeholder": "请输入姓名"
},
{
"id": "2",
"type": "Number",
"label": "年龄",
"placeholder": " 请输入年龄"
},
{
"id": "4",
"type": "MySelect",
"label": "爱好",
"options": [
{
"label": "选项一",
"value": "1"
},
{
"label": "选项二",
"value": "2"
},
{
"label": "选项三",
"value": "3"
}
]
}
]
},
"h": 172,
"category": "base"
},
"point": {
"i": "x-1",
"x": 0,
"y": 98,
"w": 24,
"h": 172,
"isBounded": true
},
"status": "inToCanvas"
}
]
}
```
### 保存 H5 数据
- `POST` /visible/h5/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ---------- | :------: | :----: | ------------------: |
| pageConfig | false | object | H5 页面配置数据 |
| tpl | true | object | H5 页面组件配置数据 |
| tid | true | string | H5 页面唯一 id |
参数示例
```json
{
"pageConfig": {
"bgColor": "rgba(151,25,25,1)",
"title": "医院宣传页"
},
"tpl": [],
"tid": "EF123D3"
}
```
返回示例
```json
{
"state": 200,
"result": {
"tid": "EF123D3"
},
"msg": "保存成功"
}
```
### 删除 H5 数据
- `DELETE` /visible/h5/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -------------: |
| tid | true | string | H5 页面唯一 id |
返回示例
```json
{
"state": 200,
"result": [
{
"tid": "EF123D3",
"name": "test页面"
},
{
"tid": "EF123D6",
"name": "test2页面"
}
],
"msg": "删除成功"
}
```
## H5 表单数据管理
### 保存表单数据
- `POST` /vip/h5/form/post
| 参数名 | 是否必选 | 类型 | 说明 |
| -------------- | :------: | :----: | --------------: |
| tid(query) | true | string | H5 页面唯一 id |
| formData(body) | true | array | H5 页面表单数据 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "表单提交成功"
}
```
### 批量导入表单数据
- `POST` /vip/h5/form/import
| 参数名 | 是否必选 | 类型 | 说明 |
| -------------- | :------: | :----: | ------------------: |
| tid(query) | true | string | H5 页面唯一 id |
| formData(body) | true | array | H5 页面表单数据集合 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "批量导入成功"
}
```
### 删除表单数据
- `DELETE` /vip/h5/form/del
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -------------: |
| tid | true | string | H5 页面唯一 id |
| ID | true | string | 表单专属 id |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 模版管理
### 获取模版库
- `GET` /visible/tpls/free
返回示例
```json
{
"state": 200,
"result": [
{
"img": "http://xxx/uploads/tpl_175adabd8dd.jpg",
"name": "合作模版",
"tid": "B73349B6"
}
]
}
```
### 保存模版
- `POST` /visible/tpl/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ---------- | :------: | :----: | --------------: |
| name | true | string | H5 模版名称 |
| cate | true | string | H5 模版分类 |
| img | false | string | H5 模版封面图 |
| tpl | true | array | H5 模版数据 |
| pageConfig | false | object | H5 模版全局配置 |
返回示例
```json
{
"state": 200,
"result": {
"tid": "B73349B6"
},
"msg": "保存成功"
}
```
### 删除模版
- `DELETE` /visible/tpl/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ---------: |
| tid | true | string | H5 模版 id |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 文件上传
## 数据统计
### 数据大盘接口
### 页面埋点

View File

@ -5,13 +5,14 @@
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
私有化部署需要获取4个核心项目包, 包括
- H5编辑器(editor)
- H5基座(h5)
- Dooring管理后台(iH5)
私有化部署需要获取 4 个核心项目包, 包括
- H5 编辑器(h5_plus)
- H5 基座(h5)
- Dooring 管理后台(Dooring-Admin)
- 服务端项目(Server)
获取以上四个核心源码工程需要满足商业授权协议, 具体可联系作者[徐小夕](http://h5.dooring.cn/uploads/WechatIMG3_1758e9753e2.jpeg)
获取以上四个核心源码工程需要满足商业授权协议, 具体可联系作者[徐小夕](http://h5.dooring.cn/uploads/WechatIMG3_1758e9753e2.jpeg)
### 部署架构图
@ -19,29 +20,31 @@
部署流程如下:
1. 下载4个源码工程, 安装依赖(npm install 或 yarn)
2. 打包3个前端工程至`server`的static目录下
1. 下载 4 个源码工程, 安装依赖(npm install 或 yarn)
2. 打包 3 个前端工程至`server` static 目录下
3. 在`server`下本地运行 `yarn start``npm start` 启动服务端进行本地测试
4. 打包服务端代码, `yarn build` 生成 `dist` 目录, 建议使用 `pm2``nodejs`服务的负载均衡, 运行 `pm2 start dist/index.js`启动生产环境代码
也可以将以上步骤集成到gitlab等CI, CD服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
也可以将以上步骤集成到 gitlab CI, CD 服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
### 步骤3.4详细流程
### 步骤 3.4 详细流程
#### 1. 安装项目环境
服务器需提前安装node和pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
npm install
```
#### 2. 修改项目域名
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com``http://xxx.com:8080`(如非80端口)
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com``http://xxx.com:8080`(如非 80 端口)
#### 3. 编译项目
执行`npm run build`编译项目, 生成`dist`目录
#### 4. 运行项目
在项目根目录执行 `pm2 start dist/index.js`启动项目

View File

@ -5,9 +5,10 @@
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
私有化部署需要获取3个核心项目包, 包括
- 可视化大屏编辑器(v6.dooring/)
- v6管理后台(v6.dooring/manage)
私有化部署需要获取 3 个核心项目包, 包括
- 可视化大屏编辑器(v6.dooring/)
- v6 管理后台(v6.dooring/manage)
- 服务端项目(v6.dooring/server)
获取以上三个核心源码工程需要满足商业授权协议, 具体可参考[商业授权方案](http://h5.dooring.cn/h5_plus/price)
@ -18,29 +19,31 @@
部署流程如下:
1. 下载3个源码工程, 安装依赖(npm install 或 yarn)
2. 打包2个前端工程至`server`的static目录下
1. 下载 3 个源码工程, 安装依赖(npm install 或 yarn)
2. 打包 2 个前端工程至`server` static 目录下
3. 在`server`下本地运行 `yarn start``npm start` 启动服务端进行本地测试
4. 打包服务端代码, `yarn build` 生成 `dist` 目录, 建议使用 `pm2``nodejs`服务的负载均衡, 运行 `pm2 start dist/index.js`启动生产环境代码
也可以将以上步骤集成到gitlab等CI, CD服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
也可以将以上步骤集成到 gitlab CI, CD 服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
### 步骤3.4详细流程
### 步骤 3.4 详细流程
#### 1. 安装项目环境
服务器需提前安装node和pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
npm install
```
#### 2. 修改项目域名
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com``http://xxx.com:8080`(如非80端口)
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com``http://xxx.com:8080`(如非 80 端口)
#### 3. 编译项目
执行`npm run build`编译项目, 生成`dist`目录
#### 4. 运行项目
在项目根目录执行 `pm2 start dist/index.js`启动项目

View File

@ -6,16 +6,16 @@
-->
服务端主要是我们的`server`工程, 数据主要存放在`server/public`下, 具体数据指代含义我们接下来会详细介绍.
- bed 存放图片库中的分类图片, 私有化部署的用户可以直接在此处扩充图片(更好的建议是直接存到第三方图床)
- h5 用户保存的h5数据文件, 一个页面对应一个json文件
- h5_tpl 平台保存的模版数据文件夹
- xxx.json 模版页面文件
- tpls.json 模版库中的模版列表数据, 可以手动清空
- h5_vip 会员数据目录
- form 会员制作的含表单页面的表单收集数据
- view.json 用户浏览量数据
- vip.json 会员列表数据
- vipCard.json 会员订单数据(暂时无用, 可删除)
- image.json 图片库, 主要用来渲染页面的图片库数据
- city.json 省市3级联动数据, 为表单组件提供数据支持
- bed 存放图片库中的分类图片, 私有化部署的用户可以直接在此处扩充图片(更好的建议是直接存到第三方图床)
- h5 用户保存的 h5 数据文件, 一个页面对应一个 json 文件
- h5_tpl 平台保存的模版数据文件夹
- xxx.json 模版页面文件
- tpls.json 模版库中的模版列表数据, 可以手动清空
- h5_vip 会员数据目录
- form 会员制作的含表单页面的表单收集数据
- view.json 用户浏览量数据
- vip.json 会员列表数据
- vipCard.json 会员订单数据(暂时无用, 可删除)
- image.json 图片库, 主要用来渲染页面的图片库数据
- city.json 省市 3 级联动数据, 为表单组件提供数据支持

View File

@ -0,0 +1,43 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
### 获取 Form 组件的值数据
Form 表单组件在`editor`目录下`src/components/BasicShop/BasicComponents`位置.
Form 组件是`Dooring`的核心组件之一, 内部的值通过 Form 组件内部收集, 当然我们也可以暴露出来让其他交互或者组件消费(需要一定的二次开发), 关键代码如下:
```js
req
.post(`/vip/h5/form/post${location.search}`, { ...fields, ...formData })
.then(res => {
if (type === "link") {
// 解析参数
let isPre = content.indexOf("?") < 0;
let query = { dr: Date.now(), from: urlParmas.tid };
try {
query = params ? { ...JSON.parse(params), ...query } : query;
} catch (err) {
console.log(err);
}
// 跳转
if (content.indexOf("http") > -1) {
window.location.href = content + urlencode(query, isPre);
return;
}
history.push(`/m?tid=${content}&${urlencode(query)}`);
} else if (type === "modal") {
setVisible(true);
} else if (type === "code") {
eval(content);
}
});
```
数据收集提交的核心代码在 Form 组件的第 56-149 行, 也就是`submit`方法. 表单组件收集到的数据统一存放在代码中的`formData`字段, 所以要想在其他地方获取用户表单填写的值, 我们只需要手动将`formData`传递出去, 或者挂载到全局(如 window 对象, localStorage, indexedDB 等).

View File

@ -0,0 +1,62 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
目前**H5-Dooring**全面支持 https 部署, 具体方式方案如下.
### 前端工程
我们需要在前端工程中的`src/pages/document.ejs`中的`head`中添加如下代码:
```html
<meta
http-equiv="Content-Security-Policy"
content="upgrade-insecure-requests"
/>
```
目的是强制将页面中 HTTP 请求转换为 HTTPS.
### 服务器工程
#### 1. 申请 SSL 证书
#### 2. 生成 server.csr+server.key
#### 3. 通过证书链生成.pem 文件
#### 在`server`中的`src/index.js`按如下方式修改
```js
// 忽略部分无影响代码
import https from "https";
// 你的ssl存放路径, 建议直接放在server目录下
const filePath = path.join(__dirname, "../ssl");
// 启动逻辑
async function start() {
// https配置
const httpsOptions = {
key: fs.readFileSync(path.join(filePath, "3536084__doctopia.com.cn.key")), //ssl文件路径
cert: fs.readFileSync(path.join(filePath, "3536084__doctopia.com.cn.pem")) //ssl文件路径
};
// https服务
const server = https.createServer(httpsOptions, app.callback());
const io = require("socket.io")(server);
// 忽略其他无影响代码
// https默认443, 这里我们可以走公共配置
server.listen(443, () => {
console.log(`服务器地址:${config.staticPath}`);
});
}
start();
```

View File

@ -7,60 +7,40 @@
### 更新日志
#### 2.01
1. 优化编辑器加载性能
2. iframe容器组件添加边框等属性
3. 富文本组件添加背景色配置
4. 修复真机预览时空数据还能显示二维码bug
5. 优化页面高度适配问题, 添加高度适配器
6. 优化组件交互时空链接点击出现message bug
7. 更新dooring文档
#### 2.0
1. 完善数据源功能
2. 轮播图/图片列表/List/文字跑马灯/横向滚动组件数据源对接完毕
3. 组件库支持搜索功能
4. 组件列表支持分页, 提高渲染性能
5. 赞助墙交互优化
6. 界面局部优化
#### 1.99
1. 添加数据源功能
2. 视频组件支持封面图
3. 优化页面DSL结构, 降低了jsonSchema体积30%-50%
3. 优化页面 DSL 结构, 降低了 jsonSchema 体积 30%-50%
4. 官网优化
5. 管理后台替换logo, 部分文案信息
5. 管理后台替换 logo, 部分文案信息
6. 添加图片列表组件
7. 轮播图支持一键绑定数据源
#### 1.98
1. 编辑器功能区添加更多折叠下拉框, 优化头部界面
2. 添加数据源入口和界面
3. 模版库优化, 剔除无用模版, 累计60+模版
3. 模版库优化, 剔除无用模版, 累计 60+模版
4. 入口页添加赞助墙
5. 升级视频组件, 支持弹幕, 截屏, 模式设置等功能
6. 文件上传路径兼容window服务器本地化部署
6. 文件上传路径兼容 window 服务器本地化部署
#### 1.96
1. 修复首页推荐项目外链地址和站内文案
2. 替换Dooring网站logo
3. 优化ios8以下访问H5时可能出现的页面卡顿问题
4. 图片上传组件添加svg, gif图片格式支持
2. 替换 Dooring 网站 logo
3. 优化 ios8 以下访问 H5 时可能出现的页面卡顿问题
4. 图片上传组件添加 svg, gif 图片格式支持
5. 后台管理系统添加一键跳编辑器按钮
6. 服务端编辑侧路由加固
7. 文件上传组件添加自定义上传文档支持七牛云腾讯云阿里oss等第三方图床方式
7. 文件上传组件添加自定义上传文档,支持七牛云,腾讯云,阿里 oss 等第三方图床方式
#### 1.95
1. dooring文档添加更新日志模块
2. dooring增报错监控函数, 提供一键清空缓存按钮和自动重载功能
3. 新增电商商品H5模版
1. dooring 文档添加更新日志模块
2. dooring 增报错监控函数, 提供一键清空缓存按钮和自动重载功能
3. 新增电商商品 H5 模版
4. 页面配置增加背景模式和背景重复
5. 表单添加字段名配置项
@ -71,7 +51,7 @@
3. 优化页面控制条组件样式
4. 按钮组件添加组件动画
5. 图片组件添加组件动画
6. 媒体组件icon优化
6. 媒体组件 icon 优化
7. 全局错误监控组件添加一键清除缓存功能
#### 1.93
@ -89,8 +69,3 @@
3. 界面局部调整
4. 后台管理表单数据支持多键查询
5. 可视化大屏柱状图组件支持实时数据请求

View File

@ -0,0 +1,134 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**全面支持第三方对象存储服务, 我们以七牛云对象存储为例.
### 前端上传文件到 oss
首先我们需要在第三方对象储存服务中配置对应的服务和域名. 其次安装对应的 sdk, 如七牛云 sdk:
```js
import * as qiniu from "qiniu-js";
```
其次我们修改`h5_plus`工程的`Upload`组件, 详细地址为`src/core/FormComponents/Upload`.
修改内容如下:
```js
const fileName = file.name;
const suffix = "自定义文件后缀";
const putExtra = {
fname: fileName,
params: {}
};
const uid = +new Date() + uuid(16, 8) + suffix;
// 使用七牛云上传api, 前提是提前在前端拿到对应的ticket, 可以通过请求的方式获取
const observe = qiniu.upload(
file,
uid,
this.state.qnToken.ticket,
putExtra,
{}
);
observe.subscribe(
() => {},
null,
res => {
// 拼接路径
const url = `${this.state.qnToken.domain}/${res.key}`;
// 存库
const fileList = [{ uid, name: fileName, status: "done", url }];
this.setState({
curImgUrl: url,
fileList
});
this.props.onChange && this.props.onChange(fileList);
}
);
```
其他 oss 服务类似, 如果不清楚如何配置, 可以在[H5-Dooring 官网](http://h5.dooring.cn/)中找到我们.
### 如何接入任何第三方上传服务
首先我们的上传组件`Upload`使用内部的服务接口来实现上传功能, 所以需要给组件的`action`赋值, 如下:
```jsx
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
onRemove={this.handleRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={sdk_upload_api || action}
withCredentials={withCredentials}
headers={{
"x-requested-with": localStorage.getItem("user") || "",
authorization: localStorage.getItem("token") || "",
...headers
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
如果需要集成第三方 oss, 如七牛云, 阿里 oss 等, 我们需要将`Upload`组件的`action`属性设置为空字符串, 其次删除`onChange`属性, 上传操作统一在`beforeUpload`中进行. 案例如下:
```jsx
<Upload
fileList={fileList}
action=""
onPreview={this.handlePreview}
onRemove={this.onRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
headers={{ ...headers }}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
自定义上传的核心逻辑放在了`beforeUpload`上. 我们具体看看`beforeUpload`这个方法如何实现.
```js
handleBeforeUpload = (file: RcFile) => {
// 1. 限制图片类型
const isJpgOrPng =
file.type === "image/jpeg" ||
file.type === "image/png" ||
file.type === "image/jpg" ||
file.type === "image/gif";
if (!isJpgOrPng) {
message.error("只能上传格式为jpeg/png/gif的图片");
}
// 限制上传文件大小
const isLt3M = file.size / 1024 / 1024 < 3;
if (!isLt3M) {
message.error("图片必须小于3MB!");
}
if (isJpgOrPng && isLt3M) {
// 3. 正常上传逻辑
const fileName = file.name;
// 3.1 调用oss接口, 将图片上传oss
// 3.2 将接口返回的url信息, 组装成fileList数据结构, 并更新state
const fileList = [{ uid, name: fileName, status: "done", url }];
this.setState({
curImgUrl: url,
fileList
});
// 3.3 将数据传给上层保存
this.props.onChange && this.props.onChange(fileList);
}
return isJpgOrPng && isLt3M;
};
```

View File

@ -4,15 +4,17 @@
* @LastEditTime: 2021-05-17 21:32:58
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/saveJson.md
-->
## 下载源码
目前Dooring已支持下载源码功能, 我们可以使用编辑器页面头部的下载按钮来实现下载用户搭建的H5源码.
目前 Dooring 已支持下载源码功能, 我们可以使用编辑器页面头部的下载按钮来实现下载用户搭建的 H5 源码.
<img src="../../../img/functionRealization/down.png" alt="foo">
源码下载之后是完整的React项目源代码, 开发人员可以直接根据自己的业务需求来二次编写代码来满足不同的业务需求.
源码下载之后是完整的 React 项目源代码, 开发人员可以直接根据自己的业务需求来二次编写代码来满足不同的业务需求.
在拿到源码之后, 我们需要进入项目, 使用npm或者yarn安装项目依赖, 如下:
在拿到源码之后, 我们需要进入项目, 使用 npm 或者 yarn 安装项目依赖, 如下:
``` bash
```bash
npm install
// 或者
yarn
@ -20,16 +22,18 @@ yarn
之后我们就可以本地运行项目了:
``` bash
```bash
npm start
// 或者
yarn start
```
因为源码工程采用`umi3.0`搭建, 所以代码配置可以参考`umi3.0`规范, 比如路由配置, `history`模式, 打包路径等, 二次开发完成之后, 我们可以执行:
``` bash
```bash
npm run build
// 或者
yarn build
```
将项目打包成html, 以便部署到任何服务器中.
将项目打包成 html, 以便部署到任何服务器中.

View File

@ -4,6 +4,7 @@
* @LastEditTime: 2021-01-17 21:49:26
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/machinePreview.md
-->
# 真机预览
真机预览和网页预览的流程类似,工作流程如下:

View File

@ -4,6 +4,7 @@
* @LastEditTime: 2021-01-17 21:49:18
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/pagePreview.md
-->
# 网页预览
我们看看网页预览的工作流程:

View File

@ -0,0 +1,38 @@
<!--
* @Date: 2021-01-17 14:27:28
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:50:17
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/revocation.md
-->
# 撤销/重做
撤销重做我们主要使用了 redux-undo 这个库,配合 Dva 使用,具体使用方法参考如下操作:
```js
import { createLogger } from "redux-logger";
import { message } from "antd";
import undoable, { StateWithHistory } from "redux-undo";
import { Reducer, AnyAction } from "redux";
export const dva = {
config: {
onAction: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
onReducer: (reducer: Reducer<any, AnyAction>) => {
let undoReducer = undoable(reducer);
return function(state: StateWithHistory<any>, action: AnyAction) {
let newState = undoReducer(state, action);
let router = newState.present.router
? newState.present.router
: newState.present.routing;
return { ...newState, router: router };
};
}
}
};
```
以上我们就实现了全局配置 redux-undo在撤销重做按钮中我们就可以触发对应的方法来实现撤销重做的功能其次我们还使用了 redux-logger 来实现 redux 的日志输出。

View File

@ -0,0 +1,12 @@
<!--
* @Date: 2021-01-17 14:26:00
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:32:58
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/saveJson.md
-->
# 保存 json
我们配置好 H5 页面之后,如果希望其他人观看,我们可以保存页面并发送链接。但是如果有多人协作的需求,比如一个 H5 页面可能由多个人完成,这个时候该怎么实现呢?基于已有的方案,我们可以采用 socket 实现多人协同编辑,但是成本比较大,所有这里我们提供了保存 json 的功能。
我们可以将配置好的页面导出为 json发送给另一个人这样另一个人通过导入该 json 文件可以实时看到当前的页面,这里还是依靠我们的页面渲染引擎 viewEngine。实现思路也很简单可以在 github[[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)]上参考体验。

View File

@ -4,8 +4,9 @@
* @LastEditTime: 2021-01-17 21:49:46
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/screenshot.md
-->
# 截图功能
截图功能这里我们主要使用了dom-to-image这个库来将html转化为图片并进行分享。
截图功能这里我们主要使用了 dom-to-image 这个库,来将 html 转化为图片,并进行分享。
<img src="../../../img/functionRealization/screenshot.png" alt="foo">

View File

@ -4,9 +4,10 @@
* @LastEditTime: 2021-01-17 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/templateLibrary.md
-->
## 模板库实现思路
我们目前开放了模板库功能一方面我们会定期配置行业模板另一个方面Dooring还支持用户自己配置模板可以一键保存到云端供用户使用。我们也可以将模板变成自己的页面共享给其他人。实现方式本质上是保存用户的配置信息上传到服务器中做存储在后台提供了管理模板的模块可以修改删除模板。如下图所示
我们目前开放了模板库功能,一方面我们会定期配置行业模板,另一个方面 Dooring 还支持用户自己配置模板,可以一键保存到云端供用户使用。我们也可以将模板变成自己的页面共享给其他人。实现方式本质上是保存用户的配置信息,上传到服务器中做存储,在后台提供了管理模板的模块,可以修改,删除模板。如下图所示:
### 模板前台展示:

View File

@ -0,0 +1,3 @@
<img src="../../img/common/framework.png" alt="foo">
注:灰色部分还未实现,正在更新中...

View File

@ -1,10 +1,9 @@
# 快速上手
## 从零搭建一个H5表单页面
## 从零搭建一个 H5 表单页面
<iframe src="//player.bilibili.com/player.html?aid=715343955&bvid=BV1QQ4y1Z725&cid=332145157&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 100%;height: 420px; margin-top: 20px"> </iframe>
## 环境准备
首先得有 node并确保 node 版本是 `10.13` 或以上mac/win 下推荐使用 n 来管理 node 版本)
@ -31,9 +30,3 @@ v10.13.0
2.其次本地启动 h5_plus,启动完毕在浏览器打开对应的启动地址即可查看,如下:
<img src="../../img/common/home.png" alt="foo">
## 项目路径说明
- `/h5_plus` H5编辑器项目
- `/iH5` Dooring后台管理系统
- `/doc` Dooring文档

View File

@ -1,3 +0,0 @@
#!/bin/bash
cd ./packages/editor && yarn
cd ../ui && yarn

View File

@ -1,6 +0,0 @@
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}

24298
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
{
"name": "h5-dooring",
"version": "1.4.0",
"name": "h5-dooring",
"version": "1.3.0",
"description": "H5-Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。技术栈以react为主 后台采用nodejs开发。",
"private": false,
"author": {
"name": "徐小夕",
"email": "xujiang156@qq.com",
"url": "http://h5.dooring.cn"
"url": "http://h5.dooring.cn/h5_visible"
},
"keywords": [
"h5 editor",
@ -22,17 +22,12 @@
"yehuozhili <yehuozhili@outlook.com> (https://github.com/yehuozhili))"
],
"scripts": {
"pkg": "bash install.sh",
"start": "npm run start:ui & npm run start:editor",
"start:editor": "npx lerna exec --scope @h5-dooring/editor -- npm run start",
"start:ui": "npx lerna exec --scope @h5-dooring/ui -- npm run start",
"build": "npm run build:ui && npm run build:editor",
"build:editor": "npx lerna exec --scope @h5-dooring/editor -- npm run build",
"build:ui": "npx lerna exec --scope @h5-dooring/ui -- npm run build",
"docs:dev": "npx lerna exec --scope @h5-dooring/doc -- npm run dev",
"docs:build": "npx lerna exec --scope @h5-dooring/doc -- npm run build",
"start": "umi dev",
"build": "umi build",
"server": "node server.js",
"dev": "http-server dist",
"docs:dev": "vuepress dev doc",
"docs:build": "vuepress build doc",
"test-demo": "http-server dist",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
@ -40,7 +35,17 @@
"test:coverage": "umi-test --coverage",
"nocompress": "cross-env RM_TMPDIR=none COMPRESS=none umi build"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,less,md,json}": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
},
"homepage": "http://h5.dooring.cn",
"repository": {
"type": "git",
@ -49,7 +54,81 @@
"bugs": {
"url": "https://github.com/MrXujiang/h5-Dooring/issues"
},
"dependencies": {
"@ant-design/icons": "^4.2.1",
"@antv/f2": "^3.7.7",
"@uiw/react-baidu-map": "^1.17.3",
"@umijs/plugin-esbuild": "^1.0.1",
"@umijs/plugin-sass": "^1.1.1",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.2.19",
"antd": "^4.7.0",
"antd-img-crop": "^3.10.0",
"axios": "^0.19.2",
"braft-editor": "^2.3.9",
"chatbot-antd": "^0.6.0",
"codemirror": "^5.57.0",
"dom-to-image": "^2.6.0",
"file-saver": "^2.0.2",
"http-server": "^0.12.3",
"keymaster": "^1.6.2",
"qrcode.react": "^1.0.0",
"react": "^16.12.0",
"react-audio-player": "^0.14.0",
"react-codemirror2": "^7.2.1",
"react-color": "^2.18.1",
"react-contexify": "^4.1.1",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "^16.12.0",
"react-draggable": "^4.4.3",
"react-draggable-ball": "^0.1.0",
"react-grid-layout": "^1.0.0",
"react-hotkeys-hook": "^2.3.1",
"react-text-loop": "^2.3.0",
"redux-undo": "^1.0.1",
"socket.io-client": "^2.3.0",
"umi": "^3.2.19",
"video-react": "^0.14.1",
"xlsx": "^0.16.7",
"yh-react-popover": "^0.3.0",
"yorkie": "^2.0.0",
"zarm": "^2.5.1"
},
"license": "GPL-3.0",
"devDependencies": {
"lerna": "^4.0.0"
"@types/classnames": "^2.2.10",
"@types/codemirror": "^0.0.98",
"@types/events": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/node": "^14.6.2",
"@types/qrcode.react": "^1.0.1",
"@types/react-color": "^3.0.4",
"@types/react-grid-layout": "^1.1.0",
"@types/redux-logger": "^3.0.8",
"@types/xlsx": "^0.0.36",
"@typescript-eslint/eslint-plugin": "4.1.1",
"@typescript-eslint/parser": "4.1.1",
"babel-eslint": "10.x",
"babel-plugin-import": "^1.13.0",
"cross-env": "^7.0.2",
"eslint": "6.x",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "4.x",
"eslint-plugin-import": "2.x",
"eslint-plugin-jsx-a11y": "6.x",
"eslint-plugin-react": "7.x",
"eslint-plugin-react-hooks": "2.x",
"koa": "^2.13.0",
"koa-body": "^4.2.0",
"koa-logger": "^3.2.1",
"koa-static": "^5.0.0",
"koa2-cors": "^2.0.6",
"lint-staged": "^10.0.7",
"prettier": "^1.19.1",
"redux-logger": "^3.0.6",
"sass-loader": "^9.0.3",
"typescript": "^4.0.2",
"vuepress": "^1.8.0"
}
}

View File

@ -1,220 +0,0 @@
module.exports = {
base: '/doc/',
title: 'h5-dooring',
dest: './doc-dist',
themeConfig: {
search: false,
searchMaxSuggestions: 10,
lastUpdated: 'Last Updated',
nav: [
{ text: '首页', link: '/' },
{ text: '文档', link: '/zh/guide/' },
// { text: '1.X', link: '/zh/guide/' },
// { text: '语言', link: '/zh/guide/' },
{ text: '体验', link: 'http://h5.dooring.cn' },
{ text: '私有化部署', link: 'http://h5.dooring.cn/h5_plus/price' },
],
sidebar: [
{
title: '基本介绍',
path: '/zh/guide/',
collapsable: false,
sidebarDepth: 1,
},
{
title: 'doring如何工作',
path: '/zh/guide/introduced',
collapsable: false,
sidebarDepth: 1,
},
{
title: '产品介绍',
path: '/zh/guide/product',
collapsable: false,
sidebarDepth: 1,
},
{
title: '快速上手',
path: '/zh/guide/startedQuickly',
collapsable: false,
sidebarDepth: 1,
},
{
title: '目录结构',
path: '/zh/guide/directoryStructure',
collapsable: false,
sidebarDepth: 1,
},
{
title: '组件开发',
collapsable: false,
sidebarDepth: 1,
type: 'group',
children: [
{
name: 'componentStructure',
title: '组件结构',
path: '/zh/guide/componentDev/componentStructure',
collapsable: false,
sidebarDepth: 2,
},
{
name: 'DSLAnalysis',
title: 'DSL设计',
path: '/zh/guide/componentDev/DSLAnalysis',
collapsable: false,
sidebarDepth: 2,
},
{
name: 'dynamicLoading',
title: '动态加载',
path: '/zh/guide/componentDev/dynamicLoading',
collapsable: false,
sidebarDepth: 1,
},
],
},
{
title: '功能实现',
collapsable: false,
sidebarDepth: 1,
type: 'group',
children: [
{
title: '模板库',
path: '/zh/guide/functionRealization/templateLibrary',
collapsable: false,
sidebarDepth: 1,
},
{
title: '保存json',
path: '/zh/guide/functionRealization/saveJson',
collapsable: false,
sidebarDepth: 1,
},
{
title: '下载源码',
path: '/zh/guide/functionRealization/download',
collapsable: false,
sidebarDepth: 1,
},
{
title: '网页预览',
path: '/zh/guide/functionRealization/pagePreview',
collapsable: false,
sidebarDepth: 1,
},
{
title: '真机预览',
path: '/zh/guide/functionRealization/machinePreview',
collapsable: false,
sidebarDepth: 1,
},
{
title: '撤销/重做',
path: '/zh/guide/functionRealization/revocation',
collapsable: false,
sidebarDepth: 1,
},
{
title: '截图功能',
path: '/zh/guide/functionRealization/screenshot',
collapsable: false,
sidebarDepth: 1,
},
],
},
{
title: '组件商店',
collapsable: false,
sidebarDepth: 1,
type: 'group',
children: [
{
name: 'intro',
title: '基本介绍',
path: '/zh/guide/componentShop/intro',
collapsable: false,
sidebarDepth: 2,
},
{
name: 'uploadCp',
title: '上传组件',
path: '/zh/guide/componentShop/uploadCp',
collapsable: false,
sidebarDepth: 2,
},
{
name: 'cpManage',
title: '组件审核',
path: '/zh/guide/componentShop/cpManage',
collapsable: false,
sidebarDepth: 1,
},
],
},
{
title: '私有化部署和二次开发',
collapsable: false,
sidebarDepth: 1,
type: 'group',
children: [
{
title: '私有化部署',
path: '/zh/guide/deployDev/deploy',
collapsable: false,
sidebarDepth: 1,
},
{
title: 'v6.dooring私有化部署(临时)',
path: '/zh/guide/deployDev/deploy_v6',
collapsable: false,
sidebarDepth: 1,
},
{
title: '服务端数据说明',
path: '/zh/guide/deployDev/dir',
collapsable: false,
sidebarDepth: 1,
},
{
title: '支持https',
path: '/zh/guide/deployDev/https',
collapsable: false,
sidebarDepth: 1,
},
{
title: '接入第三方oss',
path: '/zh/guide/deployDev/oss',
collapsable: false,
sidebarDepth: 1,
},
{
title: '获取Form组件的值数据',
path: '/zh/guide/deployDev/form',
collapsable: false,
sidebarDepth: 1,
},
{
title: 'API接口文档',
path: '/zh/guide/deployDev/api',
collapsable: false,
sidebarDepth: 1,
},
{
title: '更新日志',
path: '/zh/guide/deployDev/log',
collapsable: false,
sidebarDepth: 1,
},
],
},
{
title: 'FAQ',
path: '/zh/guide/FAQ',
collapsable: false,
sidebarDepth: 1,
},
],
},
};

View File

@ -1,3 +0,0 @@
module.exports = {
extend: '@vuepress/theme-default',
};

View File

@ -1,16 +0,0 @@
---
content: Home
home: true
#heroImage: ../imgs/common/logo.svg
heroText: 一款所见即所得的H5编辑器
features:
- title: 简洁方便
details: 任何人只需傻瓜式拖拽或进行简单编辑即可生成精美的H5页面
- title: 插拔式体验
details: 产品以GPL协议部分开源, 授权后可植入任何系统,并支持二次开发
- title: 持续迭代,无限可能
details: 目前正在持续迭代中,后续可根据需求开发功能更强大的可视化系统
actionText: 快速上手 →
actionLink: /zh/guide/
footer: GPL Licensed | Copyright © 2020-present H5-Dooring
---

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
{
"name": "@h5-dooring/doc",
"version": "0.0.1",
"description": "文档",
"license": "ISC",
"main": "index.js",
"scripts": {
"dev": "vuepress dev",
"build": "vuepress build"
},
"devDependencies": {
"vuepress": "^1.8.2",
"vuepress-plugin-autobar": "github:boboidream/vuepress-bar"
},
"files": [
"lib"
]
}

View File

@ -1,28 +0,0 @@
<img src="http://cdn.dooring.cn/dr/logo.ff7fc6bb.png" alt="dooring" width=200>
H5-Dooring 仍在持续迭代, 如果有任何问题, 可以点击 [立即反馈](http://h5.dooring.cn/h5?tid=86C45FAE)。
## 问题汇总
#### 1. 本地部署后, win系统下如何启动server?
win下启动server, 需要将server/package.json的启动脚本修改为如下:
``` js
"scripts": {
"start": "set NODE_ENV=development&& nodemon -w src --exec \"babel-node src\"",
// ... 其他不变
},
```
#### 2. 添加自定义组件, 需要改哪些文件?
dooring支持根据企业自身业务添加自定义组件, 目前2.0版本添加自定义组件只需要在 `editor/src/components/BasicShop` 对应的组件分类下添加组件即可, 最后在 `editor/src/components/BasicShop/template.ts`下对应位置添加组件描述即可生效. 同时, 为了保证发布后的H5页面保持最新, 需要在部署发布代码前在H5工程中对应的位置也同步一份组件代码. 后续会对这一流程做一定的优化.
#### 3. 本地如何调试对应的项目?
授权后, 本地安装项目依赖, 在调试编辑器(editor)项目或后台管理项目(admin)前, 需要先启动服务器工程(server), 在
`server/index.js` 代码中添加 editor / admin 项目的启动 `ip`, 这样就能跨域请求了, 同时需要修改 editor / admin 项目中 `utils/tools.ts` 下的api地址, 开发环境改为本地 `server` 项目的 `ip`.
如果在部署中遇到其他问题, 欢迎加我微信交流: `Mr_xuxiaoxi`

View File

@ -1,65 +0,0 @@
# DSL设计
DSL层主要约定了Dooring组件的数据协议包括组件的可编辑属性、编辑类型、初始值等之所以定义一致的协议层主要是方便后期的组件扩展配置后移有助于不同后端语言开发和数据存储接下来我们看看header组件的schema。
1.editData 可编辑的属性类型DSL
2.config 可编辑组件的默认属性
```js
const Header: IHeaderSchema = {
editData: [
{
key: 'bgColor',
name: '背景色',
type: 'Color',
},
{
key: 'height',
name: '高度',
type: 'Number',
},
{
key: 'logo',
name: 'logo',
type: 'Upload',
isCrop: true,
cropRate: 1000 / 618,
},
{
key: 'logoText',
name: 'logo文字',
type: 'Text',
},
{
key: 'color',
name: '文字颜色',
type: 'Color',
},
{
key: 'fontSize',
name: '文字大小',
type: 'Number',
}
],
config: {
bgColor: 'rgba(245,245,245,1)',
logo: [
{
uid: '001',
name: 'image.png',
status: 'done',
url: `${serverUrl}/uploads/3_1740be8a482.png`,
},
],
logoText: '页头Header',
fontSize: 20,
color: 'rgba(47,84,235,1)',
height: 50
},
};
```
由以上代码可知我们可以在editData属性中给组件添加可编辑的属性比如背景图然后再component中接受属性从而设置样式。
在config属性中我们可以设置组件默认属性值和editData中每一项的key一一对应。

View File

@ -1,17 +0,0 @@
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 19:42:53
* @FilePath: /github-h5-Dooring/doc/zh/guide/componentDev/dynamicLoading.md
-->
# 组件动态加载
目前H5-Dooring的组件都是通过动态加载的方式引入好处是我们在页面中只会加载我们需要的组件不需要的组件不会被加载这样可以提高页面加载的速度这样做也会出现一些问题比如一个长页面配置了很多组件那么一个页面加载过程可以会触发多次请求目前还没有遇到性能问题但后续会逐渐优化这个问题。
## umi3提供的dynamic
目前组件的动态加载我们采用的umi的dynamic方案基于它我们上层封装了一个组件动态加载器原理如下
<img src="../../../img/componentDev/dynamic.png" alt="foo">
具体代码可以参考Dooring的Github地址[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)

View File

@ -1,16 +0,0 @@
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-17 19:42:53
-->
# 组件审核
组件审批主要由网站管理人员来操作,当用户组件提交成功之后,客户端会通过消息信令通知管理员,管理员收到消息后会审核组件。那么整个过程也很简单,我们可以使用 websocket 来实现消息双向通信,完整流程如下:
<img src="http://cdn.dooring.cn/dr/WX20210720-133112%402x.png" />
通过以上的实现方式客户端和服务端就可以随时进行通信了。接下来我们看看审批的效果:
<img src="http://cdn.dooring.cn/dr/WX20210720-133329%402x.png" />

View File

@ -1,25 +0,0 @@
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-17 19:42:53
-->
## 组件商店基本介绍
对于可视化搭建平台而言,其中一个核心的环节就是组件资产。用户在设计搭建页面时会消费各种各样的组件,但是对于不同的用户而言,组件的需求往往是不一样的,大部分的 lowcode 或者 nocode 平台都不能很好的解决用户这些定制化的组件需求,所以为了解决这一问题,组件商店就孕育而生。
<img src="http://cdn.dooring.cn/dr/WX20210720-131747%402x.png" />
## 组件商店工作流设计
我们要想实现完整的组件商店工作流,需要满足以下几点:
- 组件线上编辑(上传)模块
- 组件审核模块
- 组件更新/发布模块
- 组件管理(上架/下架/删除/下载)
有了以上4块的支持基本的组件商店就可以 work 了。具体流程如下:
<img src="http://cdn.dooring.cn/dr/WX20210720-132319%402x.png" />

View File

@ -1,19 +0,0 @@
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-17 19:42:53
-->
# 上传组件
当“ 生产者 ”编写好组件代码之后,需要对组件自身进行定义。因为可视化平台组件物料很依赖平台的组件开发协议,我们需要按照平台的规范去上传规范的自定义组件,这样平台才能更好的理解应用组件,保持用户认知的一致性。
组件描述信息笔者这里设计了如下字段:
- 组件名称 (中文)
- 组件名 (英文,方便存库)
- 组件分类 (基础,可视化,营销,媒体等)
- 组件默认大小 (宽高)
- 组件图标 (方便用户认知,查找)
<img src="http://cdn.dooring.cn/dr/WechatIMG45705.png" />

View File

@ -1,791 +0,0 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**后端部分主要使用 `Nodejs` 开发, 为了满足更多定制化需求和服务的可移植性, 特意编写了API接口文档,
方便大家使用不同的后端语言实现服务接入.
- 注意: 接口统一前缀为`/api/v0`
## 用户相关
### 用户登录
用户登录接口
- `POST` /vip/check
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| n | true | string | 用户名 |
| co | true | string | 密码 |
返回示例
``` json
{
"result": {
"n": "test",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"rp": "AAAAA",
"maxage": 300000
}
}
```
### 注销登录
注销接口
- `POST` /vip/checkout
返回示例
``` json
{
"result": null,
"msg": "退出成功"
}
```
### 权限控制
不同用户级别所访问的页面权限不同, 这块可结合服务端已有代码设计属于自己的权限字段, 地址为`server/src/router`
### 用户列表
获取用户列表接口
- `GET` /vip/all
获取用户列表需要账号满足以下条件:
- 已登录
- 为超级管理员
返回示例
``` json
[
{
"id": "",
"n": "test",
"co": "123456",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"wx": "Mr_xuxiaoxi",
"rp": "AAAAA"
}
]
```
### 添加用户
添加用户接口
- `POST` /vip/add
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| nickname | true | string | 用户名 |
| wx | true | string | 微信号 |
| co | true | string | 密码 |
注: co是由笔者写的`加密算法`实现, 不需要手动填写, 详情见`dooirng`后台管理/用户管理页面.
返回示例
``` json
{
"id": "3422EF",
"n": "test",
"wx": "Mr_xuxiaoxi",
"co": "123456",
"od": [],
"h5": [],
"tpl": [],
"rp": "AAAAA",
"h5Num": 10,
"tplNum": 3
}
```
### 生成登录码
生成登录码接口
- `GET` /vip/gcode
先决条件:
- 用户已登陆
- 为超级管理员
注: 生成登录码是由笔者写的`加密算法`实现, 不需要手动实现, 如果有自定义需求, 可以自行二次开发实现.
返回示例
``` json
{
"co": "1x2fgggteee3456_zdd4",
}
```
`说明:` 为了保护用户信息安全, 返回的登录码是加密后的密文, 会调用笔者写的`xib.xip`方法进行加密, 如果想看到原始密码, 需要调用`xib.uxip`进行解密.
### 获取用户真实密码
获取用户真实密码接口
- `GET` /vip/gcode/get
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| co | true | string | 加密后的密码 |
返回示例
``` json
{
"co": "12345678",
}
```
### 修改用户信息
修改用户接口
- `POST` /vip/edit
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| id | false | string | 用户ID |
| nickname | false | string | 用户名 |
| co | false | string | 登录码 |
| wx | false | string | 微信号 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "修改成功",
}
```
### 删除用户
删除用户接口
- `DELETE` /vip/del
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| id | true | string | 用户ID |
| wx | true | string | 微信号 |
| n | true | string | 用户名 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "删除成功",
}
```
## H5页面管理
### 获取H5数据
- `GET` /visible/h5/get
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid | true | string | H5唯一id |
返回示例
``` json
{
"pageConfig": {
},
"tpl": [
{
"id": "879742",
"item": {
"type": "Carousel",
"config": {
"direction": "left",
"swipeable": false,
"autoPlay": false,
"imgList": [
{
"id": "1",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/1_1740bd7c3dc.png"
}
]
},
{
"id": "2",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/2_1740bd8d525.png"
}
]
}
],
"tplImg": "http://io.nainor.com/uploads/carousal_17442e1420f.png"
},
"h": 82,
"editableEl": [
{
"key": "direction",
"name": "方向",
"type": "Radio",
"range": [
{
"key": "down",
"text": "从上到下"
},
{
"key": "left",
"text": "从左到右"
}
]
},
{
"key": "swipeable",
"name": "是否可拖拽",
"type": "Switch"
},
{
"key": "autoPlay",
"name": "是否自动播放",
"type": "Switch"
},
{
"key": "imgList",
"name": "图片列表",
"type": "DataList"
}
],
"category": "base"
},
"point": {
"i": "x-0",
"x": 0,
"y": 13,
"w": 24,
"h": 82,
"isBounded": true
},
"status": "inToCanvas"
},
{
"id": "481194",
"item": {
"type": "Form",
"config": {
"title": "表单定制组件",
"fontSize": 18,
"titColor": "rgba(60,60,60,1)",
"titWeight": "400",
"bgColor": "rgba(255,255,255,1)",
"btnColor": "rgba(20,54,226,100)",
"btnTextColor": "rgba(255,255,255,1)",
"api": "",
"formControls": [
{
"id": "1",
"type": "Text",
"label": "姓名",
"placeholder": "请输入姓名"
},
{
"id": "2",
"type": "Number",
"label": "年龄",
"placeholder": " 请输入年龄"
},
{
"id": "4",
"type": "MySelect",
"label": "爱好",
"options": [
{
"label": "选项一",
"value": "1"
},
{
"label": "选项二",
"value": "2"
},
{
"label": "选项三",
"value": "3"
}
]
}
]
},
"h": 172,
"category": "base"
},
"point": {
"i": "x-1",
"x": 0,
"y": 98,
"w": 24,
"h": 172,
"isBounded": true
},
"status": "inToCanvas"
}
]
}
```
### 保存H5数据
- `POST` /visible/h5/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| pageConfig | false | object | H5页面配置数据 |
| tpl | true | object | H5页面组件配置数据 |
| tid | true | string | H5页面唯一id |
参数示例
``` json
{
"pageConfig": {
"bgColor":"rgba(151,25,25,1)",
"title":"医院宣传页"
},
"tpl": [],
"tid": "EF123D3"
}
```
返回示例
``` json
{
"state": 200,
"result": {
"tid": "EF123D3"
},
"msg": "保存成功"
}
```
### 删除H5数据
- `DELETE` /visible/h5/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid | true | string | H5页面唯一id |
返回示例
``` json
{
"state": 200,
"result": [
{
"tid": "EF123D3",
"name": "test页面"
},
{
"tid": "EF123D6",
"name": "test2页面"
}
],
"msg": "删除成功"
}
```
## H5表单数据管理
### 保存表单数据
- `POST` /vip/h5/form/post
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid(query) | true | string | H5页面唯一id |
| formData(body) | true | array | H5页面表单数据 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "表单提交成功"
}
```
### 批量导入表单数据
- `POST` /vip/h5/form/import
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid(query) | true | string | H5页面唯一id |
| formData(body) | true | array | H5页面表单数据集合 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "批量导入成功"
}
```
### 删除表单数据
- `DELETE` /vip/h5/form/del
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid | true | string | H5页面唯一id |
| ID | true | string | 表单专属id |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 模版管理
### 获取模版库
- `GET` /visible/tpls/free
返回示例
``` json
{
"state": 200,
"result": [
{
"img": "http://xxx/uploads/tpl_175adabd8dd.jpg",
"name": "合作模版",
"tid": "B73349B6"
}
]
}
```
### 保存模版
- `POST` /visible/tpl/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| name | true | string | H5模版名称 |
| cate | true | string | H5模版分类 |
| img | false | string | H5模版封面图 |
| tpl | true | array | H5模版数据 |
| pageConfig | false | object | H5模版全局配置 |
返回示例
``` json
{
"state": 200,
"result": {
"tid": "B73349B6"
},
"msg": "保存成功"
}
```
### 删除模版
- `DELETE` /visible/tpl/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| tid | true | string | H5模版id |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 文件上传
- `POST` /files/upload/free
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| File | true | File | 文件对象 |
返回示例
``` json
{
"state": 200,
"result": {
"filename": "H5-Dooring",
"url": "http://h5.dooring.cn/uploads/1_17ac208480d.png",
"size": 261972
},
"msg": "文件上传成功"
}
```
## 数据统计
### 数据大盘接口
- `GET` /vip/dashboard
先决条件:
- 用户已登陆
返回示例
``` json
{
"state": 200,
"result": {
"userNums": 1300,
"pageNums": 808,
"tplNums": 86,
"views": 60235,
"userList": []
}
}
```
## 组件商店
### 组件上传
- `POST` /visible/cp/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| react | true | string | react字符串 |
| css | true | string | css字符串 |
| schema | true | string | schema字符串 |
| form | true | object | 表单对象 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "提交成功, 请等待审核..."
}
```
### 获取组件上传列表
- `GET` /visible/cp/list
先决条件:
- 用户已登陆
返回示例
``` json
{
"state": 200,
"result": [
{
"cpName":"test",
"cpField":"test",
"cate":"base",
"width":3,
"height":6,
"icon":"http://196.345.0.345:3000/uploads/1_17a8a4eaedf.png",
"ispass":3
},
{
"cpName":"test2",
"cpField":"test2",
"cate":"media",
"width":4,
"height":20,
"icon":"http://196.345.0.345:3000/uploads/1_17a8a4eaedf.png",
"ispass":1
}
],
}
```
### 获取组件详情
- `GET` /visible/cp/get
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| cate | true | string | 组件分类 |
| cpField | true | string | 组件英文名字段 |
返回示例
``` json
{
"state": 200,
"result": {
"react": "react代码",
"css": "css/less代码",
"schema": "schema的js/ts代码"
}
}
```
### 更新组件审批状态
- `POST` /visible/cp/update
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| cate | true | string | 组件分类 |
| cpField | true | string | 组件英文名字段 |
| ispass | true | number | 组件审核状态, 0 待处理 1 审核中 2 审核通过 3 审核不通过 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "审批成功"
}
```
### 删除组件
- `DELETE` /visible/cp/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| cate | true | string | 组件分类 |
| cpField | true | string | 组件英文名字段 |
返回示例
``` json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
### 下载组件源码
- `POST` /visible/cp/download
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| cate | true | string | 组件分类 |
| cpField | true | string | 组件英文名字段 |
返回示例
``` json
{
"state": 200,
"result": "组件压缩包地址"
}
```
## 数据源管理
### 获取用户数据源
- `GET` /vip/sourcedata/get
先决条件:
- 用户已登陆
返回示例
``` json
{
"state": 200,
"result": [],
}
```
### 保存用户数据源
- `POST` /vip/sourcedata/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------------- |:-------------:|:-----:| -------------:|
| sourcedata | true | array | 用户数据源集合 |
返回示例
``` json
{
"state": 200,
"result": []
}
```

View File

@ -1,41 +0,0 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
### 获取Form组件的值数据
Form表单组件在`editor`目录下`src/components/BasicShop/BasicComponents`位置.
Form组件是`Dooring`的核心组件之一, 内部的值通过Form组件内部收集, 当然我们也可以暴露出来让其他交互或者组件消费(需要一定的二次开发), 关键代码如下:
``` js
req.post(`/vip/h5/form/post${location.search}`, {...fields, ...formData}).then(res => {
if(type === 'link') {
// 解析参数
let isPre = content.indexOf('?') < 0;
let query = {dr: Date.now(), from: urlParmas.tid};
try {
query = params ? {...JSON.parse(params), ...query} : query;
}catch(err) {
console.log(err)
}
// 跳转
if(content.indexOf('http') > -1) {
window.location.href = content + urlencode(query, isPre);
return
}
history.push(`/m?tid=${content}&${urlencode(query)}`);
}else if(type === 'modal') {
setVisible(true);
}else if(type === 'code') {
eval(content);
}
})
```
数据收集提交的核心代码在Form组件的第56-149行, 也就是`submit`方法. 表单组件收集到的数据统一存放在代码中的`formData`字段, 所以要想在其他地方获取用户表单填写的值, 我们只需要手动将`formData`传递出去, 或者挂载到全局(如window对象, localStorage, indexedDB等).

View File

@ -1,59 +0,0 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
目前**H5-Dooring**全面支持https部署, 具体方式方案如下.
### 前端工程
我们需要在前端工程中的`src/pages/document.ejs`中的`head`中添加如下代码:
``` html
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
```
目的是强制将页面中HTTP请求转换为HTTPS.
### 服务器工程
#### 1. 申请SSL证书
#### 2. 生成 server.csr+server.key
#### 3. 通过证书链生成.pem文件
#### 在`server`中的`src/index.js`按如下方式修改
``` js
// 忽略部分无影响代码
import https from 'https';
// 你的ssl存放路径, 建议直接放在server目录下
const filePath = path.join(__dirname, '../ssl')
// 启动逻辑
async function start() {
// https配置
const httpsOptions = {
key: fs.readFileSync(path.join(filePath, '3536084__doctopia.com.cn.key')), //ssl文件路径
cert: fs.readFileSync(path.join(filePath, '3536084__doctopia.com.cn.pem')) //ssl文件路径
};
// https服务
const server = https.createServer(httpsOptions, app.callback());
const io = require('socket.io')(server);
// 忽略其他无影响代码
// https默认443, 这里我们可以走公共配置
server.listen(443, () => {
console.log(`服务器地址:${config.staticPath}`)
});
}
start()
```

View File

@ -1,118 +0,0 @@
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**全面支持第三方对象存储服务, 我们以七牛云对象存储为例.
### 前端上传文件到oss
首先我们需要在第三方对象储存服务中配置对应的服务和域名. 其次安装对应的sdk, 如七牛云sdk:
``` js
import * as qiniu from 'qiniu-js';
```
其次我们修改`h5_plus`工程的`Upload`组件, 详细地址为`src/core/FormComponents/Upload`.
修改内容如下:
``` js
const fileName = file.name
const suffix = '自定义文件后缀'
const putExtra = {
fname: fileName,
params: {}
}
const uid = +new Date() + uuid(16, 8) + suffix
// 使用七牛云上传api, 前提是提前在前端拿到对应的ticket, 可以通过请求的方式获取
const observe = qiniu.upload(file, uid, this.state.qnToken.ticket, putExtra, {})
observe.subscribe(() => {}, null, (res) => {
// 拼接路径
const url = `${this.state.qnToken.domain}/${res.key}`;
// 存库
const fileList = [{ uid, name: fileName, status: 'done', url }];
this.setState({
curImgUrl: url,
fileList
})
this.props.onChange && this.props.onChange(fileList)
})
```
其他oss服务类似, 如果不清楚如何配置, 可以在[H5-Dooring官网](http://h5.dooring.cn/)中找到我们.
### 如何接入任何第三方上传服务
首先我们的上传组件`Upload`使用内部的服务接口来实现上传功能, 所以需要给组件的`action`赋值, 如下:
``` jsx
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
onRemove={this.handleRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={sdk_upload_api || action}
withCredentials={withCredentials}
headers={{
'x-requested-with': localStorage.getItem('user') || '',
'authorization': localStorage.getItem('token') || '',
...headers
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
如果需要集成第三方oss, 如七牛云, 阿里oss等, 我们需要将`Upload`组件的`action`属性设置为空字符串, 其次删除`onChange`属性, 上传操作统一在`beforeUpload`中进行. 案例如下:
``` jsx
<Upload
fileList={fileList}
action=""
onPreview={this.handlePreview}
onRemove={this.onRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
headers={{...headers}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
自定义上传的核心逻辑放在了`beforeUpload`上. 我们具体看看`beforeUpload`这个方法如何实现.
``` js
handleBeforeUpload = (file:RcFile) => {
// 1. 限制图片类型
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg' || file.type === 'image/gif';
if (!isJpgOrPng) {
message.error('只能上传格式为jpeg/png/gif的图片');
}
// 限制上传文件大小
const isLt3M = file.size / 1024 / 1024 < 3;
if (!isLt3M) {
message.error('图片必须小于3MB!');
}
if(isJpgOrPng && isLt3M) {
// 3. 正常上传逻辑
const fileName = file.name
// 3.1 调用oss接口, 将图片上传oss
// 3.2 将接口返回的url信息, 组装成fileList数据结构, 并更新state
const fileList = [{ uid, name: fileName, status: 'done', url }];
this.setState({
curImgUrl: url,
fileList,
})
// 3.3 将数据传给上层保存
this.props.onChange && this.props.onChange(fileList)
}
return isJpgOrPng && isLt3M;
}
```

View File

@ -1,35 +0,0 @@
<!--
* @Date: 2021-01-17 14:27:28
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:50:17
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/revocation.md
-->
# 撤销/重做
撤销重做我们主要使用了redux-undo这个库配合Dva使用具体使用方法参考如下操作
```js
import { createLogger } from 'redux-logger';
import { message } from 'antd';
import undoable, { StateWithHistory } from 'redux-undo';
import { Reducer, AnyAction } from 'redux';
export const dva = {
config: {
onAction: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
onReducer: (reducer: Reducer<any, AnyAction>) => {
let undoReducer = undoable(reducer);
return function(state: StateWithHistory<any>, action: AnyAction) {
let newState = undoReducer(state, action);
let router = newState.present.router ? newState.present.router : newState.present.routing;
return { ...newState, router: router };
};
},
},
};
```
以上我们就实现了全局配置redux-undo在撤销重做按钮中我们就可以触发对应的方法来实现撤销重做的功能其次我们还使用了redux-logger来实现redux的日志输出。

View File

@ -1,11 +0,0 @@
<!--
* @Date: 2021-01-17 14:26:00
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:32:58
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/saveJson.md
-->
# 保存json
我们配置好H5页面之后如果希望其他人观看我们可以保存页面并发送链接。但是如果有多人协作的需求比如一个H5页面可能由多个人完成这个时候该怎么实现呢基于已有的方案我们可以采用socket实现多人协同编辑但是成本比较大所有这里我们提供了保存json的功能。
我们可以将配置好的页面导出为json发送给另一个人这样另一个人通过导入该json文件可以实时看到当前的页面这里还是依靠我们的页面渲染引擎viewEngine。实现思路也很简单可以在github[[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)]上参考体验。

View File

@ -1,3 +0,0 @@
<img src="../../img/common/framework.png" alt="foo">
注:灰色部分不包括在实现范围内,可以参考商业授权版源码二次开发.

View File

@ -1,177 +0,0 @@
<img src="http://cdn.dooring.cn/dr/ccc.png" alt="dooring" width="100%">
H5-Dooring 是一款功能强大,高可扩展的 H5 可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳实践。
## 背景介绍
**LowCode** 平台这两年在国内热度很高, 也有很多公司在着手设计能支撑更多业务场景的低代码平台, 但是国内发展仍然处于起步阶段, 还有非常多的问题需要处理。就我本人服务过的多家上市公司, 中后台业务非常复杂, 但是通用性非常高, 业务逻辑和展现形态有很多复用性, 为了解决复用性低和研发效能低的问题, **规则引擎** 孕育而生, 当然这只是 **Lowcode/nocode** 的一个过渡方案, 但已经能为企业提高20%以上的研发效率。
当然很多企业早已看到这样的机会, 也研发出了非常优秀的 **LowCode** 产品, 但是由于国内企业业务的复杂性, 很多 **LowCode** 产品都存在一定的局限, 所以各大中小企业也开始研究可视化搭建相关的产品, 可谓“百花齐放”。
[**H5-Dooring**](http://h5.dooring.cn/h5_plus) 正是为了解决企业内部可视化搭建需求的解决方案, 它更多的是提供一套可视化搭建解决方案, 以源码的方式交付企业, 使得企业能从此方案中受益, 二次开发适合自身业务需求的搭建平台。当然随着 [**H5-Dooring**](http://h5.dooring.cn/h5_plus) 的不断更新和完善, 部分企业甚至不需要二次开发就能直接使用 [**H5-Dooring**](http://h5.dooring.cn/h5_plus) , 我们也提供部署服务, 来快速帮企业做项目部署。目前已完成了常用的功能:
- 微信/QQ分享(支持配置微信分享文案, 自定义分享图标)
- https服务支持解决方案
- oss解决方案(已跑通七牛云上传服务)
- 国际化方案
同时由于 **H5-Dooring** 的核心在编辑器前端, 也是其最大价值所在, 我们服务层主要提供基础的数据支持服务(用户管理, H5页面管理, 模版管理, 基本页面统计等), 企业也可以轻松接入自己的服务后台,如 **java, php, go, python** 等。所以不用担心整合内部系统之后的数据问题, 同时我建议有二次开发需求的企业需要具备一定的技术能力, 比如**React, Nodejs** 等的开发经验。
## H5编辑器介绍
接下来和大家详细介绍一下 [**H5-Dooring**](http://h5.dooring.cn/h5_plus) 编辑器的功能模块。
<img src="http://cdn.dooring.cn/dr/chrome-capture%20%284%29.gif" alt="dooring" width="100%">
### 1.入口页面
入口页面主要是我们在编辑器前台管理用户页面的一个入口, 我们可以创建H5页面, 在线编程等, 同时也能基于已有的精选模版来快速的创建页面:
<img src="http://cdn.dooring.cn/dr/1.png" alt="dooring" width="100%">
### 2.编辑器页面
编辑器页面的内容比较多, 也是最为重要的部分。我们可以从几个部分来介绍:
- 组件区
- 画布区
- 顶部功能区
- 属性编辑面板
- 数据源
- 快捷键
- 国际化
#### 2.1 组件区
<img src="http://cdn.dooring.cn/dr/3.png" alt="dooring" width="100%">
组件主要包括 基础组件, 图表组件, 媒体组件, 商品组件, 功能组件, 当然企业也可以基于自身业务划分不同的分类, 并进行组件的二次开发。我们只需要从左侧拖拽组件到画布, 即可使用该组件。
同时 [**H5-Dooring**](http://h5.dooring.cn/h5_plus) 还提供了组件定制的能力, 让用户选择自己常用的组件, 这样用户可以更高效的搭建页面:
<img src="http://cdn.dooring.cn/dr/4.png" alt="dooring" width="100%">
由上图所示, 用户只需要选择或者取消选择, 可以决定组件是否在编辑器页面显示, 如果后期用户想恢复设置, 也可以在该页面恢复。
#### 2.2 画布区
画布区可以动态调整画布大小来试试预览不同尺寸的样式, 也可以移动画布, 缩放画布来快捷的操作页面:
<img src="http://cdn.dooring.cn/dr/13.png" alt="dooring" width="100%">
#### 2.3 顶部功能区
顶部功能区包括的功能有:
- 模版库
- 保存
- 下载源码
- 导出json
- 导入json
- 预览
- 真机预览
- 撤销/重做
- 删除
- 截图
- 页面设置
大家可以亲自体验一下。
#### 2.4 属性编辑面板
<img src="http://cdn.dooring.cn/dr/12.png" alt="dooring" width="100%">
属性编辑面板可以对组件的外观, 逻辑, 交互进行配置, 比较简单但大部分满足业务搭建需求, 企业技术人员也可以增量式地添加更多属性配置。
#### 2.5 数据源
数据源主要为瓶平台用户提供一种高效的数据接入机制, 不同页面或者统同一页面的不同组件可以共享数据:
<img src="http://cdn.dooring.cn/dr/11.png" alt="dooring" width="100%">
更详细的数据源介绍可以参考我的文章 [Dooring可视化搭建平台数据源设计剖析](https://mp.weixin.qq.com/s?__biz=MzU2Mzk1NzkwOA==&mid=2247487877&idx=2&sn=770ff16d69d3e7ac2bbcd78e97ab8f32&chksm=fc53087ecb2481685451a50e892fa889781788ca16a4ce689ec7f7fff1ae99c91ac8b82a160d&token=1373546109&lang=zh_CN#rd)
#### 2.6 快捷键
快捷键主要分为画布内组件的快捷键(复制, 删除)和全局的快捷键, 如下图所示:
<img src="http://cdn.dooring.cn/dr/10.png" alt="dooring" width="100%">
#### 2.7 国际化
国际化目前已支持, 我们只需要在入口页面切换即可:
<img src="http://cdn.dooring.cn/dr/9.png" alt="dooring" width="100%">
### 3.预览页面
用户可以点击顶部功能区的预览按钮一键预览:
<img src="http://cdn.dooring.cn/dr/5.png" alt="dooring" width="100%">
也可以在预览页面, 打开控制台调整为谷歌的仿真预览模式:
<img src="http://cdn.dooring.cn/dr/6.png" alt="dooring" width="100%">
### 4.真机预览
<img src="http://cdn.dooring.cn/dr/7.png" alt="dooring" width="100%">
### 5.登录页面
<img src="http://cdn.dooring.cn/dr/8.png" alt="dooring" width="100%">
## Dooring后台管理介绍
**Dooring后台管理** 主要是为 **H5-Dooring** 提供数据支撑, 比如增删查改等操作, 同时随着用户需求的不断增加, **Dooring后台管理** 目前已实现了非常多的功能, 比如说表单数据收集, 表单数据分析, 导出数据, 基本的页面数据监控等, 接下来我就来带大家介绍一下。
<img src="http://cdn.dooring.cn/dr/20.png" alt="dooring" width="100%">
我们可以从上图的编辑器界面右上角头像下拉框中进入后台管理系统。
### 1.后台主页
后台主页主要是对编辑器页面提供基本的访问量统计, 同时对用户数, 模版数, 页面数进行统计, 企业可以根据自身需求二次开发更多数据统计方案。
<img src="http://cdn.dooring.cn/dr/21.png" alt="dooring" width="100%">
### 2.用户管理
用户管理主要是对网站用户进行管理(注册, 修改, 删除, 查看等), 当然只有超级管理员能看到, 目前我们做了简单的权限管理: 超级管理员, 普通用户. 普通用户可以管理自己的页面, 查看页面数据分析等,超级管理员可以使用所有功能, 比如管理用户, 生成注册链接, 模版管理, 页面管理等, 同时可以审核页面, 一键删除其他用户产生的不符合规定的页面。
<img src="http://cdn.dooring.cn/dr/22.png" alt="dooring" width="100%">
### 3.页面管理
页面管理主要是对用户搭建的H5页面进行管理, 我们可以查看页面的链接, 页面访问量, 编辑页面标题, 删除页面等,如果这个页面包含表单, 我们还能一键查看表单数据的收集情况,并一键进行数据分析。
<img src="http://cdn.dooring.cn/dr/24.png" alt="dooring" width="100%">
#### 3.1表单页面数据分析
表单数据分析主要针对有表单的页面, 我们可以一键统计表单数据, 并生成分析报告, 如下图:
<img src="http://cdn.dooring.cn/dr/23.png" alt="dooring" width="100%">
同时我们可以将数据导出为 `excel`, 或者将数据导入, 一键生成数据分析报告, 当然更多分析维度开发者也可以二次开发。
### 4.模版管理
超级管理员可以对用户产生的模版进行预览, 审核, 删除, 保证线上用户使用的模版都是精美可用的:
<img src="http://cdn.dooring.cn/dr/26.png" alt="dooring" width="100%">
### 5.精选模版管理
精选模版主要是用来管理 **H5-Dooring** 入口页面中的模版, 我们可以把精美的行业模版上传到精选模版中, 让用户更高效的搭建页面:
<img src="http://cdn.dooring.cn/dr/25.png" alt="dooring" width="100%">
## Dooring后台服务介绍
后台服务主要采用的 koa + nodejs, 同时还使用了微信 `sdk`, 轻松帮我们实现微信相关的功能, 接口遵循 `restful` 规范, 并且提供了接口文档, 企业可以轻松基于此使用自己的后端语言来接入, 比如 java, python, go, php等。

View File

@ -1,16 +0,0 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

View File

@ -1,8 +0,0 @@
/lambda/
/scripts
/config
.history
public
dist
.umi
mock

View File

@ -1,26 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
extends: [
require.resolve('@umijs/fabric/dist/eslint'),
'plugin:prettier/recommended',
],
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/no-unused-vars':'warn',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-use-before-define':'off',
'@typescript-eslint/no-shadow':'off'
},
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
page: true,
REACT_APP_ENV: true,
},
};

View File

@ -1,7 +0,0 @@
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production

View File

@ -1,7 +0,0 @@
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.prettier,
};

View File

@ -1,82 +0,0 @@
import path from 'path';
import { defineConfig } from 'umi';
const { ModuleFederationPlugin } = require("webpack").container;
export default defineConfig({
nodeModulesTransform: {
type: 'none',
},
dynamicImport: {
loading: '@/components/LoadingCp',
},
dva: {
immer: true,
},
// devtool: 'source-map',
antd: {},
title: 'h5-dooring',
// exportStatic: {},
base: '/',
publicPath: '/',
outputPath: 'dist',
routes: [
{
exact: false,
path: '/',
component: '@/layouts/index',
routes: [
{
path: '/',
component: '../pages/home',
},
{
path: '/editor',
component: '../pages/editor',
},
{
path: '/ide',
component: '../pages/ide',
},
{
path: '/help',
component: '../pages/help',
},
{
path: '/login',
component: '../pages/login',
},
{
path: '/mobileTip',
component: '../pages/mobileTip',
},
],
},
],
theme: {
'primary-color': '#2F54EB',
// "btn-primary-bg": "#2F54EB"
},
extraBabelPlugins: [['import', { libraryName: 'zarm', style: true }]],
// sass: {},
alias: {
components: path.resolve(__dirname, 'src/components/'),
utils: path.resolve(__dirname, 'src/utils/'),
assets: path.resolve(__dirname, 'src/assets/'),
},
fastRefresh: {},
webpack5: {},
plugins: [
'./src/plugins/umi-msfu-plugin.ts',
],
chainWebpack(memo) {
memo
.plugin('mf')
.use(ModuleFederationPlugin, [{
name: "dooringEditor",
remotes: {
dooringUI: "dooringUI@//localhost:8008/remoteEntry.js"
},
shared: { react: { singleton: true, eager: true, requiredVersion: '17.x' }, "react-dom": { singleton: true,eager: true, requiredVersion: '17.x' }, }
}])
},
});

View File

@ -1,11 +0,0 @@
# `editor`
> TODO: description
## Usage
```
const editor = require('editor');
// TODO: DEMONSTRATE API
```

View File

@ -1,126 +0,0 @@
{
"name": "@h5-dooring/editor",
"version": "0.0.1",
"description": "编辑器",
"private": false,
"author": {
"name": "徐小夕",
"email": "xujiang156@qq.com",
"url": "http://h5.dooring.cn"
},
"keywords": [
"h5 editor",
"h5",
"react",
"antd",
"react-dnd",
"web visible"
],
"contributors": [
"徐小夕 <xujiang156@qq.com> (https://github.com/MrXujiang))",
"yehuozhili <yehuozhili@outlook.com> (https://github.com/yehuozhili))"
],
"scripts": {
"start": "umi dev",
"build": "umi build",
"server": "node server.js",
"dev": "http-server dist",
"test-demo": "http-server dist",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
"test:coverage": "umi-test --coverage",
"nocompress": "cross-env RM_TMPDIR=none COMPRESS=none umi build"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,less,md,json}": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
},
"homepage": "http://h5.dooring.cn",
"repository": {
"type": "git",
"url": "git+https://github.com/MrXujiang/h5-Dooring.git"
},
"bugs": {
"url": "https://github.com/MrXujiang/h5-Dooring/issues"
},
"dependencies": {
"@ant-design/icons": "^4.2.1",
"@umijs/plugin-esbuild": "^1.0.1",
"@umijs/plugin-sass": "^1.1.1",
"antd": "^4.7.0",
"antd-img-crop": "^3.10.0",
"axios": "^0.19.2",
"braft-editor": "^2.3.9",
"chatbot-antd": "^0.6.0",
"react-cropper-pro": "1.0.4",
"codemirror": "^5.57.0",
"dom-to-image": "^2.6.0",
"file-saver": "^2.0.2",
"http-server": "^0.12.3",
"keymaster": "^1.6.2",
"qrcode.react": "^1.0.0",
"react": "17.x",
"react-dom": "17.x",
"react-codemirror2": "^7.2.1",
"react-color": "^2.18.1",
"react-contexify": "^4.1.1",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-draggable": "^4.4.3",
"react-draggable-ball": "^0.1.0",
"react-grid-layout": "^1.0.0",
"react-hotkeys-hook": "^2.3.1",
"react-text-loop": "^2.3.0",
"redux-undo": "^1.0.1",
"socket.io-client": "^2.3.0",
"umi": "^3.5.13",
"xlsx": "^0.16.7",
"yh-react-popover": "^0.3.0",
"yorkie": "^2.0.0",
"zarm": "^2.5.1",
"@alex_xu/react-slider-vertify": "^1.1.7"
},
"license": "GPL-3.0",
"devDependencies": {
"@typescript-eslint/parser": "^4.28.2",
"@umijs/fabric": "^2.6.2",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.5.13",
"@types/classnames": "^2.2.10",
"@types/codemirror": "^0.0.98",
"@types/events": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/node": "^14.6.2",
"@types/qrcode.react": "^1.0.1",
"@types/react-color": "^3.0.4",
"@types/react-grid-layout": "^1.1.0",
"@types/redux-logger": "^3.0.8",
"@types/xlsx": "^0.0.36",
"@typescript-eslint/eslint-plugin": "4.1.1",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"prettier": "^2.2.0",
"typescript": "^4.1.2",
"babel-eslint": "10.x",
"babel-plugin-import": "^1.13.0",
"cross-env": "^7.0.2",
"eslint-config-react-app": "^5.2.1",
"koa": "^2.13.0",
"koa-body": "^4.2.0",
"koa-logger": "^3.2.1",
"koa-static": "^5.0.0",
"koa2-cors": "^2.0.6",
"lint-staged": "^10.0.7",
"redux-logger": "^3.0.6",
"sass-loader": "^9.0.3"
}
}

View File

@ -1,50 +0,0 @@
const Koa = require('koa');
const { resolve } = require('path');
const staticServer = require('koa-static');
const koaBody = require('koa-body');
const cors = require('koa2-cors');
const logger = require('koa-logger');
const app = new Koa();
app.use(staticServer(resolve(__dirname, './static')));
app.use(koaBody());
app.use(logger());
// 设置跨域
app.use(
cors({
origin: function(ctx) {
if (ctx.url.indexOf('/dooring') > -1) {
return '*'; // 允许来自所有域名请求
}
return '';
},
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'x-test-code'],
maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒
credentials: true,
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: [
'Content-Type',
'Authorization',
'Accept',
'x-requested-with',
'Content-Encoding',
],
}),
);
let htmlStr = '';
app.use(async (ctx, next) => {
console.log(ctx.url);
if (ctx.url === '/dooring/render') {
htmlStr = ctx.request.body;
ctx.body = 'success';
} else if (ctx.url.indexOf('/html') === 0) {
ctx.type = 'html';
ctx.body = htmlStr;
}
});
app.listen(3000);

View File

@ -1,110 +0,0 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import styles from './index.less';
export interface calibrationTypes {
width: number;
height: number;
}
export type CalibrationTypes = {
direction: 'up' | 'left' | 'right';
multiple: number;
id: string;
};
export default function Calibration(props: CalibrationTypes) {
const { direction, multiple } = props;
const [calibrationLength, setCalibration] = useState<calibrationTypes>({ width: 0, height: 0 });
const calibrationRef = useRef<HTMLDivElement>(null);
const generateElement = useCallback(
(item?: boolean, num?: number) => {
if (calibrationRef.current) {
let createSpan = document.createElement('div');
createSpan.className = 'calibrationLine';
createSpan.style.backgroundColor = '#ccc';
calibrationRef.current.style.display = 'flex';
calibrationRef.current.style.justifyContent = 'space-between';
if (direction === 'up') {
calibrationRef.current.style.marginLeft = '50px';
createSpan.style.width = '1px';
createSpan.style.height = '6px';
createSpan.style.display = 'inline-block';
} else {
calibrationRef.current.style.flexDirection = 'column';
createSpan.style.height = '1px';
createSpan.style.width = '6px';
}
if (item) {
let createSpanContent = document.createElement('span');
if (direction === 'up') {
createSpan.style.height = '12px';
createSpanContent.style.transform = 'translate3d(-4px, 20px, 0px)';
createSpan.style.transform = 'translateY(0px)';
} else {
createSpan.style.width = '12px';
createSpanContent.style.paddingLeft = '20px';
}
createSpanContent.style.display = 'block';
createSpanContent.className = 'calibrationNumber';
createSpanContent.innerHTML = num! * 5 + '';
createSpan.appendChild(createSpanContent);
}
calibrationRef.current.appendChild(createSpan);
}
},
[direction],
);
useEffect(() => {
if (calibrationRef.current) {
let calibration = calibrationRef.current.getBoundingClientRect();
setCalibration({ width: calibration.width, height: calibration.height });
let length = direction === 'up' ? calibration.width : calibration.height;
for (let i = 0; i < length / 5; i++) {
if (i % 10 === 0) {
generateElement(true, i);
} else {
generateElement();
}
}
}
}, [direction, generateElement]);
useEffect(() => {
if (calibrationRef.current) {
let width = calibrationLength.width
? calibrationLength.width
: calibrationRef.current.getBoundingClientRect().width;
let height = calibrationLength.height
? calibrationLength.height
: calibrationRef.current.getBoundingClientRect().height;
let arr = [...Array.from(calibrationRef.current.querySelectorAll('.calibrationLine'))];
if (arr.length) {
if (direction === 'up') {
calibrationRef.current.style.width = parseFloat(multiple.toFixed(1)) * width + 'px';
arr.forEach(el => {
let dom = [...Array.from(el.querySelectorAll('.calibrationNumber'))][0] as HTMLElement;
if (dom) {
dom.style.transform = `translate3d(-4px, 16px, 0px) scale(${(multiple + 0.1).toFixed(
1,
)})`;
}
});
} else {
calibrationRef.current.style.height = parseFloat(multiple.toFixed(1)) * height + 'px';
arr.forEach(el => {
let dom = [...Array.from(el.querySelectorAll('.calibrationNumber'))][0] as HTMLElement;
if (dom) {
dom.style.transform = `translate3d(-4px, -8px, 0px) scale(${(multiple + 0.1).toFixed(
1,
)})`;
}
});
}
}
}
}, [calibrationLength.height, calibrationLength.width, direction, multiple]);
return <div className={styles.calibration} ref={calibrationRef}></div>;
}

View File

@ -1,130 +0,0 @@
import { Input, Cell, DateSelect, Radio, Select, Checkbox } from 'zarm';
import styles from './baseForm.less';
import React, { ReactText, useState } from 'react';
import {
baseFormDateTpl,
baseFormMyRadioTpl,
baseFormMyCheckboxTpl,
baseFormMySelectTpl,
baseFormNumberTpl,
baseFormTextAreaTpl,
baseFormTextTpl,
baseFormTextTipTpl,
baseFormUnionType,
} from '@/components/PanelComponents/FormEditor/types';
import { formatTime } from '@/utils/tool';
// 维护表单控件, 提高form渲染性能
type TBaseForm = {
[key in baseFormUnionType]: any;
};
const BaseForm: TBaseForm = {
Text: (props: baseFormTextTpl & { onChange: (v: string | undefined) => void }) => {
const { label, placeholder, onChange } = props;
return (
<Cell title={label}>
<Input clearable type="text" placeholder={placeholder} onChange={onChange} />
</Cell>
);
},
Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => {
const { label, placeholder, onChange } = props;
return (
<Cell title={label}>
<Input
type="text"
rows={3}
autoHeight
showLength
placeholder={placeholder}
onChange={onChange}
/>
</Cell>
);
},
Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => {
const { label, placeholder, onChange } = props;
return (
<Cell title={label}>
<Input type="number" placeholder={placeholder} onChange={onChange} />
</Cell>
);
},
MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => {
const { label, options, onChange } = props;
return (
<div className={styles.radioWrap}>
<div className={styles.radioTitle}>{label}</div>
<Cell>
<Radio.Group onChange={onChange}>
{options.map((item, i) => {
return (
<Radio value={item.value} key={i} className={styles.radioItem}>
{item.label}
</Radio>
);
})}
</Radio.Group>
</Cell>
</div>
);
},
MyCheckbox: (
props: baseFormMyCheckboxTpl & { onChange: (v: Array<ReactText> | undefined) => void },
) => {
const { label, options, onChange } = props;
return (
<div className={styles.radioWrap}>
<div className={styles.radioTitle}>{label}</div>
<Cell>
<Checkbox.Group onChange={onChange}>
{options.map((item, i) => {
return (
<Checkbox value={item.value} key={i} className={styles.radioItem}>
{item.label}
</Checkbox>
);
})}
</Checkbox.Group>
</Cell>
</div>
);
},
Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => {
const { label, placeholder, onChange } = props;
const [value, setValue] = useState<any>('');
const handleChange = (v: any) => {
setValue(v);
onChange && onChange(formatTime('yyyy-MM-dd', v));
};
return (
<Cell title={label}>
<DateSelect
placeholder={placeholder}
mode="date"
min="1949-05-15"
max="2100-05-15"
value={value}
onOk={handleChange}
/>
</Cell>
);
},
MySelect: (
props: baseFormMySelectTpl & { onChange: ((v: Record<string, any>) => void) | undefined },
) => {
const { label, options, onChange } = props;
return (
<Cell title={label}>
<Select dataSource={options} onOk={onChange} />
</Cell>
);
},
MyTextTip: (props: baseFormTextTipTpl) => {
const { label, color, fontSize } = props;
return <Cell title={<div style={{ color, fontSize }}>{label}</div>}></Cell>;
},
};
export default BaseForm;

View File

@ -1,109 +0,0 @@
import React, { ReactText } from 'react';
import { Button } from 'antd';
import {
baseFormDateTpl,
baseFormMyRadioTpl,
baseFormMyCheckboxTpl,
baseFormMySelectTpl,
baseFormNumberTpl,
baseFormTextAreaTpl,
baseFormTextTpl,
baseFormTextTipTpl,
baseFormUnionType,
} from '@/components/PanelComponents/FormEditor/types';
// 维护表单控件, 提高form渲染性能
type TBaseForm = {
[key in baseFormUnionType]: any;
};
const BaseForm: TBaseForm = {
Text: (props: baseFormTextTpl & { onChange: (v: string | undefined) => void }) => {
const { label, onChange } = props;
return (
<Button
style={{
color: '#fff',
backgroundColor: '#4A4A4A',
borderRadius: '2px',
lineHeight: '0px',
}}
onChange={() => onChange}
>
{label}
</Button>
);
},
Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => {
const { label, onChange } = props;
return (
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
);
},
Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => {
const { label, onChange } = props;
return (
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
);
},
MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => {
const { label, onChange } = props;
return (
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
);
},
MyCheckbox: (
props: baseFormMyCheckboxTpl & { onChange: (v: Array<ReactText> | undefined) => void },
) => {
const { label, onChange } = props;
return (
<div>
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
</div>
);
},
Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => {
const { label, onChange } = props;
return (
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
);
},
MySelect: (
props: baseFormMySelectTpl & { onChange: ((v: Record<string, any>) => void) | undefined },
) => {
const { label, onChange } = props;
return (
<Button style={{ color: '#fff', backgroundColor: '#4A4A4A' }} onChange={() => onChange}>
{label}
</Button>
);
},
MyTextTip: (props: baseFormTextTipTpl) => {
const { label } = props;
return (
<Button
style={{
color: '#fff',
backgroundColor: '#4A4A4A',
borderRadius: '2px',
lineHeight: '0px',
}}
>
{label}
</Button>
);
},
};
export default BaseForm;

View File

@ -1,142 +0,0 @@
import {
IColorConfigType,
IFormItemsConfigType,
INumberConfigType,
ITextConfigType,
TColorDefaultType,
TFormItemsDefaultType,
TNumberDefaultType,
TSelectDefaultType,
ISelectConfigType,
TTextDefaultType,
} from '@/components/FormComponents/types';
export type TTextWeightSelectKeyType = '300' | '400' | '500' | '600';
export type TFormEditData = Array<
| ITextConfigType
| INumberConfigType
| IColorConfigType
| ITextConfigType
| IFormItemsConfigType
| ISelectConfigType<TTextWeightSelectKeyType>
>;
export interface IFormConfig {
title: TTextDefaultType;
fontSize: TNumberDefaultType;
titColor: TColorDefaultType;
titWeight: TSelectDefaultType<TTextWeightSelectKeyType>;
bgColor: TColorDefaultType;
btnColor: TColorDefaultType;
btnTextColor: TColorDefaultType;
api: TTextDefaultType;
formControls: TFormItemsDefaultType;
}
export interface IFormSchema {
editData: TFormEditData;
config: IFormConfig;
}
const Form: IFormSchema = {
editData: [
{
key: 'title',
name: '标题',
type: 'Text',
},
{
key: 'fontSize',
name: '标题大小',
type: 'Number',
},
{
key: 'titColor',
name: '标题颜色',
type: 'Color',
},
{
key: 'titWeight',
name: '标题粗细',
type: 'Select',
range: [
{
key: '300',
text: '300 x 300',
},
{
key: '400',
text: '400 x 400',
},
{
key: '500',
text: '500 x 500',
},
{
key: '600',
text: '600 x 600',
},
],
},
{
key: 'bgColor',
name: '背景色',
type: 'Color',
},
{
key: 'btnColor',
name: '按钮颜色',
type: 'Color',
},
{
key: 'btnTextColor',
name: '按钮文本颜色',
type: 'Color',
},
{
key: 'api',
name: '表单Api地址',
type: 'Text',
},
{
key: 'formControls',
name: '表单控件',
type: 'FormItems',
},
],
config: {
title: '表单定制组件',
fontSize: 18,
titColor: 'rgba(60,60,60,1)',
titWeight: '400',
bgColor: 'rgba(255,255,255,1)',
btnColor: 'rgba(20,54,226,100)',
btnTextColor: 'rgba(255,255,255,1)',
api: '',
formControls: [
{
id: '1',
type: 'Text',
label: '姓名',
placeholder: '请输入姓名',
},
{
id: '2',
type: 'Number',
label: '年龄',
placeholder: ' 请输入年龄',
},
{
id: '4',
type: 'MySelect',
label: '爱好',
options: [
{ label: '篮球', value: '1' },
{ label: '乒乓球', value: '2' },
{ label: '健身', value: '3' },
],
},
],
},
};
export default Form;

View File

@ -1,6 +0,0 @@
const template = {
type: 'Form',
h: 172,
displayName: '表格组件',
};
export default template;

View File

@ -1,2 +0,0 @@
import FormItems from './FormItems';
export default FormItems;

View File

@ -1,244 +0,0 @@
////////////////////
export interface IUploadConfigType {
key: string;
name: string;
type: 'Upload';
isCrop?: boolean;
cropRate?: number;
}
export type TUploadDefaultType = Array<{
uid: string;
name: string;
status: string;
url: string;
}>;
/////////////////
export interface ITextConfigType {
key: string;
name: string;
type: 'Text';
}
export type TTextDefaultType = string;
////////////////////////
export interface ITextAreaConfigType {
key: string;
name: string;
type: 'TextArea';
}
export type TTextAreaDefaultType = string;
////////////////////////////
export interface INumberConfigType {
key: string;
name: string;
type: 'Number';
range?: [number, number];
step?: number;
}
export type TNumberDefaultType = number;
///////////////////
export interface IDataListConfigType {
key: string;
name: string;
type: 'DataList';
cropRate?: number;
}
export type TDataListDefaultTypeItem = {
id: string;
title: string;
desc: string;
link: string;
imgUrl: TUploadDefaultType;
type?: number;
price?: string;
};
export type TDataListDefaultType = Array<TDataListDefaultTypeItem>;
////////////////////
export interface IColorConfigType {
key: string;
name: string;
type: 'Color';
}
export type TColorDefaultType = string;
/////////////////
export interface IRichTextConfigType {
key: string;
name: string;
type: 'RichText';
}
export type TRichTextDefaultType = string;
export interface IMutiTextConfigType {
key: string;
name: string;
type: 'MutiText';
}
export type TMutiTextDefaultType = Array<string>;
/////////////////////////////////
export interface ISelectConfigType<KeyType> {
key: string;
name: string;
type: 'Select';
range: Array<{
key: KeyType;
text: string;
}>;
}
export type TSelectDefaultType<KeyType> = KeyType;
/////////////////////////
export interface IRadioConfigType<KeyType> {
key: string;
name: string;
type: 'Radio';
range: Array<{
key: KeyType;
text: string;
}>;
}
export type TRadioDefaultType<KeyType> = KeyType;
///////////////
export interface ISwitchConfigType {
key: string;
name: string;
type: 'Switch';
}
export type TSwitchDefaultType = boolean;
/////////////////////////////
export interface ICardPickerConfigType<T> {
key: string;
name: string;
type: 'CardPicker';
icons: Array<T>;
}
export type TCardPickerDefaultType<T> = T;
/////////////
export interface ITableConfigType {
key: string;
name: string;
type: 'Table';
}
export type TTableDefaultType = Array<{
name: string;
value: number;
}>;
// position input control
export interface IPosConfigType {
key: string;
name: string;
type: 'Pos';
placeObj: {
text: string;
link: string;
};
}
export type TPosItem = number | undefined;
export type TPosDefaultType = [TPosItem, TPosItem];
//////////////////
export interface IFormItemsConfigType {
key: string;
name: string;
type: 'FormItems';
}
//0---------baseform
export type baseFormOptionsType = {
label: string;
value: string;
};
export type baseFormTextTpl = {
id: string;
type: 'Text';
label: string;
placeholder: string;
};
export type baseFormTextTipTpl = {
id: string;
type: 'MyTextTip';
label: string;
color: string;
fontSize: number;
};
export type baseFormNumberTpl = {
id: string;
type: 'Number';
label: string;
placeholder: string;
};
export type baseFormTextAreaTpl = {
id: string;
type: 'Textarea';
label: string;
placeholder: string;
};
export type baseFormMyRadioTpl = {
id: string;
type: 'MyRadio';
label: string;
options: baseFormOptionsType[];
};
export type baseFormMyCheckboxTpl = {
id: string;
type: 'MyCheckbox';
label: string;
options: baseFormOptionsType[];
};
export type baseFormMySelectTpl = {
id: string;
type: 'MySelect';
label: string;
options: baseFormOptionsType[];
};
export type baseFormDateTpl = {
id: string;
type: 'Date';
label: string;
placeholder: string;
};
export type baseFormUnion =
| baseFormTextTpl
| baseFormTextTipTpl
| baseFormNumberTpl
| baseFormTextAreaTpl
| baseFormMyRadioTpl
| baseFormMyCheckboxTpl
| baseFormMySelectTpl
| baseFormDateTpl;
export type baseFormUnionType =
| baseFormTextTpl['type']
| baseFormTextTipTpl['type']
| baseFormNumberTpl['type']
| baseFormTextAreaTpl['type']
| baseFormMyRadioTpl['type']
| baseFormMyCheckboxTpl['type']
| baseFormMySelectTpl['type']
| baseFormDateTpl['type'];
export type TFormItemsDefaultType = Array<baseFormUnion>;

View File

@ -1,364 +0,0 @@
export type AntdIconType =
| 'max'
| 'required'
| 'default'
| 'high'
| 'low'
| 'disabled'
| 'start'
| 'open'
| 'media'
| 'hidden'
| 'cite'
| 'data'
| 'dir'
| 'form'
| 'label'
| 'slot'
| 'span'
| 'style'
| 'summary'
| 'title'
| 'pattern'
| 'async'
| 'defer'
| 'manifest'
| 'color'
| 'content'
| 'size'
| 'wrap'
| 'multiple'
| 'height'
| 'rotate'
| 'translate'
| 'width'
| 'prefix'
| 'src'
| 'children'
| 'key'
| 'list'
| 'step'
| 'aria-label'
| 'spin'
| 'accept'
| 'acceptCharset'
| 'action'
| 'allowFullScreen'
| 'allowTransparency'
| 'alt'
| 'as'
| 'autoComplete'
| 'autoFocus'
| 'autoPlay'
| 'capture'
| 'cellPadding'
| 'cellSpacing'
| 'charSet'
| 'challenge'
| 'checked'
| 'classID'
| 'cols'
| 'colSpan'
| 'controls'
| 'coords'
| 'crossOrigin'
| 'dateTime'
| 'download'
| 'encType'
| 'formAction'
| 'formEncType'
| 'formMethod'
| 'formNoValidate'
| 'formTarget'
| 'frameBorder'
| 'headers'
| 'href'
| 'hrefLang'
| 'htmlFor'
| 'httpEquiv'
| 'integrity'
| 'keyParams'
| 'keyType'
| 'kind'
| 'loop'
| 'marginHeight'
| 'marginWidth'
| 'maxLength'
| 'mediaGroup'
| 'method'
| 'min'
| 'minLength'
| 'muted'
| 'name'
| 'nonce'
| 'noValidate'
| 'optimum'
| 'placeholder'
| 'playsInline'
| 'poster'
| 'preload'
| 'readOnly'
| 'rel'
| 'reversed'
| 'rows'
| 'rowSpan'
| 'sandbox'
| 'scope'
| 'scoped'
| 'scrolling'
| 'seamless'
| 'selected'
| 'shape'
| 'sizes'
| 'srcDoc'
| 'srcLang'
| 'srcSet'
| 'target'
| 'type'
| 'useMap'
| 'value'
| 'wmode'
| 'defaultChecked'
| 'defaultValue'
| 'suppressContentEditableWarning'
| 'suppressHydrationWarning'
| 'accessKey'
| 'className'
| 'contentEditable'
| 'contextMenu'
| 'draggable'
| 'id'
| 'lang'
| 'spellCheck'
| 'tabIndex'
| 'radioGroup'
| 'role'
| 'about'
| 'datatype'
| 'inlist'
| 'property'
| 'resource'
| 'typeof'
| 'vocab'
| 'autoCapitalize'
| 'autoCorrect'
| 'autoSave'
| 'itemProp'
| 'itemScope'
| 'itemType'
| 'itemID'
| 'itemRef'
| 'results'
| 'security'
| 'unselectable'
| 'inputMode'
| 'is'
| 'aria-activedescendant'
| 'aria-atomic'
| 'aria-autocomplete'
| 'aria-busy'
| 'aria-checked'
| 'aria-colcount'
| 'aria-colindex'
| 'aria-colspan'
| 'aria-controls'
| 'aria-current'
| 'aria-describedby'
| 'aria-details'
| 'aria-disabled'
| 'aria-dropeffect'
| 'aria-errormessage'
| 'aria-expanded'
| 'aria-flowto'
| 'aria-grabbed'
| 'aria-haspopup'
| 'aria-hidden'
| 'aria-invalid'
| 'aria-keyshortcuts'
| 'aria-labelledby'
| 'aria-level'
| 'aria-live'
| 'aria-modal'
| 'aria-multiline'
| 'aria-multiselectable'
| 'aria-orientation'
| 'aria-owns'
| 'aria-placeholder'
| 'aria-posinset'
| 'aria-pressed'
| 'aria-readonly'
| 'aria-relevant'
| 'aria-required'
| 'aria-roledescription'
| 'aria-rowcount'
| 'aria-rowindex'
| 'aria-rowspan'
| 'aria-selected'
| 'aria-setsize'
| 'aria-sort'
| 'aria-valuemax'
| 'aria-valuemin'
| 'aria-valuenow'
| 'aria-valuetext'
| 'dangerouslySetInnerHTML'
| 'onCopy'
| 'onCopyCapture'
| 'onCut'
| 'onCutCapture'
| 'onPaste'
| 'onPasteCapture'
| 'onCompositionEnd'
| 'onCompositionEndCapture'
| 'onCompositionStart'
| 'onCompositionStartCapture'
| 'onCompositionUpdate'
| 'onCompositionUpdateCapture'
| 'onFocus'
| 'onFocusCapture'
| 'onBlur'
| 'onBlurCapture'
| 'onChange'
| 'onChangeCapture'
| 'onBeforeInput'
| 'onBeforeInputCapture'
| 'onInput'
| 'onInputCapture'
| 'onReset'
| 'onResetCapture'
| 'onSubmit'
| 'onSubmitCapture'
| 'onInvalid'
| 'onInvalidCapture'
| 'onLoad'
| 'onLoadCapture'
| 'onError'
| 'onErrorCapture'
| 'onKeyDown'
| 'onKeyDownCapture'
| 'onKeyPress'
| 'onKeyPressCapture'
| 'onKeyUp'
| 'onKeyUpCapture'
| 'onAbort'
| 'onAbortCapture'
| 'onCanPlay'
| 'onCanPlayCapture'
| 'onCanPlayThrough'
| 'onCanPlayThroughCapture'
| 'onDurationChange'
| 'onDurationChangeCapture'
| 'onEmptied'
| 'onEmptiedCapture'
| 'onEncrypted'
| 'onEncryptedCapture'
| 'onEnded'
| 'onEndedCapture'
| 'onLoadedData'
| 'onLoadedDataCapture'
| 'onLoadedMetadata'
| 'onLoadedMetadataCapture'
| 'onLoadStart'
| 'onLoadStartCapture'
| 'onPause'
| 'onPauseCapture'
| 'onPlay'
| 'onPlayCapture'
| 'onPlaying'
| 'onPlayingCapture'
| 'onProgress'
| 'onProgressCapture'
| 'onRateChange'
| 'onRateChangeCapture'
| 'onSeeked'
| 'onSeekedCapture'
| 'onSeeking'
| 'onSeekingCapture'
| 'onStalled'
| 'onStalledCapture'
| 'onSuspend'
| 'onSuspendCapture'
| 'onTimeUpdate'
| 'onTimeUpdateCapture'
| 'onVolumeChange'
| 'onVolumeChangeCapture'
| 'onWaiting'
| 'onWaitingCapture'
| 'onAuxClick'
| 'onAuxClickCapture'
| 'onClick'
| 'onClickCapture'
| 'onContextMenu'
| 'onContextMenuCapture'
| 'onDoubleClick'
| 'onDoubleClickCapture'
| 'onDrag'
| 'onDragCapture'
| 'onDragEnd'
| 'onDragEndCapture'
| 'onDragEnter'
| 'onDragEnterCapture'
| 'onDragExit'
| 'onDragExitCapture'
| 'onDragLeave'
| 'onDragLeaveCapture'
| 'onDragOver'
| 'onDragOverCapture'
| 'onDragStart'
| 'onDragStartCapture'
| 'onDrop'
| 'onDropCapture'
| 'onMouseDown'
| 'onMouseDownCapture'
| 'onMouseEnter'
| 'onMouseLeave'
| 'onMouseMove'
| 'onMouseMoveCapture'
| 'onMouseOut'
| 'onMouseOutCapture'
| 'onMouseOver'
| 'onMouseOverCapture'
| 'onMouseUp'
| 'onMouseUpCapture'
| 'onSelect'
| 'onSelectCapture'
| 'onTouchCancel'
| 'onTouchCancelCapture'
| 'onTouchEnd'
| 'onTouchEndCapture'
| 'onTouchMove'
| 'onTouchMoveCapture'
| 'onTouchStart'
| 'onTouchStartCapture'
| 'onPointerDown'
| 'onPointerDownCapture'
| 'onPointerMove'
| 'onPointerMoveCapture'
| 'onPointerUp'
| 'onPointerUpCapture'
| 'onPointerCancel'
| 'onPointerCancelCapture'
| 'onPointerEnter'
| 'onPointerEnterCapture'
| 'onPointerLeave'
| 'onPointerLeaveCapture'
| 'onPointerOver'
| 'onPointerOverCapture'
| 'onPointerOut'
| 'onPointerOutCapture'
| 'onGotPointerCapture'
| 'onGotPointerCaptureCapture'
| 'onLostPointerCapture'
| 'onLostPointerCaptureCapture'
| 'onScroll'
| 'onScrollCapture'
| 'onWheel'
| 'onWheelCapture'
| 'onAnimationStart'
| 'onAnimationStartCapture'
| 'onAnimationEnd'
| 'onAnimationEndCapture'
| 'onAnimationIteration'
| 'onAnimationIterationCapture'
| 'onTransitionEnd'
| 'onTransitionEndCapture'
| 'twoToneColor';

View File

@ -1,43 +0,0 @@
import React, { memo } from 'react';
import * as Icon from '@ant-design/icons';
import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon';
import { AntdIconType } from './icon';
import { IIconConfig } from './schema';
import logo from '@/assets/icon_01.png';
interface IconType extends IIconConfig {
isTpl?: boolean;
}
const XIcon = memo((props: IconType) => {
const { color, size, link, text, fontSize, fontColor, type, spin, isTpl } = props;
const MyIcon: React.ForwardRefExoticComponent<Pick<AntdIconProps, AntdIconType> &
React.RefAttributes<HTMLSpanElement>> = Icon[type];
const handleClick = () => {
if (!link || window.location.href.indexOf('/editor') > -1) return;
window.location.href = link;
};
return isTpl ? (
<div>
<img style={{ width: '100%' }} src={logo} alt="" />
</div>
) : (
<div
style={{
display: 'flex',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
}}
>
<MyIcon twoToneColor={color} style={{ fontSize: size }} spin={spin} onClick={handleClick} />
<div style={{ fontSize, color: fontColor, paddingTop: '6px' }}> {text} </div>
</div>
);
});
export default XIcon;

View File

@ -1,178 +0,0 @@
import {
ICardPickerConfigType,
IColorConfigType,
ITextConfigType,
INumberConfigType,
ISwitchConfigType,
TCardPickerDefaultType,
TColorDefaultType,
TNumberDefaultType,
TSwitchDefaultType,
TTextDefaultType,
} from '@/components/FormComponents/types';
export type TIconEditData = Array<
| IColorConfigType
| INumberConfigType
| ISwitchConfigType
| ICardPickerConfigType<IconTypes>
| ITextConfigType
>;
export interface IIconConfig {
color: TColorDefaultType;
size: TNumberDefaultType;
text: TTextDefaultType;
fontSize: TNumberDefaultType;
fontColor: TColorDefaultType;
spin: TSwitchDefaultType;
link: TTextDefaultType;
type: TCardPickerDefaultType<IconTypes>;
}
export interface IIconSchema {
editData: TIconEditData;
config: IIconConfig;
}
export type IconTypes =
| 'AccountBookTwoTone'
| 'AlertTwoTone'
| 'ApiTwoTone'
| 'AppstoreTwoTone'
| 'AudioTwoTone'
| 'BankTwoTone'
| 'BellTwoTone'
| 'BookTwoTone'
| 'BugTwoTone'
| 'BuildTwoTone'
| 'BulbTwoTone'
| 'CalculatorTwoTone'
| 'CalendarTwoTone'
| 'CameraTwoTone'
| 'CarTwoTone'
| 'CarryOutTwoTone'
| 'CiCircleTwoTone'
| 'CloudTwoTone'
| 'CodeTwoTone'
| 'CrownTwoTone'
| 'CustomerServiceTwoTone'
| 'DollarCircleTwoTone'
| 'EnvironmentTwoTone'
| 'ExperimentTwoTone'
| 'FireTwoTone'
| 'GiftTwoTone'
| 'InsuranceTwoTone'
| 'LikeTwoTone'
| 'LockTwoTone'
| 'MailTwoTone'
| 'MessageTwoTone'
| 'PhoneTwoTone'
| 'PictureTwoTone'
| 'PlaySquareTwoTone'
| 'RedEnvelopeTwoTone'
| 'ShopTwoTone'
| 'TrademarkCircleTwoTone'
| 'StarTwoTone'
| 'SafetyCertificateTwoTone'
| 'SettingTwoTone'
| 'RocketTwoTone';
const Icon: IIconSchema = {
editData: [
{
key: 'color',
name: '颜色',
type: 'Color',
},
{
key: 'size',
name: '大小',
type: 'Number',
},
{
key: 'text',
name: '文本',
type: 'Text',
},
{
key: 'fontSize',
name: '文本大小',
type: 'Number',
},
{
key: 'fontColor',
name: '文本颜色',
type: 'Color',
},
{
key: 'link',
name: '跳转链接',
type: 'Text',
},
{
key: 'spin',
name: '旋转动画',
type: 'Switch',
},
{
key: 'type',
name: '图标类型',
type: 'CardPicker',
icons: [
'AccountBookTwoTone',
'AlertTwoTone',
'ApiTwoTone',
'AppstoreTwoTone',
'AudioTwoTone',
'BankTwoTone',
'BellTwoTone',
'BookTwoTone',
'BugTwoTone',
'BuildTwoTone',
'BulbTwoTone',
'CalculatorTwoTone',
'CalendarTwoTone',
'CameraTwoTone',
'CarTwoTone',
'CarryOutTwoTone',
'CiCircleTwoTone',
'CloudTwoTone',
'CodeTwoTone',
'CrownTwoTone',
'CustomerServiceTwoTone',
'DollarCircleTwoTone',
'EnvironmentTwoTone',
'ExperimentTwoTone',
'FireTwoTone',
'GiftTwoTone',
'InsuranceTwoTone',
'LikeTwoTone',
'LockTwoTone',
'MailTwoTone',
'MessageTwoTone',
'PhoneTwoTone',
'PictureTwoTone',
'PlaySquareTwoTone',
'RedEnvelopeTwoTone',
'ShopTwoTone',
'TrademarkCircleTwoTone',
'StarTwoTone',
'SafetyCertificateTwoTone',
'SettingTwoTone',
'RocketTwoTone',
],
},
],
config: {
color: 'rgba(74,144,226,1)',
size: 36,
text: '图标',
fontSize: 14,
fontColor: 'rgba(0,0,0,1)',
link: '',
spin: false,
type: 'CarTwoTone',
},
};
export default Icon;

View File

@ -1,14 +0,0 @@
import React from 'react';
export default () => (
<div className="rotate-animate">
<div className="line-scale-pulse-out">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<h3 style={{textAlign: 'center', fontStyle: 'italic', color: '#2842d8'}}>H5-Dooring, H5制作更简单</h3>
</div>
);

View File

@ -1,2 +0,0 @@
import FormRender from './renderer/FormRender';
export { FormRender };

View File

@ -1,66 +0,0 @@
import { useMemo, memo, ReactNode, CSSProperties } from 'react';
import { useDrag } from 'react-dnd';
import dooringCompt from 'dooringUI/components';
import styles from './index.less';
interface TargetBoxProps {
item: any;
children: ReactNode;
canvasId: string;
}
const { schemaH5 } = dooringCompt
const SourceBox = memo((props: TargetBoxProps) => {
const { item } = props;
const [{ isDragging }, drag] = useDrag({
item: {
type: item.type,
config: schemaH5[item.type].config,
h: item.h,
editableEl: schemaH5[item.type].editData,
category: item.category,
x: item.x || 0,
},
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});
const containerStyle: CSSProperties = useMemo(
() => ({
opacity: isDragging ? 0.4 : 1,
cursor: 'move',
height: '140px',
}),
[isDragging],
);
return <div className={styles.listWrap}>
<div className={styles.module} style={{ ...containerStyle }} ref={drag}>
<div
style={{
height: '110px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
overflow: 'hidden',
}}
>
{props.children}
</div>
<div
style={{
height: '30px',
lineHeight: '30px',
textAlign: 'center',
backgroundColor: 'rgba(245, 245, 245, 1)',
color: 'rgba(118, 118, 118, 1)',
}}
>
{props.item.displayName}
</div>
</div>
</div>
});
export default SourceBox;

View File

@ -1,5 +0,0 @@
import req from '@/utils/req'
export function getTemplate(data) {
return req('/test', { method: 'GET', params: data })
}

View File

@ -1,26 +0,0 @@
.loginWrap {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background: #f0f0f0 url(../../assets/login_bg.png) center center;
background-size: cover;
.tit {
font-size: 35px;
text-align: center;
padding-bottom: 20px;
margin-bottom: 36px;
border-bottom: 1px solid #f0f0f0;
}
.formWrap {
margin-left: auto;
margin-right: auto;
width: 520px;
padding: 22px 0 20px;
background-color: #fff;
box-shadow: 0 0 20px rgba(0,0,0,.1);
border-radius: 6px;
}
}

View File

@ -1,107 +0,0 @@
import { Form, Input, Button, Popover, message } from 'antd';
import React, { useState } from 'react';
import req from '@/utils/req';
import { history } from 'umi';
import { Vertify } from '@alex_xu/react-slider-vertify';
import styles from './index.less';
interface FormValues {
n: string;
co: string;
}
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const tailLayout = {
wrapperCol: { offset: 6, span: 16 },
};
const content = (
<>
<div style={{ textAlign: 'center' }}>
<img
style={{ width: '180px' }}
src="http://cdn.dooring.cn/dr/qtqd_code.png"
alt="趣谈前端-徐小夕"
/>
</div>
<p style={{ width: '240px' }}>"登录码" </p>
</>
);
const Login = () => {
const [visible, setVisible] = useState(false);
const [isVertify, setVertify] = useState(false);
const onFinish = (values: FormValues) => {
if (!isVertify) {
setVisible(true);
return;
}
const { n, co } = values;
req
.post('/vip/check', { n, co })
.then((res: any) => {
localStorage.setItem('nickname', res.n);
localStorage.setItem('h5', JSON.stringify(res.h5));
history.push('/');
})
.catch(err => {
// 模拟, 需要上真实验证接口
localStorage.setItem('nickname', 'test');
localStorage.setItem('h5', JSON.stringify([]));
history.push('/');
});
};
const handleSuccess = () => {
message.success('验证成功', 1, () => {
setVisible(false);
setVertify(true);
});
};
return (
<div className={styles.loginWrap}>
<Form
{...layout}
name="login"
className={styles.formWrap}
initialValues={{ remember: true }}
onFinish={onFinish}
>
<div className={styles.tit}>
Dooring开放平台
<span style={{ marginLeft: '20px', fontSize: '18px', color: '#06c' }}></span>
</div>
<Form.Item label="用户名" name="n" rules={[{ required: true, message: '请输入用户名!' }]}>
<Input />
</Form.Item>
<Form.Item label="密码" name="co" rules={[{ required: true, message: '请输入密码!' }]}>
<Input.Password />
</Form.Item>
<div style={{ margin: '-16px 0 12px 116px' }}>
<Popover placement="topLeft" content={content} trigger="click">
<Button type="link"></Button>
</Popover>
</div>
<div style={{ margin: '-16px 0 12px 76px' }}>
<Vertify width={330} height={160} visible={visible} l={36} onSuccess={handleSuccess} />
</div>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" block>
</Button>
</Form.Item>
</Form>
</div>
);
};
export default Login;

Some files were not shown because too many files have changed in this diff Show More