dexteryao 发表于 2021-9-17 16:23:41

使用VUE组件创建SpreadJS自定义单元格(一)

本帖最后由 dexteryao 于 2021-9-23 10:49 编辑

SpreadJS自定义单元格功能提供了强大的拓展能力,一方面拓展数据展示的效果,比如checkbox,Radio button的形式展示数据。另一方面,当单元格进入编辑状态可以使用下拉菜单等任何输入控件来进行输入。

关于自定义单元格的内容可以参考学习指南-自定义单元格以及API文档。
由于框架生命周期以及自定义单元格渲染逻辑的问题,目前无法直接在框架页面下直接通过template的方式使用框架下的组件。

经过了一些调研,VUE支持动态渲染的方式来创建和挂载组件,考虑可以通过这种方式在自定义单元格中注入组件。
下面使用Element UI中autocomplete为例,演示如何在VUE 2 的项目中给SpreadJS创建一个使用VUE 组件的自定义单元格。
这种方式是否有副作用还不清楚,使用时请详细测试,大家也可以跟帖讨论,一起完善方案。
Demo中数据时mock的,实际使用中可以参考combo celltype 通过items传入。

首先,需要在项目中开启运行时加载,在vue.config.js中添加runtimeCompiler: true(更新:可以参考https://gcdn.grapecity.com.cn/showtopic-131931-1-1.html 不设置此选项)
module.exports = {
    devServer: {
      port: 3000
    },
    <font color="#ff0000">runtimeCompiler: true</font>
}引用ElementUI,这里要注意要把element 的css引用放在APP import前,这样组件中对于样式的修改,才能覆盖原有项目
import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
import router from './router'

Vue.use(ElementUI);

new Vue({
el: '#app',
router,
render: h => h(App)
})

Vue.config.productionTip = false创建AutoComplateCellType,具体代码如下,需要注意几点。
1. 对于自定义的元素,需要添加gcUIElement属性,如果元素或者其父元素没有该属性,点击创建的组件便会直接退出编辑状态无法编辑。
对于ElementUI 的autocomplete,默认下拉选项内容是注入到body中的,这就导致上面说问题,这里需要给组件模板中设置:popper-append-to-body="false",让弹出的下拉选项在gcUIElement的Div中渲染。
如果使用其他组件没有类似选项,也可以跟进实际情况在弹出时在添加gcUIElement属性。
2.使用动态挂载组件的 this.vm设置和获取 单元格的值。
3. 在deactivateEditor中销毁组件

import Vue from 'vue'
import * as GC from "@grapecity/spread-sheets"
import DataService from './dataService'

function AutoComplateCellType() {
}
AutoComplateCellType.prototype = new GC.Spread.Sheets.CellTypes.Base();
AutoComplateCellType.prototype.createEditorElement = function (context, cellWrapperElement) {
cellWrapperElement.style.overflow = 'visible'
let editorContext = document.createElement("div")
editorContext.setAttribute("gcUIElement", "gcEditingInput");
let editor = document.createElement("div");
// 自定义单元格中editorContext作为容器,需要在创建一个child用于挂载,不能直接挂载到editorContext上
editorContext.appendChild(editor);
return editorContext;
}
AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) {
    let width = cellRect.width > 180 ? cellRect.width : 180;
    if (editorContext) {
      
      // 动态创建VUE 组件并挂载到editor
      const AutoCompleteComponent = {
            props: ['text','cellStyle'],
            template: `<div>
                        <el-autocomplete
                        :style="cellStyle"
                        popper-class="my-autocomplete"
                        v-model="text"
                        :fetch-suggestions="querySearch"
                        placeholder="请输入内容"
                        :popper-append-to-body="false"
                        value-key="name"
                        @select="handleSelect">
                        <i class="el-icon-edit el-input__icon"
                            slot="suffix"
                            @click="handleIconClick">
                        </i>
                        <template slot-scope="{ item }">
                            <div class="name">{{ item.name }}</div>
                            <span class="addr">{{ item.phone }}</span>
                        </template>
                        </el-autocomplete>
                  </div>`,
            mounted() {
                this.items = DataService.getEmployeesData();
            },
            methods: {
                querySearch(queryString, cb) {
                  var items = this.items;
                  var results = queryString ? items.filter(this.createFilter(queryString)) : items;
                  // 无法设置动态内容的位置,可以动态添加gcUIElement
                  // setTimeout(() => {
                  //   let popDiv = document.getElementsByClassName("my-autocomplete");
                  //   if(popDiv){
                  //   popDiv.setAttribute("gcUIElement", "gcEditingInput");
                  //   }
                  // }, 500);
                  // 调用 callback 返回建议列表的数据
                  cb(results);
                },
                createFilter(queryString) {
                  return (restaurant) => {
                  return (restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
                  };
                },
                handleSelect(item) {
                  console.log(item);
                },
                handleIconClick(ev) {
                  console.log(ev);
                }
            }
      };

      // create component constructor
      const AutoCompleteCtor = Vue.extend(AutoCompleteComponent);
      this.vm = new AutoCompleteCtor({
      propsData: {
          cellStyle: {width: width+"px"}
      }
      }).$mount(editorContext.firstChild);
    }
    return editorContext;
};
AutoComplateCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect) {
    // 给定一个最小编辑区域大小
    let width = cellRect.width > 180 ? cellRect.width : 180;
    let height = cellRect.height > 40 ? cellRect.height : 40;
    return {width: width, height: height};
};
AutoComplateCellType.prototype.getEditorValue = function (editorContext) {
    // 设置组件默认值
    if (this.vm) {
      return this.vm.text;
    }
};
AutoComplateCellType.prototype.setEditorValue = function (editorContext, value) {
    // 获取组件编辑后的值
    if (editorContext) {
      this.vm.text = value;
    }
};
AutoComplateCellType.prototype.deactivateEditor = function (editorContext, context) {
    // 销毁组件
    this.vm.$destroy();
    this.vm = undefined;
};


export {AutoComplateCellType};
最后,按照一般使用单元格类型的方式设置即可
import {AutoComplateCellType} from "../static/autocomplateCellType"var sheet = spread.getActiveSheet();
      sheet.getCell(1, 1).value("A").backColor("lightblue")
      sheet.setCellType(1, 1, new AutoComplateCellType())



效果如图:

小鱼 发表于 2021-10-11 15:49:38

React的 autocomplete或者其他组件有相似例子吗? 这个input框里可以加前缀icon吗

小鱼 发表于 2021-10-11 16:24:38

查了一下ElementUI,里面没有找到 <el-atuocomplete>组件,我搜的2.14.1 版本

Derrick.Jiao 发表于 2021-10-11 16:55:17

小鱼 发表于 2021-10-11 16:24
查了一下ElementUI,里面没有找到 组件,我搜的2.14.1 版本

请看这个例子


https://element.eleme.cn/2.14/#/zh-CN/component/input

小鱼 发表于 2021-10-13 09:53:06

hi,我自己定义了一个组件,和你的一样,react 内 用了antd的 dropdown,但是下拉框不能从单元格出来,隐藏到里面了,有什么建议吗

dexteryao 发表于 2021-10-13 10:19:36

小鱼 发表于 2021-10-13 09:53
hi,我自己定义了一个组件,和你的一样,react 内 用了antd的 dropdown,但是下拉框不能从单元格出来,隐藏 ...

DOM里看看是那个元素overflow设置了hidden,改一下,可以参考下面代码
AutoComplateCellType.prototype.createEditorElement = function (context, cellWrapperElement) {
cellWrapperElement.style.overflow = 'visible'

梅梅梅梅 发表于 2022-7-22 17:20:12

您好,我用的版本9,为什么new GC.Spread.Sheets.CellTypes.Base();这个方法报错,显示没有CellTypes方法?

Lynn.Dou 发表于 2022-7-22 18:17:59

注意到关于此问题您另开了新帖,后续在新帖中交流:
https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=151077

sanyue 发表于 2023-5-31 19:55:44

这样写element-ui autoconplete自身的样式都失效了是为什么?变成这样了

BattleHawk76 发表于 2023-11-21 21:39:43

这个自定义的化每次都要写一个js方法吗?有没有更方便的方法?直接通过vue的h函数渲染上去.能够将这行的数据暴露出来让组件能够操作数据
页: [1] 2
查看完整版本: 使用VUE组件创建SpreadJS自定义单元格(一)