主頁 > 企業開發 > 如何實作巡檢報告?

如何實作巡檢報告?

2023-07-01 08:41:36 企業開發

什么是巡檢報告

巡檢報告是指對某一個系統或設備進行全面檢查,并把檢查結果及建議整理成報告的程序,

巡檢報告通常用于評估系統或設備的運行狀況與性能,以發現問題、優化系統、提高效率、降低故障率等方面提供參考,

file

要實作什么功能

file

自定義布局

  1. 現報告中的面板可進行拖拽改變布局,
  2. 在拖拽的程序中限制拖拽區域,只允許在同一父級內進行拖拽,不允許跨目錄移動,不允許改變目錄的級別,比如把一級目錄移動到另一個一級目錄內,變成二級目錄

目錄可收縮展開

  1. 目錄支持收縮展開,收縮時隱藏所以子面板,展開時顯示所以子面板
  2. 移動目錄時,子面板跟隨移動
  3. 改變目錄后,同步更新右側的目錄面板
  4. 生成目錄編號

file

右側目錄樹

  1. 生成目錄編號
  2. 支持錨點滾動
  3. 支持展開收縮
  4. 與左側報告聯動

file

資料面板

  1. 根據日期范圍獲取指標資料
  2. 通過圖表的形式展示指標資訊
  3. 查看詳情,洗掉
  4. 各面板的請求設計,支持重繪請求

資料面板

面板詳情

面板匯入

  1. 統計目錄下選擇的面板數量
  2. 匯入新面板時,不能破壞已有布局,新面板只能跟在舊面板后
  3. 匯入已有面板時,需要進行資料比較,有資料變更需要重新獲取最新的資料

file

保存

在保存前,所有影響布局相關的操作,都是臨時的,包括匯入面板,只有在點擊保存后,才會把當前資料提交給后端進行保存,

支持 pdf 和 word 匯出

file

巡檢報告實作方案

資料結構設計

先看看使用扁平結構下的

file

在扁平結構下,確定子項只需要找到下一個 row 面板,對于多級目錄下也是同理,只是對一級目錄需要額外處理,
這種結構上實作簡單,但是需求要求我們限制目錄的拖拽,限制目錄需要一個比較清晰的面板層級關系,很顯然,用樹能夠很清晰的描述一個資料的層級結構

file

組件設計

與傳統組件編程有所區別,

在實作上對渲染和資料處理進行了分離,分為兩塊:

  • react 組件:主要負責頁面渲染
  • class : 負責資料的處理

file

DashboardModel

class DashboardModel {
    id: string | number;
    panels: PanelModel[]; // 各個面板
		// ...
}

PanelModel

class PanelModel {
    key?: string;
    id!: number;
    gridPos!: GridPos; // 位置資訊
    title?: string;
    type: string;
    panels: PanelModel[]; // 目錄面板需要維護當前目錄下的面板資訊
    // ...
}

每一個 Dashboard 組件對應一個 DashboardModel,每一個 Panel 組件對應一個 PanelModel,

react 組建根據類實體中的資料進行渲染,

實體生產后,不會輕易的銷毀,或者改變參考地址,這讓依賴實體資料進行渲染的 React 組件無法觸發更新渲染,

需要一個方式,在實體內資料發生改變后,由我們手動觸發組件的更新渲染,

組件渲染控制

由于我們采用的是 hooks 組件,不像 class 組件有 forceUpdate 方法觸發組件的方法,

而在 react18 中有一個新特性 useSyncExternalStore,可以讓我們訂閱外部的資料,如果資料發生改變了,會觸發組件的渲染,

實際上 useSyncExternalStore 觸發組件渲染的原理就是在內部維護了一個 state,當更改了 state 值,引起了外部組件的渲染,

基于這個思路簡單的實作了一個能夠觸發組件渲染的 useForceUpdate 方法,

export function useForceUpdate() {
    const [_, setValue] = useState(0);
    return debounce(() => setValue((prevState) => prevState + 1), 0);
}

雖說實作了 useForceUpdate,但是在實際使用的程序中,還需要在組件銷毀時移除事件,

useSyncExternalStore 已經內部已經實作了,直接使用即可,

useSyncExternalStore(dashboard?.subscribe ?? (() => {}), dashboard?.getSnapshot ?? (() => 0));
useSyncExternalStore(panel?.subscribe ?? (() => {}), panel?.getSnapshot ?? (() => 0));

根據useSyncExternalStore使用,分別添加了 subscribe 和 getSnapshot 方法,

class DashboardModel {	// PanelModel 一樣 
    count = 0;
    forceUpdate() {
    this.count += 1;
    eventEmitter.emit(this.key);
    }

    /**
     * useSyncExternalStore 的第一個入參,執行 listener 可以觸發組件的重渲染
     * @param listener
     * @returns
     */
    subscribe = (listener: () => void) => {
        eventEmitter.on(this.key, listener);
        return () => {
            eventEmitter.off(this.key, listener);
        };
    };

    /**
     * useSyncExternalStore 的第二個入參,count 在這里改變后觸發diff的通過,
     * @param listener
     * @returns
     */
    getSnapshot = () => {
        return this.count;
    };
}

當改變資料后,需要觸發組件的渲染,只需要執行forceUpdate 即可,

面板拖拽

市面上比較大眾的拖拽插件有以下幾個:

  • react-beautiful-dnd
  • react-dnd
  • react-grid-layout

經過比較后,發現 react-grid-layout 非常適合用來做面板的拖拽功能,react-grid-layout 本身使用簡單,基本無上手門檻,最終決定使用 react-grid-layout 詳細說明可以查看以下鏈接:

react-grid-layout

在面板布局改變后觸發react-grid-layoutonLayoutChange 方法,可以拿到布局后的所有面板最新的位置資料,

const onLayoutChange = (newLayout: ReactGridLayout.Layout[]) => {
    for (const newPos of newLayout) {
        panelMap[newPos.i!].updateGridPos(newPos);
    }
    dashboard!.sortPanelsByGridPos();
};

panelMap 是一個 map,key 為 Panel.key, value 為面板,是在我們組件渲染時就已經準備好了,

const panelMap: Record<PanelModel['key'], PanelModel> = {};

可以通過 panelMap 找到對應的面板,執行面板的 updateGridPos 方法進行更新面板的布局資料,
到這,我們只是完成了面板本身資料更新,還需要執行儀表盤的 sortPanelsByGridPos 方法,對所有的面板進行排序,

class DashboardModel {
    sortPanelsByGridPos() {
        this.panels.sort((panelA, panelB) => {
            if (panelA.gridPos.y === panelB.gridPos.y) {
                return panelA.gridPos.x - panelB.gridPos.x;
            } else {
                return panelA.gridPos.y - panelB.gridPos.y;
            }
        });
    }
    // ...
}

面板拖動范圍

目前的拖動范圍是整個儀表盤,可隨意拖動,如下:

file

綠色是儀表盤可拖拽區域,灰色為面板,

如果需要限制就需要改成如下的結構:

file

在原本的基礎上,以目錄為單位區分,綠色為整體的可移動區域,黃色為一級目錄塊,可在綠色區域拖動,拖動時以整個黃色塊進行拖動,紫色為二級目錄塊,可在當前黃色區域內拖動,不可脫離當前黃色塊,灰色的面板只能在當前目錄下拖動,

在原先資料結構基礎上進行改造:

file

class PanelModel {
    dashboard?: DashboardModel; // 當前目錄下的 dashboard
    // ...
}

目錄

目錄收縮展開

為目錄面板維護一個 collapsed屬性用來控制面板的隱藏顯示

class PanelModel {
    collapsed?: boolean; // type = row
    // ...
}

// 組件渲染
{!collapsed && <DashBoard dashboard={panel.dashboard} serialNumber={serialNumber} />}

對目錄進行收縮展開時,會改變自身的高度,現在還需要把這個改變的高度同步給上一級的儀表盤,
上一級需要做的就是類似我們控制目錄的處理,如下,控制第一個二級目錄收縮:

file

當面板發生變更時,需要通知上級面板,進行對應的操作,

file

增加一個 top 用來獲取到父級實體,

class DashboardModel {
    top?: null | PanelModel; // 最近的 panel 面板

    /**
     * 面板高度變更,同步修改其他面板進行對應高度 Y 軸的變更
     * @param row 變更高度的 row 面板
     * @param h 變更高度
     */
    togglePanelHeight(row: PanelModel, h: number) {
        const rowIndex = this.getIndexById(row.id);

        for (let panelIndex = rowIndex + 1; panelIndex < this.panels.length; panelIndex++) {
            this.panels[panelIndex].gridPos.y += h;
        }
        this.panels = [...this.panels];

        // 頂級 dashBoard 容器沒有 top
        this.top?.changeHeight(h);
        this.forceUpdate();
    }
    // ...
}

class PanelModel {
    top: DashboardModel; // 最近的 dashboard 面板

    /**
     * @returns h 展開收起影響的高度
     */
    toggleRow() {
        this.collapsed = !this.collapsed;
        let h = this.dashboard?.getHeight();
        h = this.collapsed ? -h : h;
        this.changeHeight(h);
    }

    /**
     *
     * @param h 變更的高度
     */
    changeHeight(h: number) {
        this.updateGridPos({ ...this.gridPos, h: this.gridPos.h + h }); // 更改自身面板的高度
        this.top.togglePanelHeight(this, h); // 觸發父級變更
        this.forceUpdate();
    }
    // ...
}

整理流程與冒泡型別,一直到最頂級的 Dashboard,

file

展開收縮同理,

面板的洗掉

對于面板的洗掉,我們只需要在對應的 Dashboard 下進行移除,洗掉后會改變當前 Dashboard 高度,這塊的處理與上面的目錄收縮一致,

class DashboardModel {
    /**
     * @param panel 洗掉的面板
     */
    removePanel(panel: PanelModel) {
        this.panels = this.filterPanelsByPanels([panel]);

        // 冒泡父容器,減少的高度
        const h = -panel.gridPos.h;
        this.top?.changeHeight(h);

        this.forceUpdate();
    }

    /**
     * 根據傳入的面板進行過濾
     * @param panels 需要過濾的面板陣列
     * @returns 過濾后的面板
     */
    filterPanelsByPanels(panels: PanelModel[]) {
        return this.panels.filter((panel) => !panels.includes(panel));
    }
    // ...
}

面板的保存

PS:與后端溝通后,當前巡檢報告資料結構由前端自主維護,最終給后端一個字串就好,
獲取到目前的面板資料,用 JSON 進行轉換即可,

面板的資訊獲取程序,先從根節點出發,遍歷至葉子結點,再從葉子結點開始,一層層向上進行回傳,也就是回溯的程序,

class DashboardModel {
    /**
     * 獲取所有面板資料
     * @returns
     */
    getSaveModel() {
        const panels: PanelData[] = this.panels.map((panel) => panel.getSaveModel());
        return panels;
    }
    // ...
}

// 最終保存時所需要的屬性,其他的都不需要
const persistedProperties: { [str: string]: boolean } = {
    id: true,
    title: true,
    type: true,
    gridPos: true,
    collapsed: true,
    target: true,
};

class PanelModel {
    /**
     * 獲取所有面板資料
     * @returns
     */
    getSaveModel() {
        const model: any = {};

        for (const property in this) {
            if (persistedProperties[property] && this.hasOwnProperty(property)) {
                model[property] = cloneDeep(this[property]);
            }
        }
        model.panels = this.dashboard?.getSaveModel() ?? [];

        return model;
    }
    // ...
}

面板

面板的匯入設計

file

后端回傳的資料是一顆有著三級層級的樹,我們拿到后,在資料上維護成 moduleMapdashboardMappanelMap 3個Map,

import { createContext } from 'react';

export interface Module { // 一級目錄
    key: string;
    label: string;
    dashboards?: string[];
    sub_module?: Dashboard[];
}

export interface Dashboard { // 二級目錄
    key: string;
    dashboard_key: string;
    label: string;
    panels?: number[];
    selectPanels?: number[];
    metrics?: Panel[];
}

export interface Panel {
    expr: Expr[]; // 資料源陳述句資訊
    label: string;
    panel_id: number;
}

type Expr = {
    expr: string;
    legendFormat: string;
};

export const DashboardContext = createContext({
    moduleMap: new Map<string, Module>(),
    dashboardMap: new Map<string, Dashboard>(),
    panelMap: new Map<number, Panel>(),
});

我們在渲染模塊時,遍歷 moduleMap ,并通過 Module 內的dashboards資訊找到二級目錄,

在互動上設定一級目錄不可選中,當選中二級目錄時,通過二級目錄 Dashboardpanels 找到相關的面板渲染到右側區域,
對于這3個Map的操作,維護在 useHandleData中,匯出:

{
    ...map, // moduleMap、dashboardMap、panelMap
    getData, // 生成巡檢報告的資料結構
    init: initData, // 初始化 Map
}

面板選中回填

在進入面板管理時,需要回填已選中的面板,我們可以通過 getSaveModel 獲取到當前巡檢報告的資訊,把對應的選中資訊存放到 selectPanels 中,

現在我們只需要改變 selectPanels 中的值,就可以做到對應面板的選中,

面板選中重置

直接遍歷 dashboardMap,并把每個selectPanels重置,

dashboardMap.forEach((dashboard) => {
    dashboard.selectPanels = [];
});

面板插入

在我們選中面板后,對選中面板進行插入時,有幾種情況:

  • 巡檢報告原本存在的面板,這次也選中,在插入時會比較資料,如果資料發生改變,需要根據最新的資料源資訊進行請求,并渲染,
  • 巡檢報告原本存在的面板,這次未選中,在插入時,需要洗掉掉未選中的面板,
  • 新選中的面板,在插入時,在對應目錄的末尾進行插入,

添加新面板需要,與目錄收縮類似,不同的是:

  1. 目錄收縮針對只有一個目錄,而插入在針對的是整體,
  2. 目錄收縮是直接從子節點開始向上冒泡,而插入是先從根節點開始向下插入,插入完成后在根據最新的目錄資料,更新一遍布局,
class DashboardModel {
    update(panels: PanelData[]) {
        this.updatePanels(panels); // 更新面板
        this.resetDashboardGridPos(); // 重新布局
        this.forceUpdate();
    }

    /**
     * 以當前與傳入的進行對比,以傳入的資料為準,并在當前的順序上進行修改
     * @param panels
     */
    updatePanels(panels: PanelData[]) {
        const panelMap = new Map();
        panels.forEach((panel) => panelMap.set(panel.id, panel));
        this.panels = this.panels.filter((panel) => {
            if (panelMap.has(panel.id)) {
                panel.update(panelMap.get(panel.id));
                panelMap.delete(panel.id);
                return true;
            }
            return false;
        });
        panelMap.forEach((panel) => {
            this.addPanel(panel);
        });
    }

    addPanel(panelData: any) {
        this.panels = [...this.panels, new PanelModel({ ...panelData, top: this })];
    }

    resetDashboardGridPos(panels: PanelModel[] = this.panels) {
        let sumH = 0;
        panels?.forEach((panel: any | PanelModel) => {
            let h = ROW_HEIGHT;
            if (isRowPanel(panel)) {
                h += this.resetDashboardGridPos(panel.dashboard.panels);
            } else {
                h = panel.getHeight();
            }

            const gridPos = {
                ...panel.gridPos,
                y: sumH,
                h,
            };
            panel.updateGridPos({ ...gridPos });
            sumH += h;
        });
        return sumH;
    }
}

class PanelModel {
    /**
     * 更新
     * @param panel
     */
    update(panel: PanelData) {
        // 資料源陳述句發生變化需要重新獲取資料
        if (this.target !== panel.target) {
            this.needRequest = true;
        }
        this.restoreModel(panel);
        if (this.dashboard) {
            this.dashboard.updatePanels(panel.panels ?? []);
        }
        this.needRequest && this.forceUpdate();
    }
}

面板請求

needRequest 控制面板是否需要進行請求,如果為 true 在面板下一次進行渲染時,會進行請求,
請求的處理也放在了 PanelModel 中,(是否單獨維護請求的邏輯?)

import { Params, params as fetchParams } from '../../components/useParams';

class PanelModel {
    target: string; // 資料源資訊
    getParams() {
        return {
            targets: this.target,
            ...fetchParams,
        } as Params;
    }
    request = () => {
        if (!this.needRequest) return;
        this.fetchData(this.getParams());
    };
    fetchData = https://www.cnblogs.com/dtux/p/async (params: Params) => {
        const data = await this.fetch(params);
        this.data = data;
        this.needRequest = false;
        this.forceUpdate();
    };
    fetch = async (params: Params) => { /* ... */ }
}

我們資料渲染組件一般層級較深,而請求時會需要時間區間等外部引數,對于這部分引數采用全域變數的方式,用 useParams 進行維護,上層組件使用 change 修改引數,資料渲染組件根據拋出的 params 進行請求,

export let params: Params = {
    decimal: 1,
    unit: null,
};

function useParams() {
    const change = (next: (() => Params) | Params) => {
        if (typeof next === 'function') params = next();
        params = { ...params, ...next } as Params;
    };

    return { params, change };
}

export default useParams;

面板重繪

file

從根節點向下查找,找到葉子節點,在觸發對應的請求,

class DashboardModel {
    /**
     * 重繪子面板
     */
    reloadPanels() {
        this.panels.forEach((panel) => {
            panel.reload();
        });
    }
}

class PanelModel {
    /**
     * 重繪
     */
    reload() {
        if (isRowPanel(this)) {
            this.dashboard.reloadPanels();
        } else {
            this.reRequest();
        }
    }
    reRequest() {
        this.needRequest = true;
        this.request();
    }
}

右側目錄渲染

錨點/序號

錨點采用 Anchor + id 選中組件,

序號根據每次渲染進行生成,

采用發布訂閱管理渲染

每當儀表盤改變布局的動作時,右側目錄就需要進行同步更新,而任意一個面板都有可能需要觸發右側目錄的更新,
如果我們采用實體內維護對應組件的渲染事件,有幾個問題:

  1. 需要進行區分,比如重繪面板時,不需要觸發右側目錄的渲染,
  2. 每個面板如何訂閱右側目錄的渲染事件?

最終采用了發布訂閱者模式,對事件進行管理,

class EventEmitter {
    list: Record<string, any[]> = {};

    /**
     * 訂閱
     * @param event 訂閱事件
     * @param fn 訂閱事件回呼
     * @returns
     */
    on(event: string, fn: () => void) {}

    /**
     * 取消訂閱
     * @param event 訂閱事件
     * @param fn 訂閱事件回呼
     * @returns
     */
    off(event: string, fn: () => void) {}

    /**
     * 發布
     * @param event 訂閱事件
     * @param arg 額外引數
     * @returns
     */
    emit(event: string, ...arg: any[]) {
}
eventEmitter.emit(this.key); // 觸發面板的訂閱事件
eventEmitter.emit(GLOBAL); // 觸發頂級訂閱事件,就包括右側目錄的更新

面板詳情展示

file

對面板進行查看時,可修改時間等,這些操作會影響到實體中的資料,需要對原資料與詳情中的資料進行區分,

通過對原面板資料的重新生成一個 PanelModel 實體,對這個實體進行任意操作,都不會影響到原資料,

const model = panel.getSaveModel();
const newPanel = new PanelModel({ ...model, top: panel.top }); // 創建一個新的實體
setEditPanel(newPanel); // 設定為詳情

dom上,詳情頁面是采用絕對定位,覆寫著巡檢報告,

pdf/word 匯出

pdf 匯出由 html2Canvas + jsPDF 實作,需要注意的是,當圖片過長pdf會對圖片進行切分,有可能出現切分的時內容區域,

需要手動計算面板的高度,是否超出當前檔案,如果超出需要我們提前進行分割,添加到下一頁中,
盡可能把目錄面板和資料面板一塊切分,

word 匯出由 html-docx-js 實作, 需要保留目錄的結構,并可以在面板下添加總結,這就需要我們分別對每一個面板進行圖片的轉換,

實作的思路是根據 panels 遍歷,找到目錄面板就是用 h1、h2 標簽插入,如果是資料面板,在資料面板中維護一個 ref 的屬性,能讓我們拿到當前面板的 dom 資訊,根據這個進行圖片轉換,并為 base64 的格式(word 只支持 base64 的圖片插入),

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556431.html

標籤:Html/Css

上一篇:HTML網頁內容適配——標題欄

下一篇:返回列表

標籤雲
其他(161947) Python(38266) JavaScript(25517) Java(18286) C(15238) 區塊鏈(8275) C#(7972) AI(7469) 爪哇(7425) MySQL(7278) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4609) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2438) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1985) HtmlCss(1981) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 如何實作巡檢報告?

    # 什么是巡檢報告 巡檢報告是指對某一個系統或設備進行全面檢查,并把檢查結果及建議整理成報告的程序。 巡檢報告通常用于評估系統或設備的運行狀況與性能,以發現問題、優化系統、提高效率、降低故障率等方面提供參考。 ![file](https://img2023.cnblogs.com/other/233 ......

    uj5u.com 2023-07-01 08:41:36 more
  • HTML網頁內容適配——標題欄

    前言 現在很多網頁用的都是固定標題欄,就像這樣: 很多網站為了兼容小視窗還會做個JS適配: 但是如果視窗比這還小的話... 那就只剩下一部分了。 由于設定position:fixed后元素不會隨著滾動條滾動,所以超出頁面邊緣的部分將永遠看不見,除非增大視窗或縮小顯示比例。 很多設計師忘記考慮這一點了 ......

    uj5u.com 2023-07-01 08:40:26 more
  • 1.1 Metasploit 工具簡介

    Metasploit 簡稱(MSF)是一款流行的開源滲透測驗框架,由`Rapid7`公司開發,可以幫助安全和IT專業人士識別安全性問題,驗證漏洞的緩解措施,并管理專家驅動的安全性進行評估,提供真正的安全風險情報。并且該框架還提供了一系列攻擊模塊和`Payload`工具,可用于漏洞利用、及漏洞攻擊。同... ......

    uj5u.com 2023-07-01 08:39:46 more
  • 1.1 Metasploit 工具簡介

    Metasploit 簡稱(MSF)是一款流行的開源滲透測驗框架,由`Rapid7`公司開發,可以幫助安全和IT專業人士識別安全性問題,驗證漏洞的緩解措施,并管理專家驅動的安全性進行評估,提供真正的安全風險情報。并且該框架還提供了一系列攻擊模塊和`Payload`工具,可用于漏洞利用、及漏洞攻擊。同... ......

    uj5u.com 2023-07-01 08:38:27 more
  • Vue3從入門到精通(三)

    vue3插槽Slots 在 Vue3 中,插槽(Slots)的使用方式與 Vue2 中基本相同,但有一些細微的差異。以下是在 Vue3 中使用插槽的示例: // ChildComponent.vue <template> <div> <h2>Child Component</h2> <slot></ ......

    uj5u.com 2023-06-30 09:07:11 more
  • 記錄--寫一個高德地圖巡航功能的小DEMO

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 風格設定 加載地圖 使用AMapLoader.load加載地圖,從 控制臺 申請一個屬于自己的key import AMapLoader from '@amap/amap-jsapi-loader'; ... const AMap = a ......

    uj5u.com 2023-06-30 08:29:46 more
  • CSS基礎-背景

    # 背景 ### **background-color** 背景顏色, 可以使用十六進制、rgb、rgba表示。 **語法** ```css /**selector 背景元素的原則去*/ /** color 背景顏色的值, 可以是 顏色名稱、十六進制值、RGB、RGBA*/ selector { b ......

    uj5u.com 2023-06-30 08:29:30 more
  • 京東到家小程式-在性能及多端能力的探索實踐

    為了提高研發效率,經過技術選型采用了taro3+原生混合開發模式,本文主要講解我們是如何基于taro框架,進行多端能力的探索和性能優化。 ......

    uj5u.com 2023-06-30 08:29:18 more
  • 初入前端-HTML

    ## HTML ### HTML歷史 HTML(Hypertext Markup Language)的歷史可以追溯到上世紀90年代初,以下是HTML的主要歷史階段: 1. HTML 1.0:在1991年發布,是HTML的最初版本,用于創建基本的文本和鏈接結構,但功能有限。 2. HTML 2.0:于 ......

    uj5u.com 2023-06-30 08:29:14 more
  • 圖書商城專案練習②后端服務Node/Express/Sqlite

    本系列文章是為學習Vue的專案練習筆記,盡量詳細記錄一下一個完整專案的開發程序。面向初學者,本人也是初學者,搬磚技識訓不成熟。專案在技術上前端為主,包含一些后端代碼,從基礎的資料庫(Sqlite)、到后端服務Node.js(Express),再到Web端的Vue,包含服務端、管理后臺、商城網站、小程... ......

    uj5u.com 2023-06-30 08:28:53 more