本帖最后由 dexteryao 于 2024-5-14 14:25 编辑
对于VUE 2 的用户可以参考使用VUE组件创建SpreadJS自定义单元格(二)来创建可编辑的自定义单元格,在VUE 3中原有的$mount弃用由app.mount代替,这是个比较大的breaking change,同时也有人在问是否可以使用h函数方便的创建自定义单元格。接下来我们就看一看如何在VUE3中创建可编辑的自定义单元格。
首先,采用传统mount方式,将VUE2的Demo升级到VUE3. 示例代码时不太严格的TypeScript,大家根据实际情况可以调整。
第一步,还是封装组件
这里使用了element-plus的ElDatePicker组件,组件中设置teleported为false,保证弹出的日期选择框是渲染在单元格的dom中而不是body里
如果使用的组件没有类似属性,出现了点击组件就退出编辑的问题,那就需要在组件生成后或者弹出后给组件添加gcUIElement
defineExpose 中暴露了方法给外部调用,这也是和VUE2的一个差异,VUE2中所有的属性和方法都可以随便调用。
- <script setup lang="ts">
- import { ref } from 'vue';
- const datePicker = ref<any>(null);
- const text = defineModel('text')
- const cellStyle = ref(null);
- const setFocus = function(){
- if(datePicker.value){
- datePicker.value.focus();
- }
- }
- const updateStyle = function(style: any){
- cellStyle.value = style;
- }
- defineExpose({
- text,
- updateStyle,
- setFocus
- })
- </script>
- <template>
- <el-date-picker
- ref="datePicker"
- v-model="text"
- type="date"
- placeholder="选择日期"
- popper-class="spread-date-picker-popper"
- :teleported="false"
- :style="cellStyle"
- />
- </template>
- <style scoped>
- </style>
复制代码
第二步,实现cellType,通过createApp 以及 实例的mount方法挂载组件
mount 的返回类型是ComponentPublicInstance,通过这个instance可以调用Expose的属性和方法,通过text设置日期、setFocus设置日期选择器自动获取焦点
- import * as GC from "@grapecity/spread-sheets"
- import DatePickerNew from './DatePickerNew.vue'
- import { createApp } from 'vue'
- import { ElDatePicker } from 'element-plus'
- class datePickerEditorNew extends GC.Spread.Sheets.CellTypes.Base {
- private _app: any = null;
- private _vm: any = null;
- public typeName: string = 'datePickerEditorNew'
- public createEditorElement(_context: any): HTMLElement {
- console.log('编辑器创建')
- let editorContext = document.createElement("div")
- editorContext.setAttribute("gcUIElement", "gcEditingInput");
- let editor = document.createElement("div");
- editorContext.appendChild(editor);
- return editorContext
- }
- public activateEditor(editorContext: HTMLElement, _cellStyle: any, _cellRect: any, _context?: any): void {
- this._app = createApp(DatePickerNew, {});
- this._app.use(ElDatePicker);
- this._vm = this._app.mount(editorContext.firstChild as HTMLElement);
- }
- public updateEditor(editorContext: HTMLElement, _cellStyle: any, cellRect: any, _context?: any): any {
- console.log('编辑器更新')
- let width = cellRect.width > 180 ? cellRect.width : 180;
- this._vm.updateStyle({ width: width + "px" });
- const rect = new GC.Spread.Sheets.Rect(cellRect.x, cellRect.y, cellRect.width, cellRect.height);
- (editorContext as any).parentElement.parentElement.style.overflow = "visible"
- return rect;
- }
- public getEditorValue(_editorContext: HTMLElement, _context?: any): any {
- return this._vm.text;
- }
- public setEditorValue(_editorContext: HTMLElement, value: any, _context?: any): void {
- if (this._app) {
- this._vm.text = value;
- }
- }
- public deactivateEditor(_editorContext: HTMLElement, _context?: any): void {
- console.log('组件销毁')
- this._app.unmount();
- this._app = null;
- this._vm = null;
- }
- public isEditingValueChanged(oldValue: any, newValue: any, _context?: any): boolean {
- return oldValue !== newValue;
- }
- public focus(_editorContext: HTMLElement, _context?: any): void {
- this._vm.setFocus();
- console.log("CellType Focus")
- }
- }
- export { datePickerEditorNew }
复制代码
使用时和vue2没有区别
- sheet.getCell(2, 1).value("2024/8/1").cellType(new datePickerEditorNew())
复制代码 效果如下:
以上是完整流程的创建,在SpreadJS单元格各个生命周期方法中也掉用VUE组件instance的属性和方法,总体上有些繁琐。
如果只是简单的封装可以使用h函数快速创建一个VNode进行挂载显示,更加灵活简单。
和上面的方法主要区别是在activateEditor中通过render和h函数在创建的dom上渲染vnode。
vnode的赋值和取值直接在这里操作_text完成,没有全局的app和vm。
销毁时也是通过render渲染null
- import * as GC from "@grapecity/spread-sheets"
- import { h, ref, render, nextTick, defineComponent} from 'vue'
- import { ElDatePicker } from 'element-plus'
- class datePickerEditorRender extends GC.Spread.Sheets.CellTypes.Base {
- private _text: any = "";
- public typeName: string = 'datePickerEditorRender'
- public createEditorElement(_context: any): HTMLElement {
- console.log('编辑器创建')
- let editorContext = document.createElement("div")
- editorContext.setAttribute("gcUIElement", "gcEditingInput");
- let editor = document.createElement("div");
- editorContext.appendChild(editor);
- return editorContext
- }
- public activateEditor(editorContext: HTMLElement, _cellStyle: any, cellRect: any, context?: any): void {
- if (editorContext) {
- const { row, col, sheet } = context
- const cell = sheet.getCell(row, col)
- const value = cell.value()
- let cellType = this;
- let width = cellRect.width > 180 ? cellRect.width : 180;
- const pickerCom = defineComponent({
- data() {
- return { text: value}
- },
- mounted() {
- nextTick(() => { this.setFocus() });
- },
- methods: {
- setFocus() {
- (this as any).datePicker.value.focus();
- }
- },
- render() {
- (this as any).datePicker = ref<any>(null);
- const vnode = h(ElDatePicker, {
- modelValue: this.text,
- "onUpdate:modelValue": (val: any) => {
- this.text = val;
- cellType._text = val
- },
- teleported: false,
- style: { width: width + "px" },
- ref: (this as any).datePicker
- });
- return vnode;
- },
- expose: ["text", "setFocus"]
- });
- const vNode = h(pickerCom);
- render(vNode, editorContext.firstChild as HTMLElement)
- }
- }
- public updateEditor(editorContext: HTMLElement, _cellStyle: any, cellRect: any, _context?: any): any {
- console.log('编辑器更新')
- const rect = new GC.Spread.Sheets.Rect(cellRect.x, cellRect.y, cellRect.width, cellRect.height);
- (editorContext as any).parentElement.parentElement.style.overflow = "visible"
- return rect;
- }
- public getEditorValue(_editorContext: HTMLElement, _context?: any): any {
- return this._text;
- }
- public setEditorValue(_editorContext: HTMLElement, value: any, _context?: any): void {
- this._text = value
- }
- public deactivateEditor(editorContext: HTMLElement, _context?: any): void {
- console.log('组件销毁')
- render(null, editorContext.firstChild as HTMLElement)
- }
- public isEditingValueChanged(oldValue: any, newValue: any, _context?: any): boolean {
- return oldValue !== newValue;
- }
- }
- export { datePickerEditorRender }
复制代码
使用CellType代码相同
- sheet.getCell(4, 1).value("2024/8/1").cellType(new datePickerEditorRender())
复制代码 使用render和h函数注入组件,不需要再写额外的组件,也不需要create新的vue实例。
但是由于vNode不返回组件实例,无法在celltype其他方法中调用组件内部方法,组件的focus只能自己延时完成。
以上便是在VUE3中创建可编辑自定义单元格的示例,有问题大家可以跟帖讨论。
|
|