可視化搭建平臺(tái)的地圖組件和日歷組件方案選型
可視化搭建平臺(tái)除了需要為用戶提供簡單便捷的操作方式之外, 還需要提供豐富的組件支持和組件擴(kuò)展, 這樣才能滿足更多用戶的業(yè)務(wù)需求.
在 H5-dooring 創(chuàng)建的初期主要考慮的方向是用戶使用的便捷性, 即最大程度的降低用戶操作成本, 所以采用了智能布局, 也就是react-grid-layout這個(gè)庫, 之前考慮過完全的自由布局, 也實(shí)現(xiàn)了一套自由布局的方案(使用react-draggable和React-Resizable), 但是崇尚 less is more 的設(shè)計(jì)哲學(xué), 還是堅(jiān)定的走了智能布局的道路.
筆者接下來會(huì)介紹如何在 H5頁面編輯器 中自定義開發(fā)自己的組件, 以及如何開發(fā)可以使H5展現(xiàn)力更強(qiáng)的組件: 地圖和日歷組件.
如果大家對(duì)可視化拖拽搭建平臺(tái)的實(shí)現(xiàn)方案感興趣, 可以參考我之前的文章和 github. 后續(xù)會(huì)更新更多l(xiāng)owcode和nocode的技術(shù)實(shí)現(xiàn)和國內(nèi)外方案分析.
演示效果

實(shí)現(xiàn)自定義組件開發(fā)的流程
作為前端工程師, 我們對(duì)于開發(fā)vue組件或者react組件想必不會(huì)很陌生, 對(duì)于一個(gè)可擴(kuò)展復(fù)用當(dāng)然組件來說, 我們只需要做好以下幾點(diǎn)就好了:
- 語義化 : 組件命名可讀性強(qiáng), 比如antd, element的組件風(fēng)格
- 重用-發(fā)布等價(jià)原則(REP): 組件中的類要么都是可重用的,要么都不可重用
- 共同重用原則(CRP): 組件中所有類應(yīng)該是共同重用的,如果重用了組件中的一個(gè)類就應(yīng)該重用組件中的所有類
- 共同封閉原則(CCP): 組件的所有類對(duì)同一性質(zhì)的變化是共同封閉的, 同時(shí)不會(huì)影響到外部, 即對(duì)修改是封閉的,但對(duì)擴(kuò)展應(yīng)該是開放的
- 穩(wěn)定抽象原則(SAP): 組件的抽象程度應(yīng)該與其穩(wěn)定程度保持一致
基本上任何組件的設(shè)計(jì)都會(huì)或多或少的遵循以上原則, 所以說我們?cè)趯?shí)現(xiàn)自定義組件時(shí), 往往也需要考慮內(nèi)外部的抽象.
我們定義Dooring的自定義組件時(shí), 會(huì)分為以下幾個(gè)步驟:

組件的shape主要是組件對(duì)外暴露的屬性和方法, 可以實(shí)現(xiàn)用戶層面的配置, 也就是vue/react組件的props, 由于項(xiàng)目使用typescript編寫, 所以我們需要定義對(duì)應(yīng)的ts類型, 來實(shí)現(xiàn)組件的健壯性和可溯源. 最后我們會(huì)定義組件初始化的樣子(init shape), 然后才是實(shí)現(xiàn)組件, 這樣的步驟好處是我們可以明確組件的邊界, 自然貼合上面筆者說的組件設(shè)計(jì)原則.
以上流程我們會(huì)產(chǎn)生如下三個(gè)文件:
- componet 組件的實(shí)現(xiàn)代碼
- schema 組件的shape和type
- template 組件的類型映射模版
開發(fā)一個(gè)日歷組件
我們接下來就來實(shí)現(xiàn)拖拽平臺(tái)的日歷組件. 日歷組件我們直接采用zarm的Calendar組件, 將其封裝成dooring的受控組件.
日歷組件我們可以暴露如下props給到用戶自行配置:
- time 日歷顯示的時(shí)間
- range 日歷被選中的時(shí)間范圍, 主要用來做日程管理
- color 日歷默認(rèn)的文本顏色
- selectedColor 選中區(qū)域的顏色
- round 日歷的圓角
對(duì)應(yīng)的view如下:

由于組件的實(shí)現(xiàn)只需要處理傳過來的數(shù)據(jù), 這里我們看看簡單的代碼實(shí)現(xiàn):
- import React, { useState, memo, useEffect, useRef } from 'react';
- import { Calendar } from 'zarm';
- import styles from './index.less';
- import { ICalendarConfig } from './schema';
- const CalendarCp = memo((props: ICalendarConfig & { isTpl: boolean }) => {
- const { time, range, color, selectedColor, round, isTpl } = props;
- // ...
- return (
- <div className={styles.calenderWrap} style={{borderRadius: round + 'px', pointerEvents: isEditorPage ? 'none' : 'initial'}} ref={boxRef}>
- <Calendar
- multiple={!!range}
- value={value}
- min={min}
- max={new Date(max)}
- disabledDate={(date:any) => /(0|6)/.test(date.getDay())}
- onChange={(value:Date[] | undefined) => {
- setValue(value);
- }}
- />
- </div>
- });
- export default CalendarCp;
這是一個(gè)dooring組件的基本雛形, 其次我們看看 schema 部分. 這一部分主要包含了組件的shape的類型定義和基本的可編輯屬性(editable), 如下:
- export type TCalendarEditData = Array<INumberConfigType | ITextConfigType | IColorConfigType>;
- export interface ICalendarConfig {
- time: TTextDefaultType;
- range: TTextDefaultType;
- color: TTextDefaultType;
- selectedColor: TTextDefaultType;
- round: TNumberDefaultType;
- }
- export interface ICalendarSchema {
- editData: TCalendarEditData;
- config: ICalendarConfig;
- }
- const Calendar: ICalendarSchema = {
- editData: [
- {
- key: 'time',
- name: '日歷時(shí)間',
- type: 'Text',
- placeholder: '格式如2020-01或2020-11'
- },
- {
- key: 'range',
- name: '日歷選中范圍',
- type: 'Text',
- placeholder: '格式如01-12(幾號(hào)到幾號(hào))'
- },
- {
- key: 'color',
- name: '文本顏色',
- type: 'Color'
- },
- {
- key: 'selectedColor',
- name: '選中顏色',
- type: 'Color'
- },
- {
- key: 'round',
- name: '圓角',
- type: 'Number'
- },
- ],
- config: {
- time: '2020-12',
- range: '05-08',
- color: 'rgba(0,0,0,1)',
- selectedColor: 'rgba(22,40,212,1)',
- round: 0
- },
- };
- export default Calendar;
如果我們想增加屬性, 我們只需要在這個(gè)文件里添加對(duì)應(yīng)的屬性和類型即可.
template主要是定義了組件的分區(qū)和初始高度, 代碼如下:
- const template = {
- type: 'Calendar',
- h: 185,
- displayName: '日歷組件',
- };
- export default template;
有了以上三個(gè)部分, 我們要可以渲染出一個(gè)在畫布中可拖拽, 可編輯的組件了. 當(dāng)然這塊還需要FormRender的幫助, 這塊筆者后期會(huì)介紹.
以上基本就實(shí)現(xiàn)了一個(gè)可拖拽可配置的日歷組件, 我們接下來繼續(xù)看看地圖組件.
開發(fā)地圖組件
有了以上的組件開發(fā)經(jīng)驗(yàn)之后我們開發(fā)地圖組件就非常方便了. 地圖組件我們這里使用了@uiw/react-baidu-map, 也就是百度地圖的React版本, 大家也可以使用高德地圖.因?yàn)榈貓D組件react-baidu-map 需要提前閱讀對(duì)應(yīng)的文檔, 這里筆者就不一一介紹了, 我們直接來看如何實(shí)現(xiàn).同樣我們也需要定義好地圖對(duì)外暴露的props, 筆者這里簡單定義幾個(gè)可配置的屬性:
- ak 百度地圖使用憑證, 建議大家在生產(chǎn)環(huán)境替換成自己的
- location 地點(diǎn)的經(jīng)緯度, 方便快速定位
- position 地點(diǎn)的地名, 我們可以自定義設(shè)置
如下圖:

基本的代碼實(shí)現(xiàn)如下:
- import React, { memo } from 'react';
- import { Map, Marker, Label, APILoader } from '@uiw/react-baidu-map';
- import styles from './index.less';
- import { IMapConfig } from './schema';
- const Mapcomponent = memo((props: IMapConfig) => {
- const { ak, location, position } = props;
- return (
- <div className={styles.mapWrap}>
- <APILoader akay={ak}>
- <Map widget={['NavigationControl']} zoom={13}>
- <Marker animation={2} position={{ lng: position[0], lat: position[1] }} />
- <Label
- content={location}
- position={{ lng: position[0], lat: position[1] }}
- style={{color: '#000', borderColor: '#06c', padding: '3px 10px', borderRadius: '6px'}}
- />
- </Map>
- </APILoader>
- </div>
- )
- });
- export default Mapcomponent;
最后
目前H5-Dooring可視化搭建平臺(tái)還在持續(xù)更新, 主要更新如下:
- 列表組件添加搜索功能
- 圖標(biāo)組件添加鏈接交互功能, 自定義文本, 文本顏色, 文本大小配置
- 圖表組件支持自定義第三方api接口, 一鍵導(dǎo)入第三方數(shù)據(jù)源
