从零实现一套低代码(保姆级教程)【后端服务】 --- 【9】实现Table表格组件以及数据展示

摘要

经过前面几章的内容,我们的后端服务已经支持了对页面的管理,对图片的管理,并且也支持了在XinBuilder中如何去使用它们。其实到目前为止,我们的XinBuilderServer服务,已经具备了基本的功能了。

可能是因为我是前端开发的原因,所以我在做这个项目重点一直是放在前端的功能实现的。后端代码比较少。

话说回来,这一篇继续回归到XinBuilder的项目里,再上一篇中,我们实现了和实体相关的后端内容,也就是我们有了持久化的数据存储。但是有了数据之后,我们如何能够在低代码项目里面使用?

对于数据的使用无非就是数据的处理以及数据的展示,在数据展示上,我们有很多种方式,比较常见的有表格,列表等等。

本篇主要通过Table组件来串通数据在XinBuilder中如何展示。

1.创建一张Student表

前期的准备工作,我们就在平台下创建一张Student表。

创建好后,我们来到swagger中,给这张表增加几条数据。

这里可以多加一些数据,方便后续的展示。

有了数据之后,我们希望的是,在XinBuilder中实现一个Table组件,可以去展示这些数据。或者去展示某张表的部分数据。

所以Table组件是一定需要一个属性,去选择对应的实体以及字段。

2.创建Table组件,实现选择实体的功能

Table组件我们放在数据展示分组里面,之前写过很多组件的创建。至于创建组件的过程,这里就不去贴代码了。

我们要实现的是给Table组件能够选择实体的功能。

所以在tableAttribute中,我们需要一个弹窗类型的属性。

javascript 复制代码
  {
    label: '选择实体',
    value: 'entityCode',
    type: 'modal',
    modalType: 'EntitySelect'
  },

同时,在modal下新建一个EntitySelect的文件夹。用来去选择对应的实体,和实体下面的字段。

我们要实现出一个可以选择实体,以及实体下字段的弹窗。

上面的Select选择器用来选择对应的实体,然后再通过Switch去选择该实体下的字段。 所以对于表格组件就需要两个属性。

entityCode:实体编码 schemaList:需要展示的字段

这里我们贴一下弹窗的代码:

javascript 复制代码
import { useEffect, useState } from 'react';
import { Modal, Select, Switch, message } from 'antd';
import { getComById } from '../../../utils/nodeUtils'
import Store from '../../../store/index'
import axios from 'axios';

export interface Entity {
  entityCode: string,
  entityName: string,
  entitySchema: EntitySchema
}

interface EntitySchema {
  [key: string]: string
}

const EntitySelect: React.FunctionComponent = (props: any) => {
  const { openModal, setOpenModal, valueKey } = props;
  const comList = JSON.parse(JSON.stringify(Store.getState().comList))
  const selectCom = Store.getState().selectCom
  const selectNode = getComById(selectCom, comList)
  const [nodeSchemaList, setNodeSchemaList] = useState([...(selectNode.schemaList || [])])

  const [entityList, setEntityList] = useState<Entity []>([])
  const [selectEntity, setSelectEntity] = useState(selectNode.entityCode || '')

  useEffect(() => {
    axios.post("http://localhost:4000/entity/getEntityList")
    .then(res => {
      if(res.data.data) {
        setEntityList(res.data.data)
      }else {
        message.error("请求实体列表失败")
      }
    })
  }, [])

  const handleCancel = () => {
    setOpenModal(false)
    setSelectEntity(selectNode.entityCode || '')
    setNodeSchemaList([...(selectNode.schemaList || [])])
  }

  const handleOk = () => {
    if(selectNode) {
      selectNode[valueKey as keyof typeof selectNode] = selectEntity;
      selectNode.schemaList = nodeSchemaList;
    }
    Store.dispatch({type: 'changeComList', value:comList})
    setOpenModal(false)
    setSelectEntity(selectNode.entityCode || '')
    setNodeSchemaList([...(selectNode.schemaList || [])])
  }

  const getOptions = () => {
    return entityList.map((item: Entity) => {
      return {
        value: item.entityCode,
        label: item.entityCode
      }
    })
  }

  const changeEntity = (value: string) => {
    setSelectEntity(value)
    setNodeSchemaList([])
  }

  const changeSwitch = (schemaCode: string) => {
    return (value: boolean) => {
      if(value && !nodeSchemaList.includes(schemaCode)) {
        nodeSchemaList.push(schemaCode)
      }else if(!value && nodeSchemaList.includes(schemaCode)) {
        const index = nodeSchemaList.indexOf(schemaCode);
        nodeSchemaList.splice(index,1)
      }
      setNodeSchemaList([...nodeSchemaList])
    }
  }

  const getSchemaList = () => {
    const entity = entityList.find((item: Entity) => item.entityCode === selectEntity);
    const schemaList = Object.keys(entity?.entitySchema || {}) || [];
    return <div style={{display:'flex', width:400, justifyContent:'flex-start',flexWrap:'wrap'}}>
      {
        schemaList.map(item => {
          return <div style={{width:'100px'}}>
            <p>{item}</p>
            <Switch onChange={changeSwitch(item)} value={nodeSchemaList.includes(item)}></Switch>
          </div>
        })
      }
    </div>
  }
  
  return <Modal closable={false} open={openModal} onOk={handleOk} onCancel={handleCancel}>
    <div style={{width:250, display:'flex',justifyContent:'space-between'}}>
      <div style={{marginTop:'5px', fontWeight:1000}}>选择实体:</div>
      <Select onChange={changeEntity} value={selectEntity} style={{width: 150}} options={getOptions()}></Select>
    </div>
    <div>
      {
        getSchemaList()
      }
    </div>
  </Modal>;
};

export default EntitySelect;

Ok,有了这个弹窗之后,我们就可以给表格组件赋予这两个属性了。

3.实现表格的数据展示

当表格组件有了这两个属性之后,我们就可以在表格内部通过ajax请求到对应的数据。 赋给表格的columns属性和datasource属性。

javascript 复制代码
import { useState, useEffect } from 'react';
import { Table as AntTable, message } from 'antd';
import axios from 'axios';

const Table: React.FunctionComponent = (props: any) => {
    const { entityCode, schemaList, size, bordered, showHeader = true, pagination = false, comStyle } = props;
    const [entityData, setEntityData] = useState([])
  

    useEffect(() => {
        axios.post("http://localhost:4000/entity/getEntityData", {entityCode})
        .then(res => {
            if(res.data.data) {
                setEntityData(res.data.data)
            }
        })
    }, [entityCode])

    const getColumns = () => {
        return schemaList.map((item: any) => {
            return {
                title: item,
                dataIndex: item,
                key: item
            }
        })
    }

    const getData = () => {
        return entityData.map((item: any) => {
            return {
                key: item._id,
                ...item
            }
        })
    }
 
    return <AntTable
        dataSource={getData()}
        columns={getColumns()}
        size={size}
        pagination={pagination}
        bordered={bordered}
        showHeader={showHeader}
        style={{...comStyle}}
    />;
};

export default Table;

4.补充表格的其他属性

最后我们在给表格补充一下其他属性和样式。 就可以通过关联对应的实体,确定表格的columns和data。从而正常展示数据库中的数据了。

这部分代码提交在github上
github.com/TeacherXin/...
commit: fix: 第十九节:增加Table组件

相关推荐
Jeking21711 小时前
低代码平台 表单设计器 unione form editor 功能组件 —— 悬浮按钮组件
低代码·动态表单·表单设计·表单引擎·unione cloud
markfeng817 小时前
React入门教学
前端·react.js
zhengfei61118 小时前
第2章 Agent 核心组件深度解析
前端·javascript·react.js
光影少年1 天前
react性能优化
前端·react.js·掘金·金石计划
不爱吃糖的程序媛1 天前
React Native 三方库 react-native-share 的 HarmonyOS 适配实战
react native·react.js·harmonyos
YFF菲菲兔1 天前
renderRootConcurrent VS renderRootSync 源码解析
react.js
不爱吃糖的程序媛1 天前
React Native 应用适配鸿蒙PC 实战:从白屏到成功运行
react native·react.js·harmonyos
微扬嘴角2 天前
React篇1--JSX语法规则、组件、组件实例的3大特性
前端·react.js·前端框架
光影少年2 天前
react的Context 和 Redux 区别?
前端·javascript·react.js·前端框架
2501_912784082 天前
跨境多语种页面适配方案:低代码SaaS落地实测
低代码