<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于react/vue搭建一個(gè)通用的表單管理配置平臺(tái)

          共 9510字,需瀏覽 20分鐘

           ·

          2021-08-13 09:27

          前言

          熟悉我的朋友可能會(huì)知道,我一向是不寫熱點(diǎn)的。為什么不寫呢?是因?yàn)槲也魂P(guān)注熱點(diǎn)嗎?其實(shí)也不是。有些事件我還是很關(guān)注的,也確實(shí)有不少想法和觀點(diǎn)。但我一直奉行一個(gè)原則,就是:要做有生命力的內(nèi)容。

          這篇文章是一篇應(yīng)用性極強(qiáng)的文章,我們通過一個(gè)實(shí)際的應(yīng)用場(chǎng)景,去解決某一類的問題,提供一種或者幾種解決方案,來探索技術(shù)的魅力。接下來筆者主要分析表單定制平臺(tái)的實(shí)現(xiàn)思路和技術(shù)方案,來實(shí)現(xiàn)一個(gè)類似于金數(shù)據(jù)或者問卷星一樣的表單配置平臺(tái),大家也可以基于此方案,擴(kuò)展出功能更加強(qiáng)大的可視化平臺(tái)

          正文

          為什么要做一個(gè)這樣的平臺(tái)呢?一方面是因?yàn)楣P者多年來一直服務(wù)于B端產(chǎn)品,對(duì)于動(dòng)態(tài)表單以及配置化表單有一定的項(xiàng)目積累,并且深知配置化表單的價(jià)值所在。舉一個(gè)很傳統(tǒng)的B端表單配置化的例子:傳統(tǒng)2B企業(yè)在提供saas服務(wù)時(shí),為了滿足不同企業(yè)的定制化需求,往往會(huì)給企業(yè)客戶提供定制化或者自由配置的功能,如下圖:

          對(duì)于saas系統(tǒng)而言,軟件即服務(wù),在提供基礎(chǔ)服務(wù)的同時(shí),同樣要滿足用戶個(gè)性化需求,所以傳統(tǒng)的saas軟件提供商往往會(huì)提供給客戶自由配置的空間,這種自由配置的橋梁就是通過表單,舉一個(gè)簡(jiǎn)單的例子:

          通過這種方法就可以定制不同風(fēng)格的企業(yè)產(chǎn)品,這里只是舉了個(gè)比較簡(jiǎn)單的例子,往往實(shí)際項(xiàng)目中會(huì)更加復(fù)雜,可能會(huì)有幾十個(gè)配置項(xiàng),當(dāng)然這種模式是比較傳統(tǒng)的配置化方案,也僅僅是saas軟件提供的很小的一個(gè)服務(wù)模塊。目前主流的做法是采用可視化方案,而且國內(nèi)也有非常成熟的方案,但基本的思想是一致的,只不過后者的體驗(yàn)更好,操作難度更低。


          筆者簡(jiǎn)單介紹一下saas,方便大家更容易理解其模式:

          saas(軟件即服務(wù))是一種云計(jì)算產(chǎn)品,為用戶提供對(duì)供應(yīng)商云端軟件的訪問。用戶無需在其本地設(shè)備上安裝應(yīng)用。相反,應(yīng)用駐留在遠(yuǎn)程云網(wǎng)絡(luò)中,通過 Web 或 API 進(jìn)行訪問。通過應(yīng)用,用戶可以存儲(chǔ)和分析數(shù)據(jù),并可進(jìn)行項(xiàng)目協(xié)作。

          類似的云計(jì)算產(chǎn)品也有很多,比如Paas(平臺(tái)即服務(wù)),Iaas(基礎(chǔ)架構(gòu)即服務(wù))等,感興趣的朋友可以學(xué)習(xí)了解一下。

          以上介紹更多的是為了讓大家理解筆者設(shè)計(jì)這套平臺(tái)的基本背景,我們還可以舉個(gè)更實(shí)際的例子就是金數(shù)據(jù)或者問卷星的表單配置模式,用戶可以在管理后臺(tái)定制自己的表單,并生成一個(gè)可訪問的鏈接來向目標(biāo)用戶發(fā)放問卷,填寫信息,收集信息,最后實(shí)現(xiàn)數(shù)據(jù)分析的目的。

          本文介紹的表單定制平臺(tái),也同樣支持表單管理,表單數(shù)據(jù)分析, 表單數(shù)據(jù)收集, 表單定制等功能, 筆者將采用比較熟悉的技術(shù)棧react以及第三方ui庫antd4.0來開發(fā), 后端采用node + koa來設(shè)計(jì)路由接口.

          設(shè)計(jì)思路

          實(shí)現(xiàn)效果與分析

          1. 表單定制管理列表

          管理列表主要用來查看我們配置的表單模板,分析不同表單模板收集的數(shù)據(jù),對(duì)表單模板進(jìn)行編輯刪除等操作.

          2. 表單定制頁面

          由上圖可知表單定制頁面主要用來編輯自定義表單模板,我們可以添加表單標(biāo)題,表單字段等,目前提供了幾種自定義表單控件如下:

          • 文本框

          • 多行文本框

          • 下拉框

          • 單選框

          • 復(fù)選框

          • 文件上傳控件

          基本涵蓋了我們所需要的所有表單業(yè)務(wù)場(chǎng)景.由上圖可知我們可以在任意位置插入自定義字段,同時(shí)可以編輯修改刪除表單字段.如果想象力再大一點(diǎn),我們可以基于它來實(shí)現(xiàn)不僅僅是表單問卷型應(yīng)用,還可以實(shí)現(xiàn)答題,發(fā)布內(nèi)容等場(chǎng)景.(后期可支持富文本控件)

          3. 草稿管理

          草稿箱設(shè)計(jì)的目的是方便使用者在配置表單的過程中不確定是否符合需求或者由于某種臨時(shí)性舉動(dòng)而無法繼續(xù)配置,這個(gè)時(shí)候可以將以配置好的內(nèi)容存入草稿箱,下次繼續(xù)編輯,所以筆者專門設(shè)計(jì)了草稿箱管理列表,一旦用戶存在草稿,會(huì)在管理頁面通知用戶并顯示草稿的數(shù)量.作為一個(gè)追求體驗(yàn)的技術(shù)人,這一塊的設(shè)計(jì)還是相當(dāng)有必要的.


          4. 生成前臺(tái)表單訪問鏈接

          當(dāng)我們配置好表單之后,我們點(diǎn)擊保存, 會(huì)生成一個(gè)前臺(tái)訪問地址,實(shí)時(shí)訪問表單信息,如下圖為點(diǎn)擊鏈接之后的頁面:

          我們也可以根據(jù)自己的風(fēng)格,設(shè)計(jì)自己的表單錄入頁面, 具體如何實(shí)現(xiàn)這樣的過程, 后面我會(huì)詳細(xì)介紹.

          5. 查看用戶已有數(shù)據(jù)錄入

          我們可以通過點(diǎn)擊"查看數(shù)據(jù)"來訪問收集到的表單數(shù)據(jù),并通過可視化的工具對(duì)數(shù)據(jù)做分析比較,同時(shí)我們也可以在數(shù)據(jù)列表中刪除數(shù)據(jù),來控制我們數(shù)據(jù)展示的純凈.


          6. 表單數(shù)據(jù)分析

          收集到數(shù)據(jù)只有,我們會(huì)自動(dòng)集成幾個(gè)可視化組件來分析表單數(shù)據(jù),以上是筆者列出的幾個(gè)可視化組件,基于antv G2來封裝.


          應(yīng)用場(chǎng)景

          以上主要介紹了自定義表單定制平臺(tái)的一些功能和交互效果, 我們可以利用該平臺(tái)做很多有意思的事情.因?yàn)楸韱蔚某橄笫菙?shù)據(jù),我們拿到定制化的表單json數(shù)據(jù)之后,我們可以有不同的展現(xiàn)形式,比如用戶的問卷調(diào)查網(wǎng)站平臺(tái)的投票答題頁面發(fā)布動(dòng)態(tài)等功能,如下圖配置:

          以上配置可以實(shí)現(xiàn)類似于微信的發(fā)布朋友圈的功能, 然后我們可以通過前端的手段根據(jù)用戶發(fā)表的數(shù)據(jù)渲染成一個(gè)朋友圈列表.


          如果我們?cè)俅蜷_自己的腦洞,我們可以這樣配置,配置一個(gè)這樣的表單,表單包括一個(gè)文件上傳控件和n個(gè)文本輸入控件,如下圖:

          將這樣的表單配置到H5管理模塊,我們只需要上傳三張圖,然后填寫好對(duì)應(yīng)的配文,然后利用市面上成熟的H5全屏滾動(dòng)插件,就能輕松的定制各種H5活動(dòng)頁面了。該方案已被筆者的很多子系統(tǒng)使用,效果還是非常好的。


          當(dāng)然基于該平臺(tái)甚至能直接配置小型的宣傳網(wǎng)站,還有更多想象空間,期待大家去挖掘。

          代碼實(shí)現(xiàn)

          要想開發(fā)這樣一個(gè)表單定制平臺(tái), 核心在于如何實(shí)現(xiàn)表單動(dòng)態(tài)配置的機(jī)制.這里筆者將其劃分為兩部分:基礎(chǔ)表單物料表單編輯生成器, 如下圖所示拆分圖:

          接下來我們一步步實(shí)現(xiàn)以上兩個(gè)核心模塊。

          1. 基礎(chǔ)表單物料

          基礎(chǔ)表單物料主要是為了用戶選擇自定義表單控件使用,我們常用的表單動(dòng)態(tài)渲染有map循環(huán)+條件判斷和單層map+對(duì)象法,前者如果要渲染一個(gè)動(dòng)態(tài)表單,可能實(shí)現(xiàn)如下:

          {
          list.map((item, i) => {
          return <React.Fragment key={i}>
          {
          item.type === 'input' && <Input />
          }
          {
          item.type === 'radio' && <Radio />
          }
          // ...
          </React.Fragment>
          })
          }

          但是這樣做有個(gè)明顯的缺點(diǎn)就是會(huì)產(chǎn)生很多沒必要的判斷,如果對(duì)于復(fù)雜表單,性能往往很低,所以筆者采用后者來實(shí)現(xiàn),復(fù)雜度可以降到O(n).我們先來做配置模版:

          // 基礎(chǔ)模版數(shù)據(jù)
          const tpl = [
          {
          label: '文本框',
          placeholder: '請(qǐng)輸入內(nèi)容',
          type: 'text',
          value: '',
          index: uuid(5)
          },
          {
          label: '單選框',
          type: 'radio',
          option: [{label: '男', value: 0}, {label: '女', value: 1}],
          index: uuid(5)
          },
          {
          label: '復(fù)選框',
          type: 'checkbox',
          option: [{label: '男', value: 0}, {label: '女', value: 1}],
          index: uuid(5)
          },
          {
          label: '多行文本',
          placeholder: '請(qǐng)輸入內(nèi)容',
          type: 'textarea',
          index: uuid(5)
          },
          {
          label: '選擇框',
          placeholder: '請(qǐng)選擇',
          type: 'select',
          option: [{label: '中國', value: 0}, {label: '俄羅斯', value: 1}],
          index: uuid(5)
          },
          {
          label: '文件上傳',
          type: 'upload',
          index: uuid(5)
          }
          ]

          // 模版渲染組件
          const tplMap = {
          text: {
          component: (props) => {
          const { placeholder, label } = props
          return <div className={styles.fieldOption}><span className={styles.fieldLabel}>{label}:</span><Input placeholder={placeholder} /></div>
          }
          },
          textarea: {
          component: (props) => {
          const { placeholder, label } = props
          return <div className={styles.fieldOption}><span className={styles.fieldLabel}>{label}:</span><TextArea placeholder={placeholder} /></div>
          }
          },
          radio: {
          component: (props) => {
          const { option, label } = props
          return <div className={styles.fieldOption}>
          <span className={styles.fieldLabel}>{label}:</span>
          <Radio.Group>
          {
          option && option.map((item, i) => {
          return <Radio style={radioStyle} value={item.value} key={item.label}>
          { item.label }
          </Radio>
          })
          }
          </Radio.Group>
          </div>
          }
          },
          checkbox: {
          component: (props) => {
          const { option, label } = props
          return <div className={styles.fieldOption}>
          <span className={styles.fieldLabel}>{label}:</span>
          <Checkbox.Group>
          <Row>
          {
          option && option.map(item => {
          return <Col span={16} key={item.label}>
          <Checkbox value={item.value} style={{ lineHeight: '32px' }}>
          { item.label }
          </Checkbox>
          </Col>
          })
          }
          </Row>
          </Checkbox.Group>
          </div>
          }
          },
          select: {
          component: (props) => {
          const { placeholder, option, label } = props
          return <div className={styles.fieldOption}>
          <span className={styles.fieldLabel}>{label}:</span>
          <Select placeholder={placeholder} style={{width: '100%'}}>
          {
          option && option.map(item => {
          return <Option value={item.value} key={item.label}>{item.label}</Option>
          })
          }
          </Select>
          </div>
          }
          },
          upload: {
          component: (props) => {
          return <div className={styles.fieldOption}>
          <span className={styles.fieldLabel}>{props.label}:</span>
          <Upload
          listType="picture-card"
          className="avatar-uploader"
          showUploadList={false}
          action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
          >
          <div>+</div>
          </Upload>
          </div>
          }
          }
          }

          export {
          tpl,
          tplMap
          }

          基礎(chǔ)物料在下圖所示中使用:

          當(dāng)我們要添加一個(gè)表單項(xiàng)時(shí),我們就可以在左邊預(yù)覽操作區(qū)看到添加的項(xiàng),并可以基于表單編輯生成器來編輯表單字段。


          2. 表單編輯生成器

          表單編輯生成器分為2部分, 第一部分是用來生成表單項(xiàng)的容器組件,封裝了添加,刪除,編輯操作功能,代碼如下:

          // 表單容器組件
          const BaseFormEl = (props) => {
          const {isEdit, onEdit, onDel, onAdd} = props
          const handleEdit = (v) => {
          onEdit && onEdit(v)
          }
          return <div className={styles.formControl}>
          <div className={styles.formItem}>{ props.children }</div>
          <div className={styles.actionBar}>
          <span className={styles.actionItem} onClick={onDel}><MinusCircleOutlined /></span>
          <span className={styles.actionItem} onClick={onAdd}><PlusCircleOutlined /></span>
          <span className={styles.actionItem} onClick={handleEdit}><EditOutlined /></span>
          </div>
          </div>

          }

          第二部分主要用來渲染操作區(qū)模版,基于BaseFormEl包裝不同類型的表單組件, 這里舉一個(gè)比較復(fù)雜的select來說明,其他表單控件類似:

          const formMap = {
          title: {},
          text: {},
          textarea: {},
          radio: {},
          checkbox: {},
          select: {
          component: (props) => {
          const { onDel, onAdd, onEdit, curIndex, index, type, label, placeholder, required, message, option } = props
          return <BaseFormEl
          onDel={onDel.bind(this, index)}
          onAdd={onAdd.bind(this, index)}
          onEdit={onEdit.bind(this, {index, type, placeholder, label, option, required})}
          isEdit={curIndex === index}
          >
          <Form.Item name={label} label={label} rules={[{ message, required }]}>
          <Select placeholder={placeholder}>
          {
          option && option.map(item => {
          return <Option value={item.value} key={item.label}>{item.label}</Option>
          })
          }
          </Select>
          </Form.Item>
          </BaseFormEl>
          },
          editAttrs: [
          {
          title: '字段名稱',
          key: 'label'
          },
          {
          title: '選項(xiàng)',
          key: 'option'
          },
          {
          title: '提示文本',
          key: 'placeholder'
          },
          {
          title: '是否必填',
          key: 'required'
          },
          ]
          },
          upload: {}
          }
          editAttrs主要用來渲染編輯列表,說明哪些表單項(xiàng)可以編輯,這部分代碼比較簡(jiǎn)單,這里直接用圖舉例:

          最后我們來渲染表單生成器組件:

          export default (props) => {
          const {
          formData,
          handleDelete,
          handleAdd,
          handleEdit,
          curEditRowIdx
          } = props
          return <Form name="customForm">
          {
          formData && formData.map(item => {
          let CP = formMap[item.type].component
          return <CP {...item} key={item.index}
          onDel={handleDelete}
          onAdd={handleAdd}
          onEdit={handleEdit}
          curIndex={curEditRowIdx}
          />
          })
          }
          </Form>
          }
          至此,基本功能模塊已經(jīng)開發(fā)完成,我們只需要將這些物料和組件導(dǎo)入到編輯頁面,基于業(yè)務(wù)來操作和請(qǐng)求即可。由于實(shí)現(xiàn)該案例還是有一定復(fù)雜度的,筆者沒有將所有組件都一一寫出來,希望為大家提供一個(gè)思考空間,后續(xù)筆者將會(huì)把該平臺(tái)整合到筆者的開源CMS系統(tǒng)中,供大家學(xué)習(xí)使用。有關(guān)nodejs部分的內(nèi)容,由于筆者后期會(huì)陸續(xù)整理,如果有其他疑問,可以和筆者多交流。

          最后

          如果想學(xué)習(xí)更多H5游戲webpack,nodegulp,css3,javascriptnodeJS,canvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn),歡迎在公號(hào)《趣談前端》加入我們的技術(shù)群一起學(xué)習(xí)討論,共同探索前端的邊界。

          瀏覽 88
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产极品性爱 | 爱爱91N在线观看 | 清清清草视频 | 91在线无码精品秘 入口男同 | 香蕉视频伊人 |