本期葡萄城公開課,將由國電聯合動力技術有限公司,資深前端開發工程師——李林慧女士,與大家在線分享“在 React 框架中使用 SpreadJS 純前端表格控件”的實戰開發經驗。

本節公開課回放

國電聯合動力技術有限公司于2007年6月成立,注冊資本3.13億元,擁有五個全資子公司及五個控股公司,總部位于北京,在全國設有多家生產基地。作為中國國電集團為發展中國綠色能源事業需要,解決風電關鍵、重大設備國產化問題而組建的高新技術公司,國電聯合動力技術有限公司為構建低碳和諧社會、鑄造值得信賴的國產風機品牌提供了強大的技術支持,公司主營業務包括風電機組設計、生產制造、研究開發、銷售服務等。

以下是本期公開課部分精彩內容:

項目背景:

公司采購SpreadJS 純前端表格控件,其主要應用場景是開發一套完善的日常業務流程管理系統,該系統以部門為單位劃分,每周由各部門填報人員,根據系統運行情況、日常運營數據等在系統中進行填報,后交由部門進行數據匯總,并在移動端/PC端系統頁面進行數據展示。

在采購SpreadJS 純前端表格控件之前,公司使用excel進行數據匯總,無法保證各部門的數據流通及時、共享。在經過技術調研后,決定使用SpreadJS(該控件的功能、布局和操作均接近原生Excel)開發業務流程管理系統。通過該系統,部門領導可以實時查看業務運行狀況并給予反饋意見,保證數據流通實時、有效。

選擇SpreadJS的主要原因:

  1. 功能、布局與Excel高度一致,數據操作、使用習慣均接近原生Excel
  2. 可在前端導入、導出 Excel 文件,且保持文件的最大完整性
  3. 支持所有常見的Excel公式函數

本期公開課大綱:

  1. SpreadJS常用組件及其屬性
    1. SpreadSheets
    2. Worksheet
  2. 渲染過程
  3. 自定義數據驗證及Demo演示
  4. 課程答疑

SpreadJS常用組件及其屬性

<SpreadSheets>

    <SpreadSheets
    backColor="aliceblue"
    hostStyle={{ width: `${tableWidth}px`, height: '600px' }}
    rowChanged={(_, sheet) => this.handleRowChanged(sheet)}
    valueChanged={(_, sheet) => this.handleValueChanged(sheet)}
    rangeChanged={(_, sheet) => this.handleRangeChanged(sheet)}
    workbookInitialized={spread => this.init(spread)}
    >
  • rowChanged:主要用于刪除整行觸發,需要判斷 propertyName 屬性
 handleRowChanged(content) {// 整行刪除觸發
    const {sheet, propertyName} = content
    if (propertyName === 'deleteRows') {
      const {originalItem: {reportId}} = sheet.getDeletedRows()[0]
      if (reportId) {
        this.setState({loading: true})
        setTimeout(() => {
          alert('刪除成功!')
        }, 2000)
      }
    }
  • valueChanged:改變單元格值觸發
 handleValueChanged(sheet) {
    const {changedRow} = this.state
    changedRow.push(sheet.row)
    this.setState({changedRow: Array.from(new Set(changedRow))})
  }
  • rangeChanged:輸入公式、delete 刪除數據、移動單元格觸發
handleRangeChanged({ changedCells }) { // 輸入公式、delete刪除數據、移動單元格觸發
    const { changedRow } = this.state;
    for (let i = 0; i < changedCells.length; i++) {
      changedRow.push(changedCells[i].row);
    }
    this.setState({ changedRow: Array.from(new Set(changedRow)) });
  }
  • workbookInitialized:初始化表格控件,返回一個 spread 實例

<Worksheet>

<Worksheet dataSource = {data}  name="簡單綜合示例" autoGenerateColumns={false}>
    {tableHead.length > 0 ? tableHead.map(item => (
    <Column
        dataField={item.name}
        key={item.name}
        headerText={item.displayName}
        width={item.width}
        style={{wordWrap: 'break-wrap', locked: false}}
        formatter={item.formatter}
    />
    )): null}
</Worksheet>
  • dataSource:數據源
  • name:工作簿名稱
  • <Column>

渲染過程

  1. workbookInitialized返回一個spread實例,在init方法里可以對options按需進行配置。有了spread實例后也就可以生成一個Excel實例對象,其中包含了常用的spread和spreadNS,以及自定義的一些常用spread方法,比如搜索功能、自適應高度等。
  2. 從服務器端獲取報表數據后,再根據報表保存的模板id獲取對應的表頭模板,渲染表頭時可以做一些前期工作,如定義選項類的單元格、定義單元格的格式、公式等。
  3. 將報表數據賦值給Worksheet組件的dataSource屬性。
  4. 渲染完畢。

自定義數據驗證及Demo演示

  1. 數據驗證高亮樣式(Data Validation Highlight Style):SpreadJS 支持自定義數據驗證樣式和不同的單元格突出顯示類型,包括 circle,dogear 和 icon。

  2. 數據驗證代碼

// 數據驗證
dataValidate() {
function MyCondition(reg) {
    this.reg = reg
    GC.Spread.Sheets.ConditionalFormatting.Condition.apply(this, arguments)
}
MyCondition.prototype = new GC.Spread.Sheets.ConditionalFormatting.Condition()
MyCondition.prototype.evaluate = function(evaluator, baseRow, baseColumn, actualObj) {
    const reg = new RegExp (this.reg)
    if (reg.test(actualObj)) {
    return true
    }
    else {
    return false
    }
}
// 數據驗證相關變量
const {spread} = this.state,
    sheet = spread.getSheet(0),
    sheetActive = spread.getActiveSheet(),
    nCondition = new MyCondition(/[+-]?\d+(\.\d+)?|^\s+$/),
    validator = new GC.Spread.Sheets.DataValidation.DefaultDataValidator(nCondition)
// 數據驗證
spread.suspendPaint()
validator.type(GC.Spread.Sheets.DataValidation.CriteriaType.custom)
spread.options.highlightInvalidData = true
validator.showInputMessage(true)
validator.inputMessage('必須填寫數字!')
validator.inputTitle('提示')
sheetActive.setDataValidator(0, 4, sheet.getRowCount(), 1, validator) // rowIndex, colIndex, rowCount, colCount
sheet.resumePaint()
}

課程答疑Q&A

Q:為何數據量很小,但頁面加載時會出現卡頓

A:凡是涉及到表格重繪的地方最好都用 spread.suspendPaint() 和 spread.resumePaint() 包裹起來,避免頻繁重繪引起卡頓。

Q: 如何渲染多重表頭?

A:Column組件尚未支持多重表頭,針對這個問題,可以在渲染表頭步驟時(此時已獲取到有樹形結構的表頭模板),先給表格setDataSource,獲取所有表頭模板的葉子節點后按列進行表單級別的綁定,然后通過操作表頭區域的賦值、合并單元格等操作手動渲染表頭。

Q:為什么要引入表頭模板?

A:SpreadJS中每一列的列頭顯示的是中文,但是實際上存取對應的是數據庫中的一個字段,所以需要通過數據綁定把表格數據和字段映射起來。其中模板在系統管理內另有維護入口,支持增刪改等基本功能。

公開課示例下載

擴展閱讀