H5-Dooring 是一款低代码(LowCode),高可扩展的 H5 可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳实践。
H5-Dooring 正是为了解决企业内部可视化搭建需求的解决方案, 它更多的是提供一套可视化搭建解决方案, 以源码的方式交付企业, 使得企业能从此方案中受益, 二次开发适合自身业务需求的搭建平台。H5-dooring也提供部署服务, 来快速帮企业做项目部署。目前已完成了常用的功能:
可视化编辑器包括的核心功能区有:组件区、画布区、顶部功能区、数据源
组件主要包括 基础组件, 图表组件, 媒体组件, 商品组件, 功能组件, 当然企业也可以基于自身业务划分不同的分类, 并进行组件的二次开发。我们只需要从左侧拖拽组件到画布, 即可使用该组件。
同时我们还提供了组件定制的能力, 让用户选择自己常用的组件, 这样用户可以更高效的搭建页面。
画布区可以动态调整画布大小来试试预览不同尺寸的样式, 也可以移动画布, 缩放画布来快捷的操作页面。
顶部功能区包括的功能有:模版库、保存、下载源码、导出json、导入json、预览、真机预览、撤销/重做、删除、截图、页面设置。
数据源主要为平台用户提供一种高效的数据接入机制, 不同页面或者统同一页面的不同组件可以共享数据。
Dooring后台管理 主要是为 H5-Dooring 提供数据支撑, 比如增删查改等操作, 同时随着用户需求的不断增加, Dooring后台管理 目前已实现了非常多的功能, 比如说表单数据收集, 表单数据分析, 导出数据, 基本的页面数据监控。
后台主页主要是对编辑器页面提供基本的访问量统计, 同时对用户数, 模版数, 页面数进行统计, 企业可以根据自身需求二次开发更多数据统计方案。
用户管理主要是对网站用户进行管理(注册, 修改, 删除, 查看等), 当然只有超级管理员能看到, 目前我们做了简单的权限管理: 超级管理员, 普通用户. 普通用户可以管理自己的页面, 查看页面数据分析等,超级管理员可以使用所有功能, 比如管理用户, 生成注册链接, 模版管理, 页面管理等, 同时可以审核页面, 一键删除其他用户产生的不符合规定的页面。
页面管理主要是对用户搭建的H5页面进行管理, 我们可以查看页面的链接, 页面访问量, 编辑页面标题, 删除页面等,如果这个页面包含表单, 我们还能一键查看表单数据的收集情况,并一键进行数据分析。
使用 1.2 可视化编辑器 中介绍的组件来搭建一个H5表单页面,并下载源码工程。
首先得有 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,启动完毕在浏览器打开对应的启动地址即可查看,如下:
/h5_plus
H5编辑器项目/iH5
Dooring后台管理系统/doc
Dooring文档我们这里拿基本的header组件来举例,如下是header组件的代码:
interface HeaderPropTypes extends IHeaderConfig {
isTpl: boolean;
}
const Header = memo((props: HeaderPropTypes) => {
const { bgColor, logo, logoText, fontSize, color } = props;
return props.isTpl ? (
< img style={{width: '100%'}} src={logos} alt="" />
) : (
< img src={logo && logo[0].url} alt={logoText} />
{logoText}
);
});
我们只需要按照上面的方式编写组件即可,props是DSL定义的数据层,用来控制组件的shape,也就是组件的表现。我们看看header对应的template。
const template = {
type: 'Header',
h: 28,
displayName: '页头组件'
};
export default template;
以上就是我们template的结构,type用来定义组件的类型,方便渲染器动态查找,h代表组件的初始化高度,我们可以自由设置。displayName是组件的中文名,用来在左侧组件面板中展示,方便用户理解,我们可以在template中自定义更多辅助信息,方便使用者更高效的使用我们的编辑器。
开发一个自定义组件需要包含3部分, Component
, Schema
和 Template
. 接下来我们看一下 Header
组件的 Schema
.
import {
IColorConfigType,
INumberConfigType,
ITextConfigType,
IUploadConfigType,
TColorDefaultType,
TNumberDefaultType,
TTextDefaultType,
TUploadDefaultType,
} from '@/components/FormComponents/types';
import { baseConfig, baseDefault, ICommonBaseType } from '../../common';
export type THeaderEditData = Array<
IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType
>;
export interface IHeaderConfig extends ICommonBaseType {
bgColor: TColorDefaultType;
logo: TUploadDefaultType;
logoText: TTextDefaultType;
fontSize: TNumberDefaultType;
color: TColorDefaultType;
height: TNumberDefaultType;
}
export interface IHeaderSchema {
editData: THeaderEditData;
config: IHeaderConfig;
}
const Header: IHeaderSchema = {
editData: [
...baseConfig,
{
key: 'bgColor',
name: '背景色',
type: 'Color',
},
{
key: 'height',
name: '高度',
type: 'Number',
},
{
key: 'logo',
name: 'logo',
type: 'Upload',
isCrop: true,
cropRate: 1000 / 618,
},
{
key: 'logoText',
name: 'logo文字',
type: 'Text',
},
{
key: 'color',
name: '文字颜色',
type: 'Color',
},
{
key: 'fontSize',
name: '文字大小',
type: 'Number',
},
],
config: {
bgColor: 'rgba(0,0,0,1)',
logo: [
{
uid: '001',
name: 'image.png',
status: 'done',
url: 'http://49.234.61.19/uploads/3_1740be8a482.png',
},
],
logoText: '页头Header',
fontSize: 20,
color: 'rgba(255,255,255,1)',
height: 50,
...baseDefault,
},
};
export default Header;
editData
表示组件的可编辑属性, 我们可以自定义哪些组件可编辑. config
为组件接收的属性, 和editData
数组项中的key
一一对应。
DSL层主要约定了Dooring组件的数据协议,包括组件的可编辑属性、编辑类型、初始值等,之所以定义一致的协议层,主要是方便后期的组件扩展,配置后移,有助于不同后端语言开发和数据存储,接下来我们看看header组件的schema。
1.editData 可编辑的属性类型DSL
2.config 可编辑组件的默认属性
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一一对应。
我们要想实现完整的组件商店工作流,需要满足以下几点:
有了以上4块的支持,基本的组件商店就可以 work 了。具体流程如下:
当“ 生产者 ”编写好组件代码之后,需要对组件自身进行定义。因为可视化平台组件物料很依赖平台的组件开发协议,我们需要按照平台的规范去上传规范的自定义组件,这样平台才能更好的理解应用组件,保持用户认知的一致性。 组件描述信息笔者这里设计了如下字段:
组件审批主要由网站管理人员来操作,当用户组件提交成功之后,客户端会通过消息信令通知管理员,管理员收到消息后会审核组件。
部署流程如下:
server
的static目录下server
下本地运行 yarn start
或 npm start
启动服务端进行本地测试yarn build
生成 dist
目录, 建议使用 pm2
做nodejs
服务的负载均衡, 运行 pm2 start dist/index.js
启动生产环境代码也可以将以上步骤集成到gitlab等CI, CD服务中, 进行自动化打包发布, 或者采用docker
进行容器化部署.
服务器需提前安装node和pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
npm install
进入./src/config/index.js
, 修改staticPath
变量为当前服务器域名/ip, 如http://xxx.com
或http://xxx.com:8080
(如非80端口)
执行npm run build
编译项目, 生成dist
目录
在项目根目录执行 pm2 start dist/index.js
启动项目
目前H5-Dooring全面支持https部署, 具体方式方案如下。
我们需要在前端工程中的src/pages/document.ejs
中的head
中添加如下代码:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
目的是强制将页面中HTTP请求转换为HTTPS。
server
中的src/index.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()
服务端主要是我们的server
工程, 数据主要存放在server/public
下, 具体数据指代含义我们接下来会详细介绍.
**H5-Dooring **全面支持第三方对象存储服务, 我们以七牛云对象存储为例.
首先我们需要在第三方对象储存服务中配置对应的服务和域名. 其次安装对应的sdk, 如七牛云sdk:
import * as qiniu from 'qiniu-js';
其次我们修改h5_plus
工程的Upload
组件, 详细地址为src/core/FormComponents/Upload
.
修改内容如下:
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官网 (opens new window)中找到我们.
首先我们的上传组件Upload
使用内部的服务接口来实现上传功能, 所以需要给组件的action
赋值, 如下:
{fileList.length >= maxLen ? null : uploadButton}
如果需要集成第三方oss, 如七牛云, 阿里oss等, 我们需要将Upload
组件的action
属性设置为空字符串, 其次删除onChange
属性, 上传操作统一在beforeUpload
中进行. 案例如下:
{fileList.length >= maxLen ? null : uploadButton}
自定义上传的核心逻辑放在了beforeUpload
上. 我们具体看看beforeUpload
这个方法如何实现.
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;
}
Form表单组件在editor
目录下src/components/BasicShop/BasicComponents
位置.
Form组件是Dooring
的核心组件之一, 内部的值通过Form组件内部收集, 当然我们也可以暴露出来让其他交互或者组件消费(需要一定的二次开发), 关键代码如下:
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等).
详见 http://h5.dooring.cn/doc/zh/guide/deployDev/api.html#%E7%94%A8%E6%88%B7%E7%9B%B8%E5%85%B3