# Technical Documentation: MrXujiang/h5-Dooring

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/MrXujiang/h5-Dooring)

### Doc README (`doc/README.md`)

---
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
---

---

### Doc Zh Guide README (`doc/zh/guide/README.md`)

<img src="../../img/common/logo.svg" alt="foo">

H5-Dooring 是一款功能强大，高可扩展的 H5 可视化页面配置解决方案，致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳实践。

## 功能特点

🎉 **可扩展，** Dooring 实现了较为完整的业务闭环，并使其模块化，编辑器内部功能接口也全部可以对接不同服务端语言，实现了标准化接口。此外还支持自定义组件，二次开发，设计模板等能力，以满足功能和跨领域的分层需求。

📦 **开箱即用，** Dooring 内置了**表单渲染器、页面渲染器、动态加载内核**等，仅需一套源码即可上手开发。并且还提供针对 React 的定制插件，内涵丰富的功能，可满足日常 80%的页面制作需求。

🚀 **大量自研，** 包含整个编辑器架构、组件设计、文档、请求库封装，后台管理系统等，满足日常项目的周边需求。

🚄 **与时俱进，** 在满足需求的同时，我们也不会停止对新技术的探索。比如更多**营销组件、业务功能，后台管理可视化，PC 页面编辑器，数据大屏定制**等等。

## 为什么选择 Dooring

目前**github**已超过 3000+star，上线 2 个月累计 500+用户使用，解决完善了 100+问题，后续会持续迭代，更新，自研优秀，先进的 lowcode/nocode 解决方案。

---

### Doc Zh Guide DirectoryStructure (`doc/zh/guide/directoryStructure.md`)

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Doc Zh Guide StartedQuickly (`doc/zh/guide/startedQuickly.md`)

# 快速上手

## 从零搭建一个 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 版本）

```
$ node-v
v10.13.0
```

注：推荐使用 yarn 管理 npm 依赖

## 源码工程

| h5_plus(编辑器项目) | admin(管理后台) | Server(服务端项目) |
| ------------------- | --------------- | ------------------ |


本地拿到源码工程之后先安装对应依赖，在对应工程目录里执行 yarn 命令，等待依赖安装完成。

## 本地运行

1.首先本地启动 server，在 src 目录的 index.js 中修改跨域白名单，改为本地的 ip+端口，如http://192.167.0.3:8000

2.其次本地启动 h5_plus,启动完毕在浏览器打开对应的启动地址即可查看，如下：

<img src="../../img/common/home.png" alt="foo">

---

### Doc Zh Guide ComponentDev ComponentStructure (`doc/zh/guide/componentDev/componentStructure.md`)

# 组件结构

dooring 的组件设计包含以下 3 个部分组件：

1、component 组件主体

2、schema 组件的 DSL，结构协议层

3、template 定义了组件的类型、外观、从属关系，后期考虑纳入 schema

接下来我会介绍一个基本的组件主体设计，以为 template 设计，在下一章会具体介绍 schema 部分。

## 组件设计

我们这里拿基本的 header 组件来举例，如下是 header 组件的代码：

```jsx
interface HeaderPropTypes extends IHeaderConfig {
  isTpl: boolean;
}

const Header = memo((props: HeaderPropTypes) => {
  const { bgColor, logo, logoText, fontSize, color } = props;
  return props.isTpl ? (
    <div>
      <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} />
      </div>
      <div className={styles.title} style={{ fontSize, color }}>
        {logoText}
      </div>
    </header>
  );
});
```

我们只需要按照上面的方式编写组件即可，props 是 DSL 定义的数据层，用来控制组件的 shape，也就是组件的表现。我们看看 header 对应的 template。

## template 设计

```js
const template = {
  type: "Header",
  h: 28,
  displayName: "页头组件"
};
export default template;
```

以上就是我们 template 的结构，type 用来定义组件的类型，方便渲染器动态查找，h 代表组件的初始化高度，我们可以自由设置。displayName 是组件的中文名，用来在左侧组件面板中展示，方便用户理解，我们可以在 template 中自定义更多辅助信息，方便使用者更高效的使用我们的编辑器。

## schema 设计

开发一个自定义组件需要包含 3 部分, `Component`, `Schema` 和 `Template`. 接下来我们看一下 `Header` 组件的 `Schema`.

```
/* Detailed source-code truncated for AI context efficiency. */
```

`editData`表示组件的可编辑属性, 我们可以自定义哪些组件可编辑. `config`为组件接收的属性, 和`editData`数组项中的`key`一一对应.

### 组件编辑区属性类型

`Dooring`组件编辑面板有如下对应编辑类型:

- Upload 上传组件
- Text 文本框
- RichText 富文本
- TextArea 多行文本
- Number 数字输入框
- DataList 列表编辑器
- FileList 文件列表编辑器
- InteractionData 交互设置
- Color 颜色面板
- MutiText 多文本
- Select 选择下拉框
- Radio 单选框
- Switch 开关切换
- CardPicker 卡片面板
- Table 表格编辑器
- Pos 坐标编辑器
- FormItems 表单设计器

---

### Doc Zh Guide ComponentDev DSLAnalysis (`doc/zh/guide/componentDev/DSLAnalysis.md`)

# 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 一一对应。

---

### Doc Zh Guide ComponentDev DynamicLoading (`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)

---

### Doc Zh Guide DeployDev Api (`doc/zh/guide/deployDev/api.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 |

返回示例

```
/* Detailed source-code truncated for AI context efficiency. */
```

### 保存 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": "删除成功"
}
```

## 文件上传

## 数据统计

### 数据大盘接口

### 页面埋点

---

### Doc Zh Guide DeployDev Deploy (`doc/zh/guide/deployDev/deploy.md`)

私有化部署需要获取 4 个核心项目包, 包括

- H5 编辑器(h5_plus)
- H5 基座(h5)
- Dooring 管理后台(Dooring-Admin)
- 服务端项目(Server)

获取以上四个核心源码工程需要满足商业授权协议, 具体可联系作者[徐小夕](http://h5.dooring.cn/uploads/WechatIMG3_1758e9753e2.jpeg)

### 部署架构图

<img src="../../../img/common/deploy.png" alt="H5-dooring部署">

部署流程如下:

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`进行容器化部署.

### 步骤 3.4 详细流程

#### 1. 安装项目环境

服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:

```
npm install
```

#### 2. 修改项目域名

进入`./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`启动项目

---

### Doc Zh Guide DeployDev Deploy V6 (`doc/zh/guide/deployDev/deploy_v6.md`)

私有化部署需要获取 3 个核心项目包, 包括

- 可视化大屏编辑器(v6.dooring/)
- v6 管理后台(v6.dooring/manage)
- 服务端项目(v6.dooring/server)

获取以上三个核心源码工程需要满足商业授权协议, 具体可参考[商业授权方案](http://h5.dooring.cn/h5_plus/price)

### 部署架构图

<img src="../../../img/common/v6.deploy.png" alt="H5-dooring部署">

部署流程如下:

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`进行容器化部署.

### 步骤 3.4 详细流程

#### 1. 安装项目环境

服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:

```
npm install
```

#### 2. 修改项目域名

进入`./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`启动项目

---

### Doc Zh Guide DeployDev Dir (`doc/zh/guide/deployDev/dir.md`)

服务端主要是我们的`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 级联动数据, 为表单组件提供数据支持

---

### Doc Zh Guide DeployDev Form (`doc/zh/guide/deployDev/form.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 等).

---

### Doc Zh Guide DeployDev Https (`doc/zh/guide/deployDev/https.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();
```

---

### Doc Zh Guide DeployDev Log (`doc/zh/guide/deployDev/log.md`)

### 更新日志

#### 1.99

1. 添加数据源功能
2. 视频组件支持封面图
3. 优化页面 DSL 结构, 降低了 jsonSchema 体积 30%-50%
4. 官网优化
5. 管理后台替换 logo, 部分文案信息
6. 添加图片列表组件
7. 轮播图支持一键绑定数据源

#### 1.98

1. 编辑器功能区添加更多折叠下拉框, 优化头部界面
2. 添加数据源入口和界面
3. 模版库优化, 剔除无用模版, 累计 60+模版
4. 入口页添加赞助墙
5. 升级视频组件, 支持弹幕, 截屏, 模式设置等功能
6. 文件上传路径兼容 window 服务器本地化部署

#### 1.96

1. 修复首页推荐项目外链地址和站内文案
2. 替换 Dooring 网站 logo
3. 优化 ios8 以下访问 H5 时可能出现的页面卡顿问题
4. 图片上传组件添加 svg, gif 图片格式支持
5. 后台管理系统添加一键跳编辑器按钮
6. 服务端编辑侧路由加固
7. 文件上传组件添加自定义上传文档，支持七牛云，腾讯云，阿里 oss 等第三方图床方式

#### 1.95

1. dooring 文档添加更新日志模块
2. dooring 增报错监控函数, 提供一键清空缓存按钮和自动重载功能
3. 新增电商商品 H5 模版
4. 页面配置增加背景模式和背景重复
5. 表单添加字段名配置项

#### 1.94

1. 转盘组件支持转盘交互功能(跳转链接/打开弹窗/自定义代码)
2. 添加网站拦截, 防止页面误关导致页面无法保存
3. 优化页面控制条组件样式
4. 按钮组件添加组件动画
5. 图片组件添加组件动画
6. 媒体组件 icon 优化
7. 全局错误监控组件添加一键清除缓存功能

#### 1.93

1. 上线源码下载功能
2. 服务端支持下载源码服务和下载次数限制
3. 界面部分文案优化
4. 出码基座优化
5. 抽奖组件支持抽奖后自定义交互(弹窗/链接/自定义代码)

#### 1.92

1. 修复背景图预览适配问题
2. 转盘组件支持中奖后自定义交互/弹窗/自定义代码
3. 界面局部调整
4. 后台管理表单数据支持多键查询
5. 可视化大屏柱状图组件支持实时数据请求

---

### Doc Zh Guide DeployDev Oss (`doc/zh/guide/deployDev/oss.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;
};
```

---

### Doc Zh Guide FunctionRealization Download (`doc/zh/guide/functionRealization/download.md`)

## 下载源码

目前 Dooring 已支持下载源码功能, 我们可以使用编辑器页面头部的下载按钮来实现下载用户搭建的 H5 源码.
<img src="../../../img/functionRealization/down.png" alt="foo">

源码下载之后是完整的 React 项目源代码, 开发人员可以直接根据自己的业务需求来二次编写代码来满足不同的业务需求.

在拿到源码之后, 我们需要进入项目, 使用 npm 或者 yarn 安装项目依赖, 如下:

```bash
npm install
// 或者
yarn
```

之后我们就可以本地运行项目了:

```bash
npm start
// 或者
yarn start
```

因为源码工程采用`umi3.0`搭建, 所以代码配置可以参考`umi3.0`规范, 比如路由配置, `history`模式, 打包路径等, 二次开发完成之后, 我们可以执行:

```bash
npm run build
// 或者
yarn build
```

将项目打包成 html, 以便部署到任何服务器中.

---

### Doc Zh Guide FunctionRealization MachinePreview (`doc/zh/guide/functionRealization/machinePreview.md`)

# 真机预览

真机预览和网页预览的流程类似，工作流程如下：

<img src="../../../img/functionRealization/preview-machine.png" alt="foo">

由于不同机型预览的效果有些许不同，最终效果以实际看到的为主。

---

### Doc Zh Guide FunctionRealization PagePreview (`doc/zh/guide/functionRealization/pagePreview.md`)

# 网页预览

我们看看网页预览的工作流程：

<img src="../../../img/functionRealization/preview-flow.png" alt="foo">

前端预览界面：

<img src="../../../img/functionRealization/preview-page.png" alt="foo">

---

### Doc Zh Guide FunctionRealization Revocation (`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 的日志输出。

---

### Doc Zh Guide FunctionRealization SaveJson (`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)]上参考体验。

---

### Doc Zh Guide FunctionRealization Screenshot (`doc/zh/guide/functionRealization/screenshot.md`)

# 截图功能

截图功能这里我们主要使用了 dom-to-image 这个库，来将 html 转化为图片，并进行分享。

<img src="../../../img/functionRealization/screenshot.png" alt="foo">

---

### Doc Zh Guide FunctionRealization TemplateLibrary (`doc/zh/guide/functionRealization/templateLibrary.md`)

## 模板库实现思路

我们目前开放了模板库功能，一方面我们会定期配置行业模板，另一个方面 Dooring 还支持用户自己配置模板，可以一键保存到云端供用户使用。我们也可以将模板变成自己的页面共享给其他人。实现方式本质上是保存用户的配置信息，上传到服务器中做存储，在后台提供了管理模板的模块，可以修改，删除模板。如下图所示：

### 模板前台展示：

<img src="../../../img/functionRealization/template-ft.png" alt="foo">

### 模板后台展示：

<img src="../../../img/functionRealization/template-bg.png" alt="foo">

---

--- METRICS ---
- Files Extracted: 22
- Estimated Token Budget: ~5902 tokens
- Canonical Reference: https://codewiki.google/github.com/MrXujiang/h5-Dooring
