2

As Vue, which has been at the top of the popular framework rankings for the past five years, part of what everyone will learn is the use of components. The modularization of front-end development can make the code logic simpler and clearer, and the scalability of the project is greatly enhanced. For Vue, the manifestation of modularity is concentrated on components, and modularization is achieved in units of components.

The way we usually use components is to register global components through the Vue.component method before instantiating the Vue object.

// 告诉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' }
    ]
  }
})

Among the many components, as an essential office spreadsheet, it also occupies an important position in the front-end components. In addition to displaying data in the form of a table, the spreadsheet also has a very important function, which is to support custom function expansion and various customized data display effects, such as checkbox, Radio button, etc.; it also needs to be realized when the cell enters the editing state When, use the drop-down menu (or other input controls) to enter the effect. We call it a "custom cell", a component that is embedded in a component. SpreadJS currently has 8 drop-down lists. Before opening the list, we only need to set the option data in the cell style. You can refer to the following code usage list:

Online experience address

  // 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"});

front-end spreadsheet is easy to use, but due to the frame life cycle and custom cell rendering logic issues, current technical means cannot directly use the components under the frame directly under the frame page in the form of template. In the previous content, we mentioned that you can use Svelte to use Web Conmponents to encapsulate components that can be used by other components.
In addition to the methods mentioned above, if we want to use custom cells in the Vue environment, we can consider using dynamic rendering to create and mount components, thereby injecting components into custom cells.

The following will show you how to create a custom cell using VUE components in the VUE project.

practice

First, start the runtime loading in the project, and add runtimeCompiler: true in vue.config.js.

    module.exports = {
        devServer: {
            port: 3000
        },
        <font color="#ff0000">runtimeCompiler: true</font>
      }

To quote ElementUI, you need to be careful to put the css reference of the element before the APP import, so that you can modify the style to cover the original project content.

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

Create AutoComplateCellType, the specific code is as follows, there are a few points to note.
1. The custom element needs to add the gcUIElement attribute. If the element or its parent element does not have this attribute, clicking the created component will directly exit the editing state and cannot be edited.
For ElementUI's autocomplete, the default drop-down options are injected into the body, and you need to set popper-append-to-body="false" in the component template, so that the pop-up drop-down options are rendered in the Div of gcUIElement.
If there is no similar option for other components, you can also follow up the actual situation and add the gcUIElement attribute when it pops up.
2. Use this.vm of the dynamically mounted component to set and get the value of the cell.
3. Destroy the component in 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: 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};

The effect is as follows:

A perfect cell is freshly baked~

The method introduced here is just one of many implementation schemes. If you have other better ideas, welcome to discuss ~

If you are interested in more interesting functions in other front-end spreadsheets, you can view SpreadJS for more examples to demonstrate .

We will continue to bring more serious and interesting content to everyone in the future~


葡萄城技术团队
2.7k 声望29.3k 粉丝

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。