请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

Matthew.Xue

超级版主

8

主题

766

帖子

1260

积分

超级版主

Rank: 8Rank: 8

积分
1260
Matthew.Xue
超级版主   /  发表于:2025-4-2 11:21  /   查看:58  /  回复:0
本帖最后由 Matthew.Xue 于 2025-4-2 11:38 编辑

背景
在V18版本中,表格(在sheet中的普通表格,后面简称为sheet table)支持了绑定数据源(DataManager)的功能,这是一项非常重要的更新,因为它允许我们在表格中直接加载数据源中的数据,并且支持前端修改后自动同步到后端接口。
熟悉SpreadJS的朋友可能会问,这不就是集算表吗?的确,它的功能和集算表非常相似,但sheet table有一个优势是集算表不可比拟的,就是sheet table兼容Excel,你可以将sheet table导出为xlsx文件,并在本地Excel打开,而集算表的功能导出后会丢失,这是因为集算表属于SpreadJS特有的功能,Excel目前尚未支持。
当然,sheet table在单纯的数据增删改查方面仍然不如集算表那么强大,比如sheet table还不支持对于列的增删改,目前dataManager包含了addColumn、updateColumn、removeColumn的配置,与数据的增删改查相似,你可以直接在集算表中添加一列,并配置这一列的字段名、显示名和数据类型,集算表会自动调用addColumn,通知后端接口去增加一列,而V18版本的sheet table还不支持这样的功能。
但是SpreadJS作为一款前端控件,支持非常灵活的定制化开发,我们完全可以通过二次开发让sheet table做到对于列的增删改查,接下来就带大家看一看如何在sheet table中实现这一点。

前期准备
信息来源
我们想要实现列的增删改查,会用到哪些信息?
  • 增删改查的接口肯定要用到,dataManager中可以配置,我们可以从中读取;
  • 删和改时,当前被点击的是哪一列?我们可以从设计器的右键菜单中拿到行列信息,进而拿到当前被点击的列字段信息。

操作方式
在添加、删除、更新列的时候,以什么方式实现会比较好,如何与SpreadJS结合?这里有两种方式,其一是在右键菜单添加选项,其二是在设计上上方添加菜单,二者均是可行的。
在本demo中,我选择在右键菜单中添加选项,如下图所示:
image.png102782286.png

点击菜单后,应该有一个弹框让我们操作,我这里采用了前端开发最熟悉的element-ui来实现,以删除为例:
image.png80153197.png

接口准备
尽管这不属于前端开发的范畴,但是为了便于读者理解,还是有必要展示一下后端接口是如何实现的。
接口分为两类,第一类是对数据的增删改查,第二是对列信息的增删改查。
image.png674461364.png
可以看到在删除列时,不仅仅列信息被删除了,我还在数据信息中将每一条数据的对应字段都删除了,新增也是同理。

具体实现
制作模板
由于这篇文章是介绍sheet table绑定数据源的功能,所以我们首先需要一个数据源,我使用设计器制作好之后将模板保存了下来,模板中包含一个名为“学生表”的数据源,并配置好数据和列的增删改查接口:
image.png791896972.png

绑定数据源到sheet table
这里就直接使用了模板对应的sjs文件:
  1. fetch("./test.sjs").then(r => r.blob()).then(res => {
  2.     spread.open(res, function () {
  3.         init()
  4.     })
  5. })

  6. function init() {
  7.     sheet = spread.getActiveSheet()
  8.     let dmTable = spread.dataManager().tables.学生表
  9.     bindSourceToTable(dmTable, sheet)
  10. }
复制代码
在bindSourceToTable方法中,就可以看到我们V18版本的新特性了,直接将数据源绑定至sheet table中:
  1. function bindSourceToTable(dmTable, sheet, table) {
  2.     // 可以通过数据源中配置的拉取列信息的接口拿到每一列的信息
  3.     axios.get(dmTable.options.remote.getColumns.url).then(res => {
  4.         app.columnList = res.data.map(v => {
  5.             v.label = v.caption
  6.             v.key = v.field
  7.             return v
  8.         })
  9.         // columnInfo可以根据列信息让表格的列头显示对应的中文名,而非原来的英文字段
  10.         let columnInfo = []
  11.         res.data.forEach((v, i) => {
  12.             columnInfo.push(new GC.Spread.Sheets.Tables.TableColumn(i, v.field, v.caption))
  13.         })
  14.         if (table) {
  15.             sheet.tables.remove(table, GC.Spread.Sheets.Tables.TableRemoveOptions.none)
  16.         }
  17.         table = sheet.tables.add("table1", 0, 0, 1, columnInfo.length)
  18.         table.autoGenerateColumns(false)
  19.         // 这里就是绑定数据源到sheet table的方法,dmTable是一个数据源
  20.         table.bind(columnInfo, undefined, dmTable)
  21.         // 由于目前无法获取sheet table绑定了哪个数据源,所以先记录下来
  22.         table.dataSourceName = dmTable.name
  23.     })
  24. }
复制代码
此时,我们表格就已经准备好了:
image.png939665832.png

添加右键菜单
这里只展示“插入字段”,删除和更新其实都是同样的方式,读者可直接查看demo中的写法。
  1. let config = GC.Spread.Sheets.Designer.DefaultConfig
  2. config.contextMenu.unshift("insertField");
  3. config.commandMap = {
  4.     "insertField": {
  5.         text: "插入字段",
  6.         commandName: "insertField",
  7.         visibleContext: "ClickViewport && TableClicked", // 这里配置的目的是让“插入字段”只在表格中右键时显示
  8.     }
  9. }
  10. // 命令的定义暂时留空,后续补充
  11. let insertFieldCommand = {}
  12. let designer = new GC.Spread.Sheets.Designer.Designer("designer-container", config)
  13. let spread = designer.getWorkbook()
  14. let commandManager = spread.commandManager()
  15. commandManager.register("insertField", insertFieldCommand, null, false, false, false, false);
复制代码

点击菜单后的逻辑

前面提到,点击菜单后展示的页面是用element-ui实现的,由于element-ui是用于vue2.0的ui组件,所以不可避免要创建一个vue实例,实例名为app:
  1. let app = new Vue({
  2.     el: '#vueapp',
  3.     data: {
  4.         showEditDialog: false,
  5.         showDeleteDialog: false,
  6.         requestInfo: null,
  7.         dmTable: null,
  8.         sheetTable: null,
  9.         editType: "",
  10.         form: {},
  11.         columnList: []
  12.     }
  13. });
复制代码

app实例中会存储数据源、当前的操作对应的接口(比如addColumn的url、请求方式)、sheet table实例等等信息,这些信息会在菜单被点击时被赋值。以点击插入字段为例,点击后实际上执行的是insertFieldCommand,前面的代码中对该命令留空了,这里咱们可以对其进行补全:
  1. let insertFieldCommand = {
  2.     canUndo: false,
  3.     execute: function (_spread, options) {
  4.         let result = testField(_spread, options)
  5.         if (result.type == "error") {
  6.             app.$message.error(result.msg)
  7.             return
  8.         }

  9.         let { dataSourceTable, _table } = result
  10.         let remote = dataSourceTable.options.remote
  11.         if (!remote.addColumn.url) {
  12.             app.$message.error(`数据源${_table.dataSourceName}未设置添加列的接口,请设置后重试`)
  13.             return
  14.         }

  15.         app.editType = "insert"
  16.         app.form = {}
  17.         app.requestInfo = remote.addColumn
  18.         app.dmTable = dataSourceTable
  19.         app.sheetTable = _table
  20.         app.showEditDialog = true
  21.     }
  22. };
复制代码
其中testField是一个校验方法,用于检查用户的操作使用合法,以及当前是否存在数据源等信息。
  1. function testField(_spread, options) {
  2.     let selections = options.selections
  3.     if (selections.length > 1) {
  4.         return {
  5.             type: "error",
  6.             msg: "不支持多区域选择"
  7.         }
  8.     }
  9.     if (selections[0].colCount > 1) {
  10.         return {
  11.             type: "error",
  12.             msg: "不支持多列选择"
  13.         }
  14.     }

  15.     let _sheet = _spread.getSheetFromName(options.sheetName)
  16.     let _table = _sheet.tables.findByName(options.tableName)
  17.     if (!_table.dataSourceName) {
  18.         return {
  19.             type: "error",
  20.             msg: "此表格未绑定数据源,请绑定数据源后再试"
  21.         }
  22.     }
  23.     let dataSourceTable = _spread.dataManager().tables[_table.dataSourceName]
  24.     if (!dataSourceTable) {
  25.         return {
  26.             type: "error",
  27.             msg: `数据源${_table.dataSourceName}不存在,请添加后重试`
  28.         }
  29.     }
  30.     return {
  31.         type: "normmal",
  32.         dataSourceTable,
  33.         _table
  34.     }
  35. }
复制代码
校验都通过后,就可以展示插入字段的弹框了:
image.png347774657.png

点击确定后的逻辑
当用户填写完成点击确定后,我们就可以调用后端接口了,由于之前已经在vue的实例app中存储了相关的信息,这里就可以直接调用了:
  1. methods: {
  2.     onEditSubmit() {
  3.         this.showEditDialog = false
  4.         // 调用插入的接口
  5.         axios(this.requestInfo.url, {
  6.             data: this.form,  // 这里是用户填写的信息
  7.             method: this.requestInfo.method
  8.         }).then(res => {
  9.             // 接口返回成功后,再重新拉取一次columnInfo,并重新进行一次绑定
  10.             this.dmTable.fetch(true).then(_columnList => {
  11.                 this.columnList = _columnList
  12.                 bindSourceToTable(this.dmTable, spread.getActiveSheet(), this.sheetTable)
  13.             })
  14.         })
  15.     }
  16. }
复制代码
到这里,一切就做完了,html部分以及一些小细节没有讲到,读者可以下载demo后自己尝试。




demo.zip

48.81 KB, 下载次数: 3

演示视频.zip

4.94 MB, 下载次数: 1

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部