隨著大資料技術的發展和資訊保安要求的提高,資料規模的不斷擴大給資料運維帶來了嚴峻的挑戰。 面對海量資料帶來的沉重管理壓力,運維人員面臨效率瓶頸,人力成本不斷上公升,單純依靠擴容運維團隊解決問題已不再可行。
由此可見,智慧型化、高效化、便捷化是運維發展的必然方向。 袋鼠雲的檢測報告功能就是為了實現這一目標而設計的,並提供優化的解決方案。
什麼是檢驗報告?
檢查報告是指對某個系統或裝置進行全面檢查,並將檢查結果和建議整理成報告的過程。 檢查報告通常用於評估系統或裝置的健康狀況和效能,並為發現問題、優化系統、提高效率和降低故障率提供參考。
本文將詳細闡述檢驗報告的功能和實施方案,以期為有此類需求的使用者提供實用參考。
自定義布局。
可以拖放報表中的面板以更改布局。
在拖拽過程中,拖拽區域受到限制,只允許拖拽同乙個父級,不允許跨目錄移動,不允許改變目錄的級別,比如將一級目錄移動到另乙個一級目錄,成為二級目錄。
目錄可以通過收縮來擴充套件。
可以通過收縮來擴充套件內容列表,收縮時隱藏所有子面板,展開時顯示所有子面板。
移動目錄時,子面板會跟隨移動。
更改目錄後,更新右側的目錄面板。
生成目錄號。
右側的目錄樹。
生成目錄號。
支援錨點滾動。
支援擴充套件和收縮。
鏈結到左側的報告。
資料面板。 根據日期範圍獲取指標資料。
以圖表的形式顯示指標資訊。
檢視詳細資訊並刪除。
每個面板的請求設計都支援重新整理請求。
面板匯入。 計算目錄中選擇的面板數。
匯入新面板時,無法銷毀現有布局,新面板只能跟隨舊面板。
匯入已有面板時,需要對資料進行對比,如果資料發生變化,則需要再次獲取最新資料。
救。 在儲存之前,與布局相關的所有操作都是臨時的,包括匯入面板。 只有點選儲存後,才會將當前資料提交到後端儲存。
支援 PDF 和 Word 匯出。
那麼,這套檢驗報告功能是如何實現的呢? 下面將介紹資料結構設計、元件設計、目錄、面板等。
讓我們看一下使用扁平結構下的圖表:
在扁平化結構中,只需要找到下一行面板即可確定子級,多級目錄也是如此,但一級目錄需要額外的處理。
扁平化結構實現起來相對簡單,但為了滿足特定需求,它限制了目錄的拖拽。 限制目錄需要面板的清晰層次結構,很明顯,樹結構可以非常恰當地清晰地描述資料層次結構。
它不同於傳統的元件程式設計。 該實現將呈現和資料處理分為兩部分:
React 元件:主要負責頁面渲染。
class :負責資料的處理。
dashboardmodel
class dashboardmodelpanelmodel
class panelmodel每個儀錶盤元件對應乙個儀錶盤模型,每個面板元件對應乙個面板模型。
React 組合基於類例項中的資料進行渲染。 例項一旦投入生產,就不會輕易被銷毀或引用位址被更改,從而防止依賴例項資料進行渲染的 react 元件觸發更新渲染。
我們需要一種方法,讓我們在例項中的資料發生變化後手動觸發元件的更新渲染。
元件呈現控制項。
由於我們之前使用過 hooks 元件,因此與類元件不同,該元件可以通過呼叫 forceupdate 方法觸發。
在 React18 中,有乙個名為 usesyncexternalstore 的新功能,它允許我們訂閱外部資料並在資料發生變化時觸發元件的渲染。
實際上,usesyncexternalstore 通過在內部維護乙個狀態來觸發元件渲染,當狀態值發生變化時,會導致外部元件渲染。
考慮到這一點,我們簡單地實現了乙個觸發元件渲染的 useforceupdate 方法。
export function useforceupdate()雖然實現了 useforceupdate,但在實際使用過程中,需要在元件被銷毀時刪除該事件。 UseSyncExternalStore 已在內部實現,您可以直接使用它。
usesyncexternalstore(dashboard?.subscribe ??=>要更新面板布局資料,可以使用面板對映準確定位對應的面板,並進一步呼叫其 UpdateGridPOS 方法進行布局更新操作。
至此,我們才剛剛完成了面板本身資料的更新,還需要執行儀錶盤的 sortpanelssbygridpos 方法對所有面板進行排序。
class dashboardmodel else }// ..面板拖動範圍。
當前拖拽範圍為整個儀錶盤,可隨意拖拽,綠色為儀錶盤的可拖拽區域,灰色為面板。 如下:
如果需要限制,需要將其更改為結構,如下圖所示:
在原來的基礎上,將目錄劃分為多個單元,綠色為整體可移動區域,黃色為一級目錄塊,可在綠色區域拖拽,拖拽時可拖動整個黃色塊,紫色為二級目錄塊,可在當前黃色區域內拖拽, 並且不能與當前黃色塊分離,灰色面板只能拖拽到當前目錄下。
需要在原有資料結構的基礎上進行轉換:
class panelmodel面板的進口設計。
後端返回的資料是一棵帶有**級別的樹,我們得到後,維護成三個map:modulemap、dashboardmap和panelmap。
import from 'react';export interface module export interface dashboard export interface panel type expr = ;export const dashboardcontext = createcontext();當我們渲染乙個模組時,我們會遍歷 modulemap 並通過模組內的儀表板資訊找到輔助目錄。
選擇輔助目錄後,通過輔助目錄儀表板的面板找到相關面板,並將其渲染到右側區域。
對於這 3 個地圖的操作,維護在 usehandledata,匯出:
面板選擇回填。進入面板管理時,我們需要回填選中的面板,可以通過gets**emodel獲取當前檢測報告的資訊,並將對應的選定資訊儲存在selectpanel中。
現在我們只需要更改選擇面板中的值即可選擇相應的面板。
面板檢查重置。
直接遍歷儀表板地圖並重置每個 SelectPanels。
dashboardmap.foreach((dashboard) => )面板插入。
選擇面板後,插入所選面板時有幾種情況:
這次還選擇了檢測報告的原始面板,插入時會對比資料,如果資料發生變化,需要根據最新的資料來源資訊進行請求和渲染。
如果這次沒有選擇檢查報告的原始面板,則在插入時需要刪除未選擇的面板。
插入新選定的面板時,該面板將插入到相應目錄的末尾。
新增新面板需要,類似於目錄收縮,但需要:
目錄收縮僅適用於乙個目錄,而插入僅適用於整個目錄。
目錄收縮是直接從子節點向上冒泡,而插入是從根節點開始向下,插入完成後,根據最新的目錄資料更新布局。
class dashboardmodel ** 將當前資料與傳入資料進行對比,以傳入資料為標準,按當前順序進行修改 * param panels * updatepanels(panels: paneldata)return false; }panelmap.foreach((panel) => )addpanel(paneldata: any) )resetdashboardgridpos(panels: panelmodel = this.panels) else const gridpos = ; panel.updategridpos();sumh += h; }return sumh; }class panelmodel this.restoremodel(panel); if (this.dashboard) this.needrequest &&this.forceupdate();小組請求。
needrequest 控制面板是否需要發出請求,如果為 true,則下次面板渲染時會請求請求,並且請求的處理也放在 PanelModel 中。
import from '../../components/useparams';class panelmodel as params; }request = ()=> ;fetchdata = async (params: params) => ;fetch = async (params: params) => }我們的資料渲染元件一般是深層次的,請求時需要時間間隔等外部引數,使用全域性變數來維護這些引數。 上層元件使用 change 來修改引數,資料渲染元件根據丟擲的引數發出請求。
export let params: params = ;function useparams() as params; }return ;}export default useparams;面板重新整理。
從根節點向下搜尋,找到葉節點,並觸發相應的請求。
class dashboardmodel );class panelmodel else }rerequest()刪除面板。
對於面板的刪除,我們只需要在對應的 dashboard 下進行刪除,刪除後會改變當前的 dashboard 高度,這與下面的目錄收縮相同。
class dashboardmodel ** 基於傳入面板的過濾器 * 引數面板 要過濾的面板陣列 * 返回過濾的面板 * filterpanelsbypanels(panels: panelmodel) 。儲存面板。
與後端溝通後,當前檢測報告的資料結構由前端維護,最後給後端乙個字串。 獲取當前面板資料,並使用 json 進行轉換。
從面板獲取資訊的過程從根節點開始,遍歷到葉節點,然後從葉節點開始,逐層返回,這就是回溯的過程。
class dashboardmodel // ..最終儲存所需的屬性,其他屬性不需要 const persistedproperties: = ; class panelmodel ; for (const property in this) model.panels = this.dashboard?.gets**emodel() return model; }// ..面板詳細資訊顯示。
在檢視面板時,可以修改時間等,這些操作會影響例項中的資料,因此需要在細節中區分原始資料和資料。
通過重新生成原始面板資料的 panelmodel 例項,對此例項的任何操作都不會影響原始資料。
const model = panel.gets**emodel();const newpanel = new panelmodel();建立乙個新例項 seteditpanel(newpanel); 設定為詳細資訊在DOM上,詳情頁面是絕對定位的,上面覆蓋著檢驗報告。
目錄會縮小和擴充套件。
維護“目錄”面板的摺疊屬性,以控制面板的隱藏顯示。
類 PanelModel 元件呈現當您縮小和展開目錄時,您將更改其高度,現在您需要將此更改的高度同步到下一級的儀表板。
下乙個級別需要做的是我們如何控制目錄。 如下所示,控制第乙個輔助目錄的收縮:
當面板發生變化時,需要通知父面板進行相應的操作。
新增 top 以獲取父例項。
class dashboardmodel this.panels = [.this.panels];頂級儀表板容器沒有 top thistop?.changeheight(h); this.forceupdate();// ..class panelmodel ** param h changed height * changeheight(h: number) ) change the height of the own panel thistop.togglepanelheight(this, h);觸發父項更改此項forceupdate();// ..組織流程和冒泡型別,一直到頂級儀表板。 展開的宮縮也是如此。
呈現目錄的右側。
錨點序列號。
錨點使用錨點 + id 來選擇元件。
序數是按渲染生成的。
渲染使用發布-訂閱方法進行管理。
每當儀錶盤更改布局時,都需要同步更新目錄右側,任何面板都可能需要觸發目錄右側進行更新。
如果我們以在例項中維護相應元件的渲染事件為例,則存在兩個問題:
需要做區分,比如重新整理面板時,不需要觸發右側目錄的渲染。
每個面板如何訂閱右側目錄中的呈現事件。
最後,採用發布-訂閱者模型來管理事件。
class eventemitter ;** 訂閱 * 引數事件 * 引數 fn 訂閱事件** 返回 * on(event: string, fn: (=> void) { ** 取消訂閱 * 引數事件 訂閱事件 * 引數 fn 訂閱事件** 返回 * off(event: string, fn: (=> void) { ** publish * 引數事件 訂閱事件 * 引數引數 額外引數 * 返回 * emit(event: string, ..)arg: any)eventemitter.emit(this.key);觸發面板的訂閱事件事件發射器emit(global);觸發頂級訂閱事件,包括對右側目錄的更新PDF 匯出由 html2canvas + jspdf 實現。 需要注意的是,當PDF太長時,內容區域可能會被拆分。 我們需要手動計算面板的高度,是否超過當前文件,如果超過當前文件,我們需要提前劃分,新增到下一頁,並盡可能地拆分目錄面板和資料面板。
Word 匯出是通過 html-docx-js 實現的,它需要保留目錄的結構,並且可以在面板下新增摘要,這需要我們單獨轉換每個面板。
實現的思路是按照面板遍歷,找到目錄面板就是插入帶有 h1 和 h2 標籤的面板,如果是資料面板,在資料面板中維護乙個 ref 屬性,這樣我們就可以獲取當前面板的 dom 資訊,按照這個進行轉換, 並以 base64 格式使用(word 僅支援 base64 插入)。
當前版本的檢查報告仍處於起步階段,尚未達到最終形式,但隨著我們不斷迭代和公升級,我們將逐步新增包括摘要描述在內的一些功能。
採用當前方法後,如果以後需要調整UI介面,只需要修改相關的UI元件,如新增餅圖、**等。 對於資料互動級別的更改,只需轉到 dashboardmodel 和 panelmodel 即可進行必要的更新。 此外,我們可以針對特定場景靈活提取特殊類,確保整個迭代過程更加模組化和高效。