常在工作實踐中,可能會遇到添加水印的情況,如何在頁面中添加水印呢?如下圖的效果
實現(xiàn)思路可以利用Watermark實現(xiàn)添加水印的功能
Watermark通常是指嵌入在頁面中的半透明圖像或文字,用于標(biāo)識頁面所有權(quán)或版權(quán)信息。在若依Ruoyi-Vue中集成Watermark,可以采用以下思路:
實現(xiàn)步驟
引入Watermark庫
首先,需要在項目中引入Watermark庫。本文推薦使用watermark-dom庫,該庫使用簡單,易于擴展。可以使用以下命令安裝watermark-dom庫:
npm install watermark-dom
或者在package.json文件dependencies節(jié)點增加watermark-dom依賴。
"watermark-dom": "2.3.0"
2. 配置Watermark參數(shù)
在Vue組件中,引入并配置watermark-dom庫。例如,在App.vue組件中可以添加如下代碼
watermark_id: 'wm_div_id', //水印總體的id
watermark_prefix: 'mask_div_id', //小水印的id前綴
watermark_txt:"測試水印", //水印的內(nèi)容
watermark_x:20, //水印起始位置x軸坐標(biāo)
watermark_y:20, //水印起始位置Y軸坐標(biāo)
watermark_rows:0, //水印行數(shù)
watermark_cols:0, //水印列數(shù)
watermark_x_space:100, //水印x軸間隔
watermark_y_space:50, //水印y軸間隔
watermark_font:'微軟雅黑', //水印字體
watermark_color:'black', //水印字體顏色
watermark_fontsize:'18px', //水印字體大小
watermark_alpha:0.15, //水印透明度,要求設(shè)置在大于等于0.005
watermark_width:100, //水印寬度
watermark_height:100, //水印長度
watermark_angle:15, //水印傾斜度數(shù)
watermark_parent_width:0, //水印的總體寬度(默認(rèn)值:body的scrollWidth和clientWidth的較大值)
watermark_parent_height:0, //水印的總體高度(默認(rèn)值:body的scrollHeight和clientHeight的較大值)
watermark_parent_node:null //水印插件掛載的父元素element,不輸入則默認(rèn)掛在body上
動態(tài)生成Watermark圖像
在AppMain.vue文件引入水印模塊,示例如下:如果想要在單一頁面使用,也可以在自己想要的頁面中添加
家好, 我是徐小夕, 之前一直在分享可視化低代碼的一些實踐, 圍繞 H5-Dooring 零代碼搭建平臺也輸出了很多技術(shù)文章, 最近2.7.0 版本也順利迭代完成, 這里詳細(xì)分享一下 H5-Dooring 無代碼搭建平臺技術(shù)方案.
兩年前我設(shè)計了H5-Dooring的第一個開源版本, 之后陸陸續(xù)續(xù)迭代了兩年, github star已達(dá)到6.5k+, 也找到了很多志同道合的小伙伴, 一起研發(fā)Dooring系的搭建產(chǎn)品, 如:
image.png
從技術(shù)設(shè)計和產(chǎn)品規(guī)劃上, 這幾年也總結(jié)摸索出了一些經(jīng)驗和實踐, 接下來我就和大家一起分享一下H5-Dooring 的技術(shù)架構(gòu)設(shè)計與演進(jìn).
image.png
我們都知道任何低代碼或者零代碼搭建產(chǎn)品都非常注重底層搭建協(xié)議, 這些產(chǎn)品通常會設(shè)計一套向上兼容且可擴展的DSL結(jié)構(gòu), 來實現(xiàn)頁面元件的標(biāo)準(zhǔn)化配置, 并支持元件的向上擴展.
image.png
上面這張圖是我在設(shè)計 V6.Dooring 可視化大屏搭建平臺的編輯器架構(gòu)圖, 這里的底層搭建協(xié)議可以認(rèn)為是 搭建基礎(chǔ), 也就是我們常說的 “經(jīng)濟(jì)基礎(chǔ)決定上層建筑”.
在設(shè)計H5-Dooring 搭建平臺前, 我也參考了很多標(biāo)準(zhǔn)化軟件數(shù)據(jù)協(xié)議, 給我啟發(fā)最大的就是 ODATA, 它是微軟于2007年發(fā)起的開放協(xié)議, 主要由以下幾部分組成:
image.png
image.png
image.png
image.png
為了讓可視化搭建平臺的組件數(shù)據(jù)標(biāo)準(zhǔn)化且可擴展, 這里我分享一下H5-Dooring的Schema設(shè)計.
image.png
Schema 分兩部分:
editData 是 組件屬性可編輯項的數(shù)組, 每一項里面包含了如下字段:
key和name 都可以按照組件屬性的語義來定, 這里值得一提的是 type. 不同屬性的值類型不同, 所以我們編輯項的 type 也不同, 所有的類型如下:
更詳細(xì)的介紹可以訪問 dooring 開發(fā)文檔
config 本質(zhì)上是一個對象, 也就是組件所能暴露出來的屬性集合, 和 editData 數(shù)組每一項的key 一致, 如下:
{
cpName: 'Header',
logoText: '',
fontSize: 20,
color: 'rgba(47,84,235,1)',
height: 60,
fixTop: false,
menuList: [
{
id: '1',
title: '首頁',
link: '/'
},
{
id: '2',
title: '產(chǎn)品介紹',
link: '/'
},
]
}
我們通過以上的設(shè)計規(guī)范, 就可以輕松制作一個可實時編輯的低代碼組件:
image.png
可以在Dooring官方文檔體驗: 低代碼組件案例
最開始設(shè)計H5-Dooring的時候為了最大限度的降低用戶的搭建成本, 我采用了智能網(wǎng)格布局的方式來搭建頁面, 用戶只需要在二維空間像搭積木一樣選擇適合的組件就可以快速的制作頁面:
這樣雖然可以降低用戶的搭建難度, 并能滿足一部分受眾的搭建需求, 比如說簡單的官網(wǎng), 活動頁面制作,下面是一個搭建的比較有代表性的例子:
但是對于平臺方, 為了滿足更多場景的頁面深度制作, 就必須提供不同場景不同行業(yè)的組件物料, 這將對研發(fā)帶來巨大的壓力(雖然也一直在添加新組件).
另一方面, 目前上很多H5活動制作平臺基本上都采用的自由布局的模式搭建, 好處就是可以最大限度的還原設(shè)計稿, 滿足更靈活的搭建需求, 缺點就是使用成本比網(wǎng)格布局的模式要高, 還會涉及圖層的概念.
當(dāng)然綜合評估下來, 確實很有必要給一部分用戶提供自由布局的模式, 所以在技術(shù)層我設(shè)計同時兼容網(wǎng)格布局和自由布局的搭建方案. 當(dāng)用戶在搭建時, 可以輕松選擇自己適合的搭建模式:
image.png
同時為了滿足自由布局下組件的層級管理, 我又設(shè)計了圖層管理面板和圖層操作, 來快速的管理頁面元素, 當(dāng)然圖層管理面板 對網(wǎng)格布局 也同樣有一定積極作用, 比如快捷的操作組件.
在前面提到了可視化搭建平臺的統(tǒng)一搭建協(xié)議和搭建模式, 在這兩個核心要素完成之后, 我們就很容易的去設(shè)計我們的插件系統(tǒng).
image.png
從插件系統(tǒng)的本質(zhì)來看, 核心價值是對頁面操作的整個周期里為頁面賦能, 而頁面的本質(zhì)是數(shù)據(jù)(也就是DSL集).
image.png
所以只要有標(biāo)準(zhǔn)的數(shù)據(jù)規(guī)范, 我們自定義的插件就可以很輕松的來對頁面進(jìn)行賦能, 類似于各種技術(shù)里面的中間件. 下面是一個例子:
{
"pageConfig": {
"allowOverlap": "freedom",
"isLogin": false,
"bgColor": "rgba(16,20,29,1)",
"bgSize": "100%",
"title": "H5-Dooring官網(wǎng)"
},
"tpl": [
{
"id": "276059",
"item": {
"category": "base",
"config": {
"cpName": "XButton",
"id": "",
"bgColor": "rgba(22,40,212,1)",
"width": 190,
"marginTop": 0,
"round": 16,
"text": "按鈕",
"fontSize": 15,
"color": "rgba(255,255,255,1)",
"animation": "none",
"animationTurn": 1,
"delay": 0,
"interaction": {
"type": "link",
"title": "",
"params": "",
"content": "",
"height": 300,
"width": 300,
"okText": "",
"cancelText": "",
"onOk": "",
"btnColor": "rgba(20,54,226,100)"
}
},
"h": 23,
"type": "XButton"
},
"point": {
"w": 24,
"h": 23,
"x": 0,
"y": 0,
"i": "276059",
"moved": false,
"static": false,
"isBounded": true
},
"status": "inToCanvas"
},
{
"id": "260487",
"item": {
"category": "base",
"config": {
"cpName": "LongText",
"id": "",
"text": "我是長文本有一段故事,dooring可視化編輯器無限可能,趕快來體驗吧,騷年們,奧利給~",
"color": "rgba(60,60,60,1)",
"fontSize": 14,
"indent": 0,
"lineHeight": 1.8,
"textAlign": "left",
"bgColor": "rgba(255,255,255,0)",
"padding": 0,
"radius": 0
},
"h": 36,
"type": "LongText"
},
"point": {
"w": 24,
"h": 36,
"x": 0,
"y": 23,
"i": "260487",
"moved": false,
"static": false,
"isBounded": true
},
"status": "inToCanvas"
}
]
}
上面是H5-Dooring生成的一個頁面DSL結(jié)構(gòu), 如果我們要對頁面元素進(jìn)行統(tǒng)計分析, 或者實現(xiàn)出碼, 國際化, PSD解析轉(zhuǎn)化等功能, 只需要對數(shù)據(jù)結(jié)構(gòu)進(jìn)行分析和處理即可.
image.png
所以說在H5-Dooring平臺實現(xiàn)自定義的插件還是非常容易的, 也是低代碼或者無代碼需要重點規(guī)劃的一個環(huán)節(jié).
H5-Dooring平臺的組件編輯器主要是對組件屬性進(jìn)行編輯,比如:
當(dāng)然還有全局的數(shù)據(jù)源配置. 如下:
image.png
同時由于我們的組件數(shù)據(jù)協(xié)議高度統(tǒng)一, 所以如果想擴展屬性配置, 也非常容易, 我們只需要按照數(shù)據(jù)協(xié)議添加屬性即可:
image.png
同理, 「v6.dooring」 也采用相似的架構(gòu), 所以我們可以輕松擴展組件的屬性:
image.png
有關(guān)可視化大屏搭建平臺的技術(shù)實踐可以參考我的另一篇文章 從零設(shè)計可視化大屏搭建引擎
image.png
由于Dooring的技術(shù)棧采用React, 并實現(xiàn)了標(biāo)準(zhǔn)的數(shù)據(jù)協(xié)議層, 所以我們可以利用類似 Taro 等跨平臺框架實現(xiàn)多端搭建, 對于我們常用的媒介如移動端, Pad和PC端, 目前編輯器也提供了快捷的切換模式:
image.png
所以我們可以輕松的實現(xiàn)不同端的搭建, 實現(xiàn)原理本質(zhì)上是通過切換畫布大小, 并同比例更新元素的計量衡.
圖層管理模塊也是在Dooring支持了自由布局之后才上線的功能. 因為我們頁面中組件的數(shù)據(jù)結(jié)構(gòu)中包含統(tǒng)一的物理信息:
image.png
所以我們只需要分析頁面的組件集合, 就可以輕松的渲染出頁面中的元素圖層信息:
image.png
有了圖層的概念我們其實可以做很多有用的事情, 比如:
后面 Dooring 也會基于圖層能力迭代更多提高用戶搭建銷效率的功能.
在Dooring 的迭代中花了大部分精力在優(yōu)化用戶搭建體驗和協(xié)議標(biāo)準(zhǔn)化上, 對于組件物料的豐富上, 我也做了一些設(shè)計, 最近也發(fā)布了一套低代碼組件庫的原型:
image.png
我們可以輕松的像寫 React 組件一樣來實現(xiàn)低代碼組件, 并支持線上實時編輯, 一個基本的例子如下:
import styles from './index.less';
import React, { memo, useState } from 'react';
import { MenuOutlined } from '@ant-design/icons';
import { IHeaderConfig } from './schema';
const Header=memo((props: IHeaderConfig)=> {
const {
cpName,
bgColor,
logo,
logoText,
fontSize,
color,
showMenuBtn,
menuColor,
height,
}=props;
const [showMenu, setShowMenu]=useState(false);
const toggleMenu=()=> {
setShowMenu(!showMenu);
};
return (
<header
className={styles.header}
style={{ backgroundColor: bgColor, height: height + 'px' }}
>
<div className={styles.logo}>
<img src={logo && logo[0] && logo[0].url} alt={logoText} />
</div>
<div className={styles.title} style={{ fontSize, color }}>
{logoText}
</div>
{showMenuBtn && (
<>
<div
className={styles.menuIconWrap}
onClick={toggleMenu}
style={{ color: menuColor, borderColor: menuColor }}
>
<MenuOutlined />
</div>
</>
)}
</header>
);
});
export default Header;
通過這種標(biāo)準(zhǔn)化的方式, 我們可以給 Dooring 平臺提供更為豐富的組件物料.
除了基礎(chǔ)物料組件之外, 為了從更大粒度提高用戶搭建的效率, 我提供了模版功能, 我們可以把重復(fù)的區(qū)塊和可復(fù)用的頁面保存為模版:
image.png
我們可以在編輯器頁面輕松將頁面保存為模版, 并自動生成海報封面:
image.png
基于網(wǎng)頁生成封面的方式也很簡單, 我這里采用的是 dom-to-image 這個庫, 當(dāng)然搭建也可以使用html2canvas.
表單編輯器的實現(xiàn)思路我之前也寫過一些分享, 這里和大家再介紹一下核心的一些思路.
「1. 靜態(tài)化配置列表」
靜態(tài)化配置列表是最傳統(tǒng)的表單配置方式之一,基本思路就是利用母表來生成配置項,進(jìn)而實現(xiàn)表單配置。類似于以下方式:
早期的網(wǎng)站配置就是類似于這種呢方案實現(xiàn)的,比如說我們要定制網(wǎng)站的主色,網(wǎng)站某些組件是否可見,是一種比較簡單的方式。但是缺點是每增加一個配置屬性,都要開發(fā)人員重新編寫一個字段配置代碼,這種方式在表單開發(fā)中非常不靈活,而且對代碼層有強依賴性,所以只適合做小型配置系統(tǒng)。比如個人網(wǎng)站,簡單的自定義表單。
「2. 基于json schema的動態(tài)表單配置」
基于「json schema」的動態(tài)表單配置有兩種實現(xiàn)方案, 一種就是支持在線修改json文件從而實現(xiàn)定制化,另一種就是完全無代碼操作,但是前提都需要提供一套通用的表單模版。類似于如下案例:
此種方案可以實現(xiàn)基本的表單自治。也是本文主要實現(xiàn)的方案。至于在線編寫json文件的方案。筆者之前也也過成熟的方案,具體可以參考:基于jsoneditor二次封裝一個可實時預(yù)覽的json編輯器組件(react版)
「3. 支持在線coding的混合式表單設(shè)計」 支持「在線編程」的混合式表單設(shè)計方案是終極方案,也是目前流行的無代碼化平臺的思想之一。一方面它提供了基于「json schema」的動態(tài)表單配置, 對于一些強定制的,需要在線設(shè)計組件方案的模式,采用在線編程,實時打包成動態(tài)組件的方式,最后根據(jù)平臺的組件約定來實現(xiàn)組件庫的方式。如下圖所示:
在線代碼編輯可以使用「react-codemirror2」或者 「react-monaco-editor」插件來實現(xiàn)。至于在線打包,我們用「nodejs」完全可以實現(xiàn),筆者在做「Dooring」項目的在線下載代碼時就用到了該方案,感興趣的可以了解一下。
可視化領(lǐng)域一方面強調(diào)的是圖形(可視化)的設(shè)計,一方面是動態(tài)表單。比如說我們想傻瓜式的改變一張圖的數(shù)據(jù),屬性,交互等,我們需要通過表單這一橋梁來實現(xiàn):
所以我們需要設(shè)計一款適合公司產(chǎn)品的“表單引擎”,來動態(tài)根據(jù)圖形組件的類型渲染不同表單配置。這塊思想也是表單設(shè)計器要解決的問題之一。在下面的文章中我們會詳細(xì)介紹實現(xiàn)過程。
在實現(xiàn)表單設(shè)計器之前,我們先來整理一下思路和需求。在筆者的最初草圖中,它長這樣:
從草圖中我們可以提取到如下任務(wù)信息:
我們這里總結(jié)了幾個常用的表單組件如下:
以上這些基本滿足我們的日常開發(fā)需求,其次我們還可以開發(fā)數(shù)據(jù)源表單組件,列表組件,比如dooring實現(xiàn)的那樣:
類似的還有顏色面板這些,我們可以更具業(yè)務(wù)需求自行定制。
在完成表單組件庫之后,我們就需要根據(jù)配置項動態(tài)渲染了。也有兩種實現(xiàn)思路,一種就是類似于多條件判斷,如下:
{
item.type==='Number' &&
<Form.Item label={item.name} name={item.key}>
<InputNumber min={1} max={item.range && item.range[1]} step={item.step} />
</Form.Item>
}
{
item.type==='Text' &&
<Form.Item label={item.name} name={item.key}>
<Input />
</Form.Item>
}
{
item.type==='TextArea' &&
<Form.Item label={item.name} name={item.key}>
<TextArea rows={4} />
</Form.Item>
}
這樣做雖然可行,也有很多成熟系統(tǒng)采用該方案,但是一旦表單變多,比如一個頁面有幾十個甚至上百個表單項,那么我們將渲染「m」 *** n**次(m為表單組件類型數(shù),n為配置項個數(shù))。另一種方式筆者看來是比較優(yōu)雅的,可以將復(fù)雜度降低到O(n),也就是筆者常用的對象法。思路大至如下:「將表單組件的類型作為對象的屬性,屬性值為對應(yīng)的表單組件,這樣遍歷的時候只需要對應(yīng)上對象的具體類型即可。」 代碼如下:
// 維護(hù)表單控件, 提高form渲染性能
const BaseForm={
"Text": (props)=> {
const { label, placeholder, onChange }=props
return <Cell title={label}>
<Input type="text" placeholder={placeholder} onChange={onChange} />
</Cell>
},
"Number": (props)=> {
const { label, placeholder, onChange }=props
return <Cell title={label}>
<Input type="number" placeholder={placeholder} onChange={onChange} />
</Cell>
}
}
// 動態(tài)渲染表單
{
formData.map((item, i)=> {
let FormItem=BaseForm[item.type]
return <div className={styles.formItem} key={i}>
<FormItem {...item} />
</div>
})
}
是不是很優(yōu)雅呢?后期我們只需要在「BaseForm」里維護(hù)表單組件即可,而且還可以基于「BaseForm」對表單進(jìn)行包裝,實現(xiàn)動態(tài)刪除,編輯等功能。如下:
image.png
包裝后的代碼如下:
<div>
<div className={styles.disClick}><FormItem {...item} /></div>
<div className={styles.operationWrap}>
<span onClick={handleEditItem}><EditOutlined /></span>
<span onClick={handleDelItem}><MinusCircleOutlined /></span>
</div>
</div>
接下來我們看看表單的全局屬性,通過實際分析我們可以知道表單有如下外觀:
所以他們因該成為表單設(shè)計的通用屬性,如下圖所示:
image.png
以上的表單通過「H5-Dooring」設(shè)計而來。當(dāng)然我們可以利用它設(shè)計更加自定的表單頁面。
最后一個比較使用的需求就是api定制,一般公司可能需要將用戶的錄入數(shù)據(jù)收集到自己的平臺,那么這個時候我們提供一個api表單提交接口積極很有必要了,上面筆者也展示過,實現(xiàn)很簡單,就是配置里多一個api的文本框即可。
在H5編輯器「Dooring」的實現(xiàn)中,我們可以做抽象,每一個頁面組件可以看成特定的表單組件,如下圖:
我們可以利用「dooring」的能力對表單平臺進(jìn)行拖拽,樣式設(shè)計,數(shù)據(jù)錄入等等操作,感興趣的朋友可以基于「Dooring」設(shè)計思路改造成自己的表單設(shè)計平臺。
對于數(shù)據(jù)收集能力, 可以參考我的另一篇文章:
前端如何一鍵生成多維度數(shù)據(jù)可視化分析報表
之前 H5-Dooring 是采用 socket 來實現(xiàn)雙向通信的, 不同的用戶如何想?yún)f(xié)作搭建, 可以通過 共享的json文件 或者 socket 來實現(xiàn). 不過最新市面上也出了非常不錯的協(xié)作方案, 大家也可以參考一下, 這塊的功能設(shè)計目前我們正在確定方案.
目前 Dooring 支持2種出碼方式:
image.png
以上就是我們需要做的在線實時打包下載代碼的工作流,由于nodejs是單線程的,為了不阻塞進(jìn)程我們可以采用父子進(jìn)程通信的方式和異步模型來處理復(fù)雜耗時任務(wù),為了通知用戶任務(wù)的完成狀況, 我們可以用socket做雙向通信。在當(dāng)前的場景下就是代碼編譯壓縮完成之后,通知給瀏覽器,以便瀏覽器顯示下載狀態(tài)彈窗。一共有三種狀態(tài):「進(jìn)行中」,「已完成」,「失敗」。對應(yīng)如下圖所示界面:
至于為什么沒有出現(xiàn)下載失敗的狀態(tài),不要問我,問就是沒有失敗過(完了,找虐了)。
以上就是「H5-Dooring」實時編譯下載的工作流設(shè)計,至于線上更多的實際需求,我們也可以參考以上設(shè)計去實現(xiàn),接下來筆者來具體介紹實現(xiàn)過程。
我們要想實現(xiàn)一個自動化工作流, 要考慮的一個關(guān)鍵問題就是任務(wù)的執(zhí)行時機以及以何種方式執(zhí)行. 因為用戶下載代碼之前需要等H5頁面打包編譯壓縮完成之后才能下載, 而這個過程需要一定的時間(8-30s), 所以我們可以認(rèn)定它為一個耗時任務(wù).
當(dāng)我們使用「nodejs」作為后臺服務(wù)器時, 由于「nodejs」本身是單線程的,所以當(dāng)用戶請求傳入「nodejs」時, 「nodejs」不得不等待這個"耗時任務(wù)"完成才能進(jìn)行其他請求的處理, 這樣將會導(dǎo)致頁面其他請求需要等待該任務(wù)執(zhí)行結(jié)束才能繼續(xù)進(jìn)行, 所以為了更好的用戶體驗和流暢的響應(yīng),我們不得不考慮多進(jìn)程處理. 好在nodejs設(shè)計支持子進(jìn)程, 我們可以把耗時任務(wù)放入子進(jìn)程中來處理,當(dāng)子進(jìn)程處理完成之后再通知主進(jìn)程. 整個流程如下圖所示:
「nodejs」有3種創(chuàng)建子進(jìn)程的方式,這里筆者簡單介紹一下「fork」的方式。使用方式如下:
// child.js
function computedTotal(arr, cb) {
// 耗時計算任務(wù)
}
// 與主進(jìn)程通信
// 監(jiān)聽主進(jìn)程信號
process.on('message', (msg)=> {
computedTotal(bigDataArr, (flag)=> {
// 向主進(jìn)程發(fā)送完成信號
process.send(flag);
})
});
// main.js
const { fork }=require('child_process');
app.use(async (ctx, next)=> {
if(ctx.url==='/fetch') {
const data=ctx.request.body;
// 通知子進(jìn)程開始執(zhí)行任務(wù),并傳入數(shù)據(jù)
const res=await createPromisefork('./child.js', data)
}
// 創(chuàng)建異步線程
function createPromisefork(childUrl, data) {
// 加載子進(jìn)程
const res=fork(childUrl)
// 通知子進(jìn)程開始work
data && res.send(data)
return new Promise(reslove=> {
res.on('message', f=> {
reslove(f)
})
})
}
await next()
})
在H5-Dooring線上打包的工作流中,我們會用到「child_process」的「exec」方法,來解析并執(zhí)行命令行指令。至于父子進(jìn)程的更多應(yīng)用,大家可以自行探索。
在上面介紹的「dooring」工作流中,我們知道為了實現(xiàn)實時打包,我們需要一個「H5 Template」項目,作為打包的母版,當(dāng)用戶點擊下載時,會將頁面的「json schema」數(shù)據(jù)傳給「node服務(wù)器」, 「node服務(wù)器」再將「json schema」進(jìn)行「數(shù)據(jù)清洗」最后生成「template.json」文件并移動到「H5 Template」母版中,此時母版拿到數(shù)據(jù)源并進(jìn)行打包編譯,最后生成可執(zhí)行文件。
以上的過程很關(guān)鍵, 這里筆者畫個大致的流程圖:
為了實現(xiàn)以上過程,我們需要兩個關(guān)鍵環(huán)節(jié):
第一個環(huán)節(jié)很好實現(xiàn),我們只需要用「nodejs」的「fs」模塊生成文件到指定目錄即可,這里筆者重點介紹第二個環(huán)節(jié)的實現(xiàn)。
當(dāng)我們將json數(shù)據(jù)生成到「H5 Template」中之后,就可以進(jìn)行打包了,但是這個過程需要自動化的去處理,不能像我們之前啟動項目一樣,手動執(zhí)行「npm start」或者「yarn start」。我們需要程序自動幫我們執(zhí)行這個命令行指令,筆者在查「nodejs API」突然發(fā)現(xiàn)了「child_process」的「exec」方法,可以用來解析指令,這個剛好能實現(xiàn)我們的需求,所以我們開始實現(xiàn)它。代碼如下:
import { exec } from 'child_process'
const outWorkDir=resolve(__dirname, '../h5_landing')
const fid=uuid(8, 16)
const cmdStr=`cd ${outWorkDir} && yarn build ${fid}`
// ...exec相關(guān)代碼
const filePath=resolve(__dirname, '../h5_landing/src/assets/config.json')
const res=WF(filePath, data)
exec(cmdStr, function(err,stdout,stderr){
if(err) {
// 錯誤處理
} else {
// 成功處理
}
})
以上代碼我們不難理解,我們只需要定義好打包的指令字符串(方式和命令行操作幾乎一致),然后傳入給「exec」的第一個參數(shù),他就會幫我們解析字符串并執(zhí)行對應(yīng)的命令行指令。在執(zhí)行完成之后,我們可以根據(jù)回調(diào)函數(shù)(第二個參數(shù))里的參數(shù)值來判斷執(zhí)行結(jié)果。整個過程是異步的,所以我們不用擔(dān)心阻塞問題,為了實時反饋進(jìn)度,我們可以用「socket」來將進(jìn)度信息推送到瀏覽器端。
在上面介紹的 「exec實現(xiàn)解析并執(zhí)行命令行指令」 中還有一些細(xì)節(jié)可以優(yōu)化,比如代碼執(zhí)行進(jìn)程的反饋,執(zhí)行狀態(tài)的反饋。因為我們用的是異步編程,所以請求不會一直等待,如果不采取任何優(yōu)化措施,用戶是不可能知道何時代碼打包編譯完成, 也不知道代碼是否編譯失敗,所以這個時候會采取幾種常用的放案:
很明顯使用「websocket雙向通信」會更適合本項目。這里我們直接使用社區(qū)比較火的「http://socket.io」.由于官網(wǎng)上有很多使用介紹,這里筆者就不一一說明了。我們直接看業(yè)務(wù)里的代碼使用:
// node端
exec(cmdStr, function(err,stdout,stderr){
if(err) {
console.log('api error:'+stderr);
io.emit('htmlFail', { result: 'error', message: stderr })
} else {
io.emit('htmlSuccess', { result: dest, message: stderr })
}
})
// 瀏覽器端
const socket=io(serverUrl);
// ...省略其他業(yè)務(wù)代碼
useEffect(()=> {
socket.on('connect', function(){
console.log('connect')
});
socket.on('htmlFail', function(data){
// ...
});
socket.on('disconnect', function(e){
console.log('disconnect', e)
});
}, [])
這樣我們就能實現(xiàn)服務(wù)器任務(wù)流的狀態(tài)實時反饋給瀏覽器端了。
實現(xiàn)前端下載功能其實也很簡單,因為用戶配置的H5項目包含了各種資源,比如「css,js,html,image」,所以為了提高下載性能和便捷性我們需要把整個網(wǎng)站打包,生成一個「zip」文件供用戶下載。原理就是使用「jszip」將目錄壓縮,然后返回壓縮后的路徑給到前端,前端采用a標(biāo)簽進(jìn)行下載。至于如何實現(xiàn)目錄遍歷壓縮和遍歷讀取目錄, 這里筆者就不說了,感興趣的可以參考筆者其他的nodejs 的文章。
跌跌撞撞的迭代了2年多, 目前已經(jīng)基本可以使用Dooring搭建大部分的場景應(yīng)用了, 比如:
等等, 后期會擴展更多的場景, 持續(xù)迭代, 滿足更多用戶的深度定制需求.
目前Dooring 已經(jīng)完成了幾個關(guān)鍵性的能力:
后期會從搭建效率和資源生態(tài) 這兩個維度繼續(xù)迭代, 比如:
血來潮有想改進(jìn)下工作效率,在代碼開發(fā)過程中同事經(jīng)常問這個錯誤是哪里觸發(fā)的,這不是我寫的我也只能一個一個調(diào)試定位到是哪個存儲過程,于是想到了我之前的存儲過程顯示源碼的玩意,又進(jìn)行了改進(jìn),直接搜索存儲過程內(nèi)容。
切換到要搜索的數(shù)據(jù)庫
SELECT top 30 OBJECT_NAME(sm.object_id) AS ProcedureName,
OBJECT_DEFINITION(sm.object_id) AS ProcedureDefinition,*
FROM sys.sql_modules AS sm
WHERE OBJECT_DEFINITION(sm.object_id) LIKE '%搜索的關(guān)鍵詞%'
不過搜索后是一行顯示,顯然不行的,還需要開發(fā)一個工具,讓他能完整顯示每行,方便執(zhí)行測試
于是整了個web版本
image.png
但是還是感覺不得勁,想開發(fā)zure data studio的擴展。
,但是開發(fā)思路是不知道怎么在存儲過程的右鍵菜單插入,于是在azure data studio的擴展中進(jìn)行搜索 search
原來有人做了類似的工具,支持緩存搜索和 熱連接接搜索
不過操作交互不太舒服,是快捷鍵彈出對話框,選中了一個結(jié)果其他結(jié)果就看不到了,還不如我網(wǎng)頁版的方便。
另外此插件的功能介紹:
緩存模式建議在打算改代碼的模式不要使用,否則可能導(dǎo)致生產(chǎn)事故。
快捷鍵ctrl+t是緩存模式
ctrl+shift+t 無緩存模式
https://github.com/MikhailProfile/SearchEverywhere/issues
我的web版本實現(xiàn)邏輯也是很簡單,因為用的 web core api,不打算套view,直接字符串拼接
private static string FormatMultiResultAsHtml2(JArray arrs)
{
string html="";
for (int a=0; a < arrs.Count; a++)
{
string title=arrs[a]["title"] +"";
string contents=arrs[a]["content"]+"";
int lineNum=1;
string linetag="結(jié)果" + (a + 1);
html +=@"
<div class=""result""
<h3> " +title+"|"+ HttpUtility.HtmlEncode(linetag) + @"</h3>
<p onclick=""toggleDetails(this)"">展開/收縮</p>
<div class=""result-details"">
<code> ";
html +=contents.Replace("\n", "<br>");
html +=@"</code>
</div>
</div>";
}
string html1=@"
<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<style>
.results-container {
display: flex;
flex-wrap: wrap;
}
.result {
flex: 0 0 98%;
margin: 2px;
padding: 10px;
border: 1px solid #ccc;
cursor: pointer;
}
.result-details {
display: none;
}
</style>
</head>
<body>
<div class=""results-container"">
" + html + @"
</div>
<script>
function toggleDetails(element) {
element=element.parentNode;
var details=element.querySelector('.result-details');
if (details.style.display==='none' || details.style.display==='') {
details.style.display='block';
} else {
details.style.display='none';
}
}
</script>
</body>
</html>
";
return html1;
}
然后
html=FormatMultiResultAsHtml2(ARR);
html=html.Replace(content, "<font color='red'><b>" + content + "</b></font>");
}
return Content(html, "text/html", Encoding.UTF8);
切換數(shù)據(jù)庫問題,這個我嘗試過似乎不能指定數(shù)據(jù)庫只能直接切換數(shù)據(jù)庫源了。
傳遞之前的連接字符串,然后進(jìn)行修改設(shè)置回去。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。