zoukankan      html  css  js  c++  java
  • 使用VUE组件创建SpreadJS自定义单元格(一)

    作为近五年都冲在热门框架排行榜首的Vue,大家一定会学到的一部分就是组件的使用。前端开发的模块化,可以让代码逻辑更加简单清晰,项目的扩展性大大加强。对于Vue而言,模块化的体现集中在组件之上,以组件为单位实现模块化。

    通常我们使用组件的方式是,在实例化Vue对象之前,通过Vue.component方法来注册全局的组件。

    // 告诉Vue,现在需要组件 todo-item,配置如下,包含props和template
    Vue.component('todo-item', {
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    }) 
    // 实例化一个Vue对象,挂载在#app-7元素下,定它的属性,数组groceryList 
    var app7 = new Vue({
      el: '#app-7',
      data: {
        groceryList: [
          { text: 'Vegetables' },
          { text: 'Cheese' },
          { text: 'Whatever else humans are supposed to eat' }
        ]
      }
    })
    
    

    在众多组件之中,作为办公必备的电子表格,在前端组件中也占据了重要地位。除了以表格的形式展示数据,电子表格还有一个非常重要的功能,即支持自定义功能拓展和各种定制化的数据展示效果,比如checkbox,Radio button等;还需要实现当单元格进入编辑状态时,使用下拉菜单(或其他输入控件)输入的效果。我们称之为"自定义单元格",一种嵌入组件内的组件。SpreadJS目前拥有8种下拉列表,在打开列表之前,我们只需要在单元格样式中设置选项数据。 你可以参考以下代码使用列表:

    在线体验地址

      // The way of click the dropdown icon to open list. 
       var style = new GC.Spread.Sheets.Style();
       style.cellButtons = [
           {
               imageType: GC.Spread.Sheets.ButtonImageType.dropdown,
               command: "openList",
               useButtonStyle: true,
           }
       ];
       style.dropDowns = [
           {
               type: GC.Spread.Sheets.DropDownType.list,
               option: {
                   items: [
                       {
                           text: 'item1',
                           value: 'item1'
                       },
                       {
                           text: 'item2',
                           value: 'item2'
                       },
                       {
                           text: 'item3',
                           value: 'item3'
                       },
                       {
                           text: 'item4',
                           value: 'item4'
                       }
                   ],
               }
           }
       ];
       sheet.setText(2, 1, "Vertical text list");
       sheet.setStyle(3, 1, style);
    
       // The way open list with command rather then clicking the dropdown button.
       spread.commandManager().execute({cmd:"openList",row:3,col:1,sheetName:"Sheet1"});
    
    
    

    前端电子表格固然好用, 但由于框架生命周期以及自定义单元格渲染逻辑的问题,目前的技术手段无法直接在框架页面下直接通过template的方式使用框架下的组件。在之前的内容中,我们提到了可以使用Svelte使用Web Conmponents封装其他组件可以使用的组件。
    除了上面提到的方法之外,我们如果想在Vue环境下使用自定义单元格,可以考虑使用持动态渲染的方式来创建和挂载组件,从而将组件注入自定义单元格。

    下面为大家演演示如何在VUE项目中,创建一个使用VUE 组件的自定义单元格。

    实践

    首先,在项目中开启运行时加载,在vue.config.js中添加runtimeCompiler: true。

        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")[0];
                        //   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+"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, 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};
    
    
    

    效果如图:

    一个完美的单元格新鲜出炉~

    这里介绍的方式只是诸多实现方案的一种。如果大家有其他更好的想法方法,欢迎一起讨论 ~

    如果你对其他更多前端电子表格中有趣功能感兴趣,可以查看 SpreadJS更多实例演示

    我们也会在之后,持续为大家带来更多带来更多严肃和有趣的内容 ~



    本文是由葡萄城技术开发团队发布,转载请注明出处:葡萄城官网


  • 相关阅读:
    LeetCode 1275. 找出井字棋的获胜者 Find Winner on a Tic Tac Toe Game
    LeetCode 307. 区域和检索
    LeetCode 1271 十六进制魔术数字 Hexspeak
    秋实大哥与花 线段树模板
    AcWing 835. Trie字符串统计
    Leetcode 216. 组合总和 III
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 高级结果映射 ResultMap Association Collection
    Mybatis 高级结果映射 ResultMap Association Collection
  • 原文地址:https://www.cnblogs.com/powertoolsteam/p/15774370.html
Copyright © 2011-2022 走看看