vue3 + Luckysheet的使用

vue3 + Luckysheet的使用

  1. 功能列表:
  • 导入excel
  • 导出excel
  • 设置数据格式
  • 自动保存(表头修改时不能自动保存。所以,目前编写模板不做自动保存功能。填报时,做自动保存功能)
  • 冻结区间
  • 工作表保护 及 区域范围
  • 数据验证 如数字 数据验证
  • 。。。
  1. 问题修复
  • 修改 luckysheet 源代码。问题修复:设置单元格格式为数字的千分位格式时,数据验证,type设置为数字时,验证不通过
    在这里插入图片描述在这里插入图片描述
    注:修改完luckysheet源码后,需要重新打包再引用到项目中。

一、 Luckysheet的介绍

Luckysheet,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。

二、Luckysheet简单使用

luckysheet源码地址:https://gitee.com/mengshukeji/Luckysheet/

  1. 下载源码,查看package.json文件,打包生成dist文件夹 npm run build
  2. 将打包生成的dist文件,重命名为luckysheet_dist,放在 根目录/public文件夹下
  3. 在index.html中引入luckysheet
<link rel='stylesheet' href='/luckysheet_dist/plugins/css/pluginsCss.css' />
<link rel='stylesheet' href='/luckysheet_dist/plugins/plugins.css' />
<link rel='stylesheet' href='/luckysheet_dist/css/luckysheet.css' />
<link rel='stylesheet' href='/luckysheet_dist/assets/iconfont/iconfont.css' />
<script src="/luckysheet_dist/plugins/js/plugin.js"></script>
<script src="/luckysheet_dist/luckysheet.umd.js"></script>

在这里插入图片描述

2.1 填报页面:配置文件

// src/utils/luckysheetConfig.js文件
import { updateTemplate } from "@/api/api.js";

/** luckysheet 职能预算填报 start */
/** 整体配置 start */
//配置项 作用于整个表格。特别的,单个sheet的配置项需要在options.data数组中
export const options = {
    container: "luckysheet", // 设定DOM容器的id:luckysheet为容器id
    title: "职能预算", // 设置表格名称
    lang: "zh", // 设定表格语言
    // 更多其它设置...
    data: [],
    column: 18, // 空表格默认的列数量
    row: 36, //空表格默认的行数据量
    showtoolbar: false, //是否显示工具栏
    showtoolbarConfig: {
        //自定义配置工具栏
        undoRedo: false, // 撤销重做,注意撤销重做是两个按钮,由这一个配置决定显示还是隐藏
        paintFormat: false, //格式刷
        currencyFormat: false, //货币格式
        percentageFormat: false, //百分比格式
        numberDecrease: false, // '减少小数位数'
        numberIncrease: false, // '增加小数位数'
        moreFormats: false, // '更多格式'
        font: false, // '字体'
        fontSize: false, // '字号大小'
        bold: false, // '粗体(Ctrl + B)'
        italic: false, // '斜体(Ctrl + I)'
        strikethrough: false, //删除线(Alt + Shift + 5)
        underline: false, // 下划线(Alt + Shift + 6)
        textColor: false, //文本颜色
        fillColor: false, // 单元格颜色
        border: false, // 边框
        mergeCell: false, //合并单元格
        horizontalAlignMode: false, //水平对齐方式
        verticalAlignMode: false, //垂直对齐方式
        textWrapMode: false, //换行方式
        textRotateMode: false, //文本旋转方式
        image: false, //插入图片
        link: false, //插入链接
        chart: false, //图表(图标隐藏,但是如果配置了chart插件,右击仍然可以新建图表)
        postil: false, //批注
        pivotTable: false, //数据透视表
        function: false, //公式
        frozenMode: false, //冻结方式
        sortAndFilter: false, //排序和筛选
        conditionalFormat: false, //条件格式
        dataVerification: false, //数据验证
        splitColumn: false, //分列
        screenshot: false, //截图
        findAndReplace: false, //查找替换
        protection: false, // 工作表保护
        print: false, //打印
    },
    showinfobar: false, //是否显示顶部信息栏
    showsheetbar: false, //是否显示底部sheet页按钮
    showsheetbarConfig: {
        //自定义配置底部sheet页按钮
        add: false, //新增sheet
        menu: false, //sheet管理菜单
        sheet: false, //sheet页显示
    },
    showstatisticBar: false, //是否显示底部计数栏
    showstatisticBarConfig: {
        //自定义配置底部计数栏
        count: true, //计数栏
        view: true, //打印视图
        zoom: true, //缩放
    },
    enableAddRow: false, //允许添加行
    enableAddBackTop: false, //允许回到顶部
    userInfo: false, //右上角的用户信息展示
    userMenuItem: [], //点击右上角的用户信息弹出的菜单
    myFolderUrl: "", //左上角 < 返回按钮的链接
    functionButton: "", //右上角功能按钮
    cellRightClickConfig: {
        //自定义配置单元格右击菜单
        copy: false, //复制
        copyAs: false, //复制为
        paste: false, //粘贴
        insertRow: false, //插入行
        insertColumn: false, //插入列
        deleteRow: false, //删除选中行
        deleteColumn: false, //删除选中列
        deleteCell: false, //删除单元格
        hideRow: false, //隐藏选中行和显示选中行
        hideColumn: false, //隐藏选中列和显示选中列
        rowHeight: false, //行高
        columnWidth: false, //列宽
        clear: false, //清除内容
        matrix: false, //矩阵操作选区
        sort: false, //排序选区
        filter: false, //筛选选区
        chart: false, //图表生成
        image: false, //插入图片
        link: false, //插入链接
        data: false, //数据验证
        cellFormat: false, //设置单元格格式
    },
    sheetRightClickConfig: {
        //自定义配置sheet页右击菜单
        delete: false, //删除
        copy: false, //复制
        rename: false, //重命名
        color: false, //更改颜色
        hide: false, //隐藏,取消隐藏
        move: false, //向左移,向右移
    },
    rowHeaderWidth: 46, //行标题区域的宽度,如果设置为0,则表示隐藏行标题
    columnHeaderHeight: 20, //列标题区域的高度,如果设置为0,则表示隐藏列标题
    sheetFormulaBar: true, //是否显示公示栏
    defaultFontSize: 11, //初始化默认字体大小
    limitSheetNameLength: true, //工作表重命名等场景下是否限制工作表名称的长度
    defaultSheetNameMaxLength: 31, //默认允许的工作表名最大长度
    // pager: { //分页器按钮设置
    //     pageIndex: 1, //当前的页码
    //     pageSize: 10, //每页显示多少行数据
    //     total: 50, //数据总行数
    //     selectOption: [10, 20], //允许设置每页行数的选项
    // },
    hook: {
        //钩子函数

        /** 单元格 start */
        /**进入单元格编辑模式之前触发。在选中了某个单元格且在非编辑状态下,通常有以下三种常规方法触发进入编辑模式 1.双击单元格 2.敲Enter键 3.使用API:enterEditMode
         * 参数:{ Array }[range]: 当前选区范围
         */
        cellEditBefore: (range) => {
            console.log("进入单元格编辑模式之前触发:cellEditBefore");
            console.log(`当前选区范围:`, range);
        },
        /**更新这个单元格值之前触发,return false 则不执行后续的更新。在编辑状态下修改了单元格之后,退出编辑模式并进行数据更新之前触发这个钩子
         * 参数:
         * {Number}[r]:单元格所在行数
         * {Number}[c]:单元格所在列数
         * {Object | String | Number}[value]:要修改的单元格内容
         * {Boolean}[isRefresh]:是否刷新整个表格
         */
        cellUpdateBefore: (r, c, value, isRefresh) => {
            console.log("更新这个单元格值之前触发:cellUpdateBefore");
            console.log(
                `单元格所在行:${r};单元格所在列:${c};是否刷新整个表格:${isRefresh};要修改的单元格内容: `,
                value
            );
        },
        /** 更新这个单元格后触发 */
        cellUpdated: (r, c, oldValue, newValue, isRefresh) => {
            console.log("cellUpdated:", r, c, oldValue, newValue);
            // 获取单元格的值
            const value = luckysheet.getCellValue(r, c, { type: "v" });
            console.log("更新后的值:", value, "对值进行数据验证");
        },
        /**单元格渲染前触发 return false 则不渲染该单元格
         * 参数:
         * {Object}[cell]:单元格对象
         * {Object}[position]:
         *  {Number}[r]:单元格所在行号
         *  {Number}[c]:单元格所在列号
         *  {Number}[start_r]:单元格左上角的水平坐标
         *  {Number}[start_c]:单元格左上角的垂直坐标
         *  {Number}[end_r]:单元格右下角的水平坐标
         *  {Number}[end_c]:单元格右下角的垂直坐标
         * {Object}[sheet]:当前sheet对象
         * {Object}[ctx]:当前画布的context
         */
        cellRenderBefore: (cell, position, sheet, ctx) => {
            // console.log('单元格渲染前触发:cellRenderBefore');
            // console.log('单元格对象:', cell, '单元格所在行号:', position.r, '单元格所在列号:', position.c, '单元格左上角的水平坐标:',
            //     position.start_r, '单元格左上角的垂直坐标:', position.start_c, '单元格右下角的水平坐标:', position.end_r, '单元格右下角的垂直坐标:', position.end_c,
            //     '当前sheet对象:', sheet, '当前画布的context:', ctx);
        },
        /**单元格渲染结束后触发,return false 则不渲染该单元格
         * 参数:
         *  {Object} [cell]:单元格对象
         *  {Object} [position]:
         *   {Number} [r]:单元格所在行号
         *   {Number} [c]:单元格所在列号
         *   {Number} [start_r]:单元格左上角的水平坐标
         *   {Number} [start_c]:单元格左上角的垂直坐标
         *   {Number} [end_r]:单元格右下角的水平坐标
         *   {Number} [end_c]:单元格右下角的垂直坐标
         * {Object} [sheet]:当前sheet对象
         * {Object} [ctx]:当前画布的context
         *
         */
        cellRenderAfter: () => {},
        /**所有单元格渲染之前执行的方法
         * 参数:
         *  {Object}[data]:当前工作表二维数组数据
         *  {Object}[sheet]:当前sheet对象
         *  {Object}[ctx]:当前画布的context
         */
        cellAllRenderBefore: (data, sheet, ctx) => {
            // console.log('当前工作表二维数组数据:', data);
        },
        /**行标题单元格渲染前触发,return false 则不渲染行标题
         * 参数:
         *  {String}[rowNum]:行号
         *  {Object}[position]:
         *   {Number}[r]:单元格所在行号
         *   {Number}[top]:单元格左上角的垂直坐标
         *   {Number}[width]:单元格宽度
         *   {Number}[height]:单元格高度
         * {Object}[ctx]:当前画布的context
         */
        rowTitleCellRenderBefore: () => {},
        /** 行标题单元格渲染后触发 return false 则不渲染行标题*/
        rowTitleCellRenderAfter: () => {},
        /** 列标题单元格渲染前触发 return false 则不渲染列标题 */
        columnTitleCellRenderBefore: () => {},
        /** 列标题单元格渲染后触发,return false 则不渲染列标题 */
        columnTitleCellRenderAfter: () => {},
        /** 单元格 end */

        /** 鼠标钩子 start */
        /** 单元格点击前的事件,return false 则终止之后的点击操作 */
        cellMousedownBefore: (cell, position, sheet, ctx) => {},
        /** 单元格点击后的事件,return false 则终止之后的点击操作 */
        cellMousedownAfter: (cell, position, sheet, ctx) => {},
        /** 鼠标移动事件,可通过cell判断鼠标停留在哪个单元格 */
        sheetMousemove: () => {},
        /** 鼠标按钮释放事件,可通过cell判断鼠标停留在哪个单元格 */
        sheetMouseup: () => {},
        /** 鼠标滚动事件 */
        scroll: (position) => {},
        /** 鼠标拖拽文件到Luckysheet内部的结束事件 */
        cellDragStop: () => {},
        /** 鼠标钩子 end */

        /** 选区操作 (包括单元格) start */
        /** 框选或者设置选区后触发 参数:{Object}[sheet]:当前选区对象 {Object | Array}[range]:选区范围,可能为多个选区 */
        rangeSelect: (sheet, range) => {
            console.log("选区范围:", range);
        },
        /** 移动选区前,包括单个单元格 */
        rangeMoveBefore: (range) => {},
        /** 移动选区后,包括单个单元格 */
        rangeMoveAfter: (range) => {},
        /** 选区修改前 */
        rangeEditBefore: (range, data) => {},
        /** 选区修改后 */
        rangeEditAfter: (range, oldData, newData) => {},
        /** 选区复制前 */
        rangeCopyBefore: (range, data) => {},
        /** 选区复制后 */
        rangeCopyAfter: (range, data) => {},
        /** 选区粘贴前 */
        rangePasteBefore: (range, data) => {},
        /** 选区粘贴后 */
        rangePasteAfter: (range, originData, pasteData) => {},
        /** 选区剪切前 */
        rangeCutBefore: (range, data) => {},
        /** 选区剪切后 */
        rangeCutAfter: (range, data) => {},
        /** 选区删除前 */
        rangeDeleteBefore: (range, data) => {},
        /** 选区删除后 */
        rangeDeleteAfter: (range, data) => {},
        /** 选区清除前 */
        rangeClearBefore: (range, data) => {},
        /** 选区清除后 */
        rangeClearAfter: (range, data) => {},
        /** 选区下拉前 */
        rangePullBefore: (range) => {},
        /** 选区下拉后 */
        rangePullAfter: (range) => {},
        /** 选区操作 (包括单元格) end */

        /** 工作表 start */
        /** 创建sheet页前触发,sheet页新建也包含数据透视表新建 */
        sheetCreatekBefore: () => {},
        /** 创建sheet页后触发,sheet页新建也包含数据透视表新建 */
        sheetCreateAfter: (sheet) => {},
        /** sheet移动前 */
        sheetMoveBefore: (i, order) => {},
        /** sheet移动后 */
        sheetMoveAfter: (i, oldOrder, newOrder) => {},
        /** sheet删除前 */
        sheetDeleteBefore: (sheet) => {},
        /** sheet删除后 */
        sheetDeleteAfter: (sheet) => {},
        /** sheet修改名称前 */
        sheetEditNameBefore: (i, name) => {},
        /** sheet修改名称后 */
        sheetEditNameAfter: (i, oldName, newName) => {},
        /** sheet修改颜色前 */
        sheetEditColorBefore: (i, color) => {},
        /** sheet修改颜色后 */
        sheetEditColorAfter: (i, oldColor, newColor) => {},
        /** sheet缩放前 */
        sheetZoomBefore: (i, zoom) => {},
        /** sheet缩放后 */
        sheetZoomAfter: (i, oldZoom, newZoom) => {},
        /** 激活工作表前 */
        sheetActivate: (i, isPivotInitial, isNewSheet) => {},
        /** 工作表从活动状态转为非活动状态前 */
        sheetDeactivateBefore: (i) => {},
        /** 工作表从活动状态转为非活动状态后 */
        sheetDeactivateAfter: (i) => {},
        /** 工作表 end */

        /** 工作薄 start */
        /** 表格创建之前触发 */
        workbookCreateBefore: (book) => {},
        /** 表格创建之后触发 */
        workbookCreateAfter: (book) => {},
        /** 表格销毁之前触发 */
        workbookDestroyBefore: (book) => {},
        /** 表格销毁之后触发 */
        workbookDestroyAfter: (book) => {},
        /** 协同编辑中的每次操作后执行的方法,监听表格内容变化,即客户端每执行一次表格操作,Luckysheet将这次操作存到历史记录中后触发,撤销重做时因为也算一次操作,也会触发此钩子函数 参数:{Object}[operate]:本次操作的历史记录信息,根据不同的操作,会有不同的历史记录 */
        updated: (operate) => {
            console.log("luckysheetUpdatedHook", operate);
            // 监听更新,并在3s后自动保存
            //     if (autoSave) {
            //         console.log(autoSave, "autoSave");
            //         clearTimeout(autoSave);
            //         $(luckysheet_info_detail_save).text("已修改");
            //         autoSave = setTimeout(() => {
            //             const excel = luckysheet.getAllSheets();
            //             // 去除临时数据,减小体积
            //             for (const i in excel) {
            //                 excel[i].ddata = undefined;
            //             }
            //             // $.post('http://127.0.0.0:8081/setWorkBook', {
            //             //     jsonExcel: JSON.stringify(excel)
            //             // }, () => {
            //             //     $(luckysheet_info_detail_save).text('已保存')
            //             // })
            //         }, 1 * 300);
            //         return true;
            //     }
        },
        /** resize 执行之后 */
        resized: (size) => {},
        /** 工作薄 end */

        /** 冻结 start */
        /** 设置冻结前 */
        frozenCreateBefore: (frozen) => {},
        /** 设置冻结后 */
        frozenCreateAfter: (frozen) => {},
        /** 取消冻结前 */
        frozenCancelBefore: (frozen) => {},
        /** 取消冻结后 */
        frozenCancelAfter: (frozen) => {},
        /** 冻结 end */

        /** 分页器 start */
        /** 点击分页按钮回调函数,返回当前页码 */
        onTogglePager: (page) => {},
        /** 分页器 end */
    },
    /** 协同编辑 start */
    // loadUrl: gridKey:工作簿的唯一标识, 加载luckysheet数据的地址
    // loadUrl: 'http://localhost:8081/getWorkBook?gridKey=1',
    // loadUrl: "/baseURL/excel",
    // updateUrl: '', //后台websocket地址
    allowUpdate: true,
    /** 协同编辑 end */
};
/** 整体配置 end */
/** luckysheet 职能预算填报 end */

2.2 填报页面:页面使用

// luckysheet/index.vue页面
<template>
    <div class="lucky">
        <div class="operate" @click="exitEditMode">
            <span>{{ luckysheet_save_text }}</span>
            <el-button type="primary">保存</el-button>
            <el-button @click="exportExcel" type="primary">导出excel</el-button>
        </div>
        <div id="luckysheet"></div>
    </div>
</template>

<script setup>
import { onMounted, reactive, onUnmounted, ref } from "vue";
import { options } from "@/utils/luckysheetConfig.js";
import { getAuth, updateTemplate } from "@/api/api.js";
import axios from "axios";
import { globalName } from "../../utils/global";

// const Excel = require("exceljs");
// import Excel from "exceljs";
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
import LuckySheet from "../../components/LuckySheet.vue";

let templateData = reactive([]);

onMounted(() => {
    // $(function () {
    // 初始化表格
    // luckysheet.create(options);
    // })

    getTemplateJson();

    // 接口请求已调通 ~~~
    // getAuth().then(res => {
    //     console.log(res, 2322)
    // })
});

onUnmounted(() => {
    exitEditMode();
});

// 获取模板文件 获取public文件夹下指定json文件内容
const getTemplateJson = () => {
    axios.get(`/${globalName}/json/luckysheet.json`).then((res) => {
        if (res.status === 200) {
            templateData = res.data;
            createLuckysheet();
            init();
        }
    });
};

// 创建luckysheet
const createLuckysheet = () => {
    templateData[0] = {
        ...templateData[0],
        // frozen: { //冻结行列配置
        //     type: 'rangeBoth',
        //     range: { row_focus: 0, column_focus: 9 } //冻结行列到'B2'选区
        // },
        // // row: 90, //行数
        // // column: 26, //列数
        // config: {
        //     ...templateData[0].config,
        //     authority: {
        //         //工作表保护
        //         //当前工作表的权限配置
        //         selectLockedCells: 1, //选定锁定单元格
        //         selectunLockedCells: 1, //选定解除锁定的单元格
        //         formatCells: 0, //设置单元格格式
        //         formatColumns: 0, //设置列格式
        //         formatRows: 0, //设置行格式
        //         insertColumns: 0, //插入列
        //         insertRows: 0, //插入行
        //         insertHyperlinks: 0, //插入超链接
        //         deleteColumns: 0, //删除列
        //         deleteRows: 0, //删除行
        //         sort: 0, //排序
        //         filter: 1, //使用自动筛选
        //         usePivotTablereports: 0, //使用数据透视表和报表
        //         editObjects: 0, //编辑对象
        //         editScenarios: 0, //编辑方案
        //         sheet: 1, //如果为1或true,则该工作表受到保护;如果为0或false,则该工作表不受保护。
        //         hintText: "", //弹窗提示的文字
        //         algorithmName: "None", //加密方案:MD2,MD4,MD5,RIPEMD-128,RIPEMD-160,SHA-1,SHA-256,SHA-384,SHA-512,WHIRLPOOL
        //         saltValue: null, //密码解密的盐参数,为一个自己定的随机数值
        //         allowRangeList: [
        //             {
        //                 //区域保护
        //                 name: "area", //名称
        //                 password: "", //密码
        //                 hintText: "", //提示文字
        //                 algorithmName: "None", //加密方案:MD2,MD4,MD5,RIPEMD-128,RIPEMD-160,SHA-1,SHA-256,SHA-384,SHA-512,WHIRLPOOL
        //                 saltValue: null, //密码解密的盐参数,为一个自己定的随机数值
        //                 sqref: "$O$1:$Z$18", //区域范围
        //             },
        //         ],
        //     },
        // },
        // 数据验证
        // dataVerification: {
        //     "1_14": {
        //         type: "number_decimal",
        //         type2: "bw",
        //         value1: "0",
        //         value2: "100000",
        //         checked: false, //是否勾选中复选框;type为checkbox时需配置;
        //         remote: false, //自动远程获取选项
        //         prohibitInput: false, //输入数据无效时禁止输入;默认为false;
        //         hintShow: false, //选中单元格时显示提示语;默认为false;
        //         hintText: "", //提示语文本,hintShow为true时需配置;
        //     },
        //     "1_15": {
        //         type: "number", //数字
        //         type2: "bw",
        //         value1: "0", //最小值为0
        //         value2: "100000",//最大值为1000000
        //         checked: false, //是否勾选中复选框;type为checkbox时需配置;
        //         remote: false, //自动远程获取选项
        //         prohibitInput: false, //输入数据无效时禁止输入;默认为false;
        //         hintShow: true, //选中单元格时显示提示语;默认为false;
        //         hintText: "请输入1到100000之间的数字", //提示语文本,hintShow为true时需配置;
        //     },
        // }
    };

    luckysheet.create({
        ...options,
        data: templateData,
        hook: {
            updated: handleUpdate,
        },
    });
};

// 初始化
const init = () => {
    getAllSheets();
    const sheetData = getSheetData();
    // console.log("sheetData", sheetData);
    transToCellData();
    getConfig();
    getSheetCellValue();
    // setDataVerification();
};

// 所有工作表的配置信息
const getAllSheets = () => {
    // 获取所有工作表的配置信息
    const allSheets = luckysheet.getAllSheets();
    console.log("所有工作表的配置信息:", allSheets);
    return allSheets;
};
// 工作表数据
const getSheetData = () => {
    const luckysheetdata = luckysheet.getSheetData({ order: 0 });
    console.log("工作表数据", luckysheetdata);
    return luckysheetdata;
};

// data => celldata,data二维数组数据转化成 {r,c,v}格式一维数组
const transToCellData = () => {
    const cellData = luckysheet.transToCellData(getSheetData());
    console.log("data=>celldata", cellData);
    return cellData;
};
// celldata => data, celldata一维数组数据转化成表格所需二维数组
const transToData = () => {
    const data = luckysheet.transToData(transToCellData());
    console.log("celldata=>data", data);
};

// 工作表配置
const getConfig = () => {
    const config = luckysheet.getConfig({ order: 0 });
    console.log("工作表配置", config);
    return config;
};

// 单元格的值
const getCellValue = (row = 0, col = 0) => {
    const cellValue = luckysheet.getCellValue(row, col, { type: "v" });
    console.log("单元格的值", cellValue);
    return getCellValue;
};

// 获取表中每个单元格的值
const getSheetCellValue = () => {
    const allSheets = getAllSheets();
    const rows = allSheets[0].data.length; //行
    const cols = allSheets[0].data[0].length; //列
    // 获取每个单元格的值
    const arr = new Array(rows);
    for (let i = 0; i < rows; i++) {
        arr[i] = new Array(cols);
    }
    // 追个追加到二维数组中
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            arr[i][j] = luckysheet.getCellValue(i, j, { type: "v" });
        }
    }
    console.log("每个单元格的值", arr);
    return arr;
};

// 指定工作表范围设置数据验证功能,并设置参数
const setDataVerification = () => {
    const optionItem = {
        type: "number", //数字  number_decimal:小数
        type2: "bw",
        value1: "0", //最小值为0
        value2: "1000000", //最大值为1000000
        checked: false, //是否勾选中复选框;type为checkbox时需配置;
        remote: false, //自动远程获取选项
        prohibitInput: false, //输入数据无效时禁止输入;默认为false;
        hintShow: false, //选中单元格时显示提示语;默认为false;
        hintText: "请输入0到1000000之间的整数", //提示语文本,hintShow为true时需配置;
    };
    const setting = {
        range: { row: [1, 74], column: [14, 26] }, //区间
    };
    luckysheet.setDataVerification(optionItem, setting, (res) => {
        console.log(res, 1);
    });
};

// toJson
const toJson = () => {
    const json = luckysheet.toJson();
};

const closeWebsocket = () => {
    luckysheet.closeWebsocket();
};

// 返回所有表格数据结构的一维数组
const getLuckysheetfile = () => {
    // 调试使用,不适用初始化表格
    const luckysheetfile = luckysheet.getLuckysheetfile();
};

// 退出编辑模式
const exitEditMode = () => {
    // 自动退出编辑模式的操作,主要是为了触发自动保存单元格
    luckysheet.exitEditMode();
};

const luckysheet_save_text = ref("已保存");

// 自动保存
const autoSave = () => {
    //调用luckysheet提供的API获取当前编辑的表格数据
    const allSheets = getAllSheets();
    // 去除临时数据,减小体积
    for (const i in allSheets) {
        allSheets[i].data = undefined;
    }
    let params = {
        jsonData: JSON.stringify(allSheets),
        targetFilePath:
            process.env.NODE_ENV === "development"
                ? "D://hhkj_project//function-budget//public//json//luckysheet.json"
                : "D://apache-tomcat-7.0.64-8080-mis//webapps//functionalBudget//json//template.json",
    };
    updateTemplate(params).then((res) => {
        if (res.success) {
            // $(luckysheet_info_detail_save).text("已保存");
            luckysheet_save_text.value = "已保存";
        }
    });
};

const handleUpdate = (operate) => {
    console.log("绑定更新处理函数", operate);
    luckysheet_save_text.value = "保存中...";
    autoSave();
};

/**
 * 协同
 * loadUrl:
 *  $.post(loadurl,{"gridKey":server.gridKey},function(d){})
 */

/** 导出excel 功能 start 已完成 */
//导出
const exportExcel = async () => {
    try {
        //获取luckysheet的数据
        // const sheetsData = luckysheet.getLuckysheetfile()[0].sheets;
        const sheetsData = luckysheet.getAllSheets();
        // 创建工作簿
        const workbook = new ExcelJS.Workbook();

        // 遍历每张表单数据,添加到工作簿
        sheetsData.forEach((sheet) => {
            if (sheet.data.length === 0) return; // 跳过空表

            const worksheet = workbook.addWorksheet(sheet.name);
            setStyleAndValue(sheet.data, worksheet);
            setMerge(sheet.config.merge, worksheet);
            setBorder(sheet.config.borderInfo, worksheet);
        });

        // 将工作簿写入Buffer
        const buffer = await workbook.xlsx.writeBuffer();

        // 使用file-saver保存文件
        saveAs(
            new Blob([buffer], { type: "application/octet-stream" }),
            "output.xlsx"
        );
    } catch (error) {
        console.error("导出Excel时发生错误:", error);
    }
};
//合并单元格
const setMerge = (luckyMerge = {}, worksheet) => {
    const mergearr = Object.values(luckyMerge);
    mergearr.forEach(function (elem) {
        // elem格式:{r: 0, c: 0, rs: 1, cs: 2}
        // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)
        worksheet.mergeCells(
            elem.r + 1,
            elem.c + 1,
            elem.r + elem.rs,
            elem.c + elem.cs
        );
    });
};
//边框
const setBorder = (luckyBorderInfo, worksheet) => {
    if (!Array.isArray(luckyBorderInfo)) return;
    luckyBorderInfo.forEach(function (elem) {
        let border = borderConvert();
        worksheet.getCell(
            elem.value.row_index + 1,
            elem.value.col_index + 1
        ).border = border;
    });
};
//表格样式
const setStyleAndValue = (cellArr, worksheet) => {
    if (!Array.isArray(cellArr)) return;
    cellArr.forEach(function (row, rowid) {
        row.every(function (cell, columnid) {
            if (!cell) return true;
            let fill = fillConvert(cell.bg);
            let font = fontConvert(
                cell.ff,
                cell.fc,
                cell.bl,
                cell.it,
                cell.fs,
                cell.cl,
                cell.ul
            );
            let alignment = alignmentConvert(
                cell.vt,
                cell.ht,
                cell.tb,
                cell.tr
            );
            let value;
            if (cell.f) {
                value = { formula: cell.f, result: cell.v };
            } else {
                // value = cell.v;
                value = luckysheet.getCellValue(rowid, columnid, {
		          type: "v",
		        });
            }
            let target = worksheet.getCell(rowid + 1, columnid + 1);
            target.fill = fill;
            target.font = font;
            target.alignment = alignment;
            // target.value = value;
            return true;
        });
    });
};
const fillConvert = (bg) => {
    // if (!bg) {
    //     return {};
    // }
    let fill = {
        type: "pattern",
        pattern: "solid",
        fgColor: { argb: "FFFFFF" }, // 默认白色背景
    };
    if (bg) fill.fgColor = { argb: bg.replace("#", "") };
    return fill;
};

const fontConvert = (
    ff = 0,
    fc = "#000000",
    bl = 0,
    it = 0,
    fs = 10,
    cl = 0,
    ul = 0
) => {
    // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
    const luckyToExcel = {
        0: "微软雅黑",
        1: "宋体(Song)",
        2: "黑体(ST Heiti)",
        3: "楷体(ST Kaiti)",
        4: "仿宋(ST FangSong)",
        5: "新宋体(ST Song)",
        6: "华文新魏",
        7: "华文行楷",
        8: "华文隶书",
        9: "Arial",
        10: "Times New Roman ",
        11: "Tahoma ",
        12: "Verdana",
        num2bl: function (num) {
            return num === 0 ? false : true;
        },
    };

    let font = {
        name: luckyToExcel[ff],
        family: 1,
        size: fs,
        color: { argb: fc.replace("#", "") },
        bold: luckyToExcel.num2bl(bl),
        italic: luckyToExcel.num2bl(it),
        underline: luckyToExcel.num2bl(ul),
        strike: luckyToExcel.num2bl(cl),
    };

    return font;
};
var alignmentConvert = function (
    vt = "default",
    ht = "default",
    tb = "default",
    tr = "default"
) {
    // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
    const luckyToExcel = {
        vertical: {
            0: "middle",
            1: "top",
            2: "bottom",
            default: "top",
        },
        horizontal: {
            0: "center",
            1: "left",
            2: "right",
            default: "left",
        },
        wrapText: {
            0: false,
            1: false,
            2: true,
            default: false,
        },
        textRotation: {
            0: 0,
            1: 45,
            2: -45,
            3: "vertical",
            4: 90,
            5: -90,
            default: 0,
        },
    };

    let alignment = {
        vertical: luckyToExcel.vertical[vt],
        horizontal: luckyToExcel.horizontal[ht],
        wrapText: luckyToExcel.wrapText[tb],
        textRotation: luckyToExcel.textRotation[tr],
    };
    return alignment;
};
const borderConvert = (
    borderType = "border-all",
    style = 1,
    color = "#000000"
) => {
    // 对应luckysheet的config中borderinfo的的参数
    // if (!borderType) {
    //     return {};
    // }
    const luckyToExcel = {
        type: {
            "border-all": "all",
            "border-top": "top",
            "border-right": "right",
            "border-bottom": "bottom",
            "border-left": "left",
        },
        style: {
            0: "none",
            1: "thin",
            2: "hair",
            3: "dotted",
            4: "dashDot", // 'Dashed',
            5: "dashDot",
            6: "dashDotDot",
            7: "double",
            8: "medium",
            9: "mediumDashed",
            10: "mediumDashDot",
            11: "mediumDashDotDot",
            12: "slantDashDot",
            13: "thick",
        },
    };
    let template = {
        style: luckyToExcel.style[style],
        color: { argb: color.replace("#", "") },
    };
    let border = {};
    if (luckyToExcel.type[borderType] === "all") {
        border["top"] = template;
        border["right"] = template;
        border["bottom"] = template;
        border["left"] = template;
    } else {
        border[luckyToExcel.type[borderType]] = template;
    }
    return border;
};

/** 导出excel 功能 end */
</script>

<!-- scss配置已生效 -->

<style lang="scss" scoped>
.lucky {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;

    .operate {
        margin: 0 10px 10px;
        position: absolute;
        top: 10px;
        right: 0;
        width: 100%;
        display: flex;
        justify-content: flex-end;
        align-items: center;
        span {
            margin-right: 10px;
        }
    }

    #luckysheet {
        margin: 0px;
        padding: 0px;
        position: absolute;
        width: 100%;
        height: 100%;
        height: calc(100% - 50px);
        left: 0px;
        top: 50px;
    }
}
</style>

2.3 模板页面:配置文件

// src/utils/templateConfig.js
import { saveAs } from "file-saver";
/** template 模板 start */
/** 工作表数据及配置 start */
export const sheetData = [
    {
        name: "sheet1", //工作表名称
        color: "", //工作表颜色,工作表名称下方会有一条底部边框
        index: 0, //工作表索引,作为唯一key值使用,不是工作表顺序,和order区分开。
        status: 1, //激活状态,仅有一个激活状态的工作表,其它工作表为0
        order: 0, //工作表的下标
        hide: 0, //是否隐藏 0不隐藏 1隐藏
        row: 36, //行数
        column: 18, //列数
        celldata: [],
        config: {},
    },
];
/** 工作表数据及配置 end */
/** 整体配置 start */
//配置项 作用于整个表格。特别的,单个sheet的配置项需要在options.data数组中
export const options = {
    container: "luckysheet", // 设定DOM容器的id:luckysheet为容器id
    title: "Luckysheet Demo", // 设置表格名称
    lang: "zh", // 设定表格语言
    // 更多其它设置...
    data: sheetData,
    column: 18, // 空表格默认的列数量
    row: 36, //空表格默认的行数据量
    showtoolbar: true, //是否显示工具栏
    showtoolbarConfig: {
        //自定义配置工具栏
        print: false, //打印
    },
    showinfobar: false, //是否显示顶部信息栏
    showsheetbar: false, //是否显示底部sheet页按钮
    showsheetbarConfig: {
        //自定义配置底部sheet页按钮
        add: false, //新增sheet
        menu: false, //sheet管理菜单
        sheet: false, //sheet页显示
    },
    showstatisticBar: false, //是否显示底部计数栏
    showstatisticBarConfig: {
        //自定义配置底部计数栏
        count: true, //计数栏
        view: false, //打印视图
        zoom: true, //缩放
    },
    enableAddRow: false, //允许添加行
    enableAddBackTop: false, //允许回到顶部
    userInfo: false, //右上角的用户信息展示
    userMenuItem: [], //点击右上角的用户信息弹出的菜单
    myFolderUrl: "", //左上角 < 返回按钮的链接
    functionButton: "", //右上角功能按钮
    cellRightClickConfig: {
        //自定义配置单元格右击菜单
        copy: true, //复制
        copyAs: false, //复制为
        paste: true, //粘贴
        insertRow: true, //插入行
        insertColumn: true, //插入列
        deleteRow: true, //删除选中行
        deleteColumn: true, //删除选中列
        deleteCell: true, //删除单元格
        hideRow: true, //隐藏选中行和显示选中行
        hideColumn: true, //隐藏选中列和显示选中列
        rowHeight: true, //行高
        columnWidth: true, //列宽
        clear: true, //清除内容
        matrix: true, //矩阵操作选区
        sort: true, //排序选区
        filter: true, //筛选选区
        chart: true, //图表生成
        image: true, //插入图片
        link: true, //插入链接
        data: true, //数据验证
        cellFormat: true, //设置单元格格式
    },
    sheetRightClickConfig: {
        //自定义配置sheet页右击菜单
        delete: true, //删除
        copy: true, //复制
        rename: true, //重命名
        color: false, //更改颜色
        hide: false, //隐藏,取消隐藏
        move: false, //向左移,向右移
    },
    rowHeaderWidth: 46, //行标题区域的宽度,如果设置为0,则表示隐藏行标题
    columnHeaderHeight: 20, //列标题区域的高度,如果设置为0,则表示隐藏列标题
    sheetFormulaBar: true, //是否显示公示栏
    defaultFontSize: 11, //初始化默认字体大小
    limitSheetNameLength: true, //工作表重命名等场景下是否限制工作表名称的长度
    defaultSheetNameMaxLength: 31, //默认允许的工作表名最大长度
    // pager: { //分页器按钮设置
    //     pageIndex: 1, //当前的页码
    //     pageSize: 10, //每页显示多少行数据
    //     total: 50, //数据总行数
    //     selectOption: [10, 20], //允许设置每页行数的选项
    // },
    hook: {
        //钩子函数

        /** 单元格 start */
        /**进入单元格编辑模式之前触发。在选中了某个单元格且在非编辑状态下,通常有以下三种常规方法触发进入编辑模式 1.双击单元格 2.敲Enter键 3.使用API:enterEditMode
         * 参数:{ Array }[range]: 当前选区范围
         */
        cellEditBefore: (range) => {
            console.log("进入单元格编辑模式之前触发:cellEditBefore");
            console.log(`当前选区范围:`, range);
        },
        /**更新这个单元格值之前触发,return false 则不执行后续的更新。在编辑状态下修改了单元格之后,退出编辑模式并进行数据更新之前触发这个钩子
         * 参数:
         * {Number}[r]:单元格所在行数
         * {Number}[c]:单元格所在列数
         * {Object | String | Number}[value]:要修改的单元格内容
         * {Boolean}[isRefresh]:是否刷新整个表格
         */
        cellUpdateBefore: (r, c, value, isRefresh) => {
            console.log("更新这个单元格值之前触发:cellUpdateBefore");
            console.log(
                `单元格所在行:${r};单元格所在列:${c};是否刷新整个表格:${isRefresh};要修改的单元格内容: `,
                value
            );
        },
        /** 更新这个单元格后触发 */
        cellUpdated: (r, c, oldValue, newValue, isRefresh) => {
            console.log("cellUpdated:", r, c, oldValue, newValue);
            // 获取单元格的值
            const value = luckysheet.getCellValue(r, c, { type: "v" });
            console.log("更新后的值:", value, "对值进行数据验证");
        },
        /**单元格渲染前触发 return false 则不渲染该单元格
         * 参数:
         * {Object}[cell]:单元格对象
         * {Object}[position]:
         *  {Number}[r]:单元格所在行号
         *  {Number}[c]:单元格所在列号
         *  {Number}[start_r]:单元格左上角的水平坐标
         *  {Number}[start_c]:单元格左上角的垂直坐标
         *  {Number}[end_r]:单元格右下角的水平坐标
         *  {Number}[end_c]:单元格右下角的垂直坐标
         * {Object}[sheet]:当前sheet对象
         * {Object}[ctx]:当前画布的context
         */
        cellRenderBefore: (cell, position, sheet, ctx) => {
            // console.log('单元格渲染前触发:cellRenderBefore');
            // console.log('单元格对象:', cell, '单元格所在行号:', position.r, '单元格所在列号:', position.c, '单元格左上角的水平坐标:',
            //     position.start_r, '单元格左上角的垂直坐标:', position.start_c, '单元格右下角的水平坐标:', position.end_r, '单元格右下角的垂直坐标:', position.end_c,
            //     '当前sheet对象:', sheet, '当前画布的context:', ctx);
        },
        /**单元格渲染结束后触发,return false 则不渲染该单元格
         * 参数:
         *  {Object} [cell]:单元格对象
         *  {Object} [position]:
         *   {Number} [r]:单元格所在行号
         *   {Number} [c]:单元格所在列号
         *   {Number} [start_r]:单元格左上角的水平坐标
         *   {Number} [start_c]:单元格左上角的垂直坐标
         *   {Number} [end_r]:单元格右下角的水平坐标
         *   {Number} [end_c]:单元格右下角的垂直坐标
         * {Object} [sheet]:当前sheet对象
         * {Object} [ctx]:当前画布的context
         *
         */
        cellRenderAfter: () => {},
        /**所有单元格渲染之前执行的方法
         * 参数:
         *  {Object}[data]:当前工作表二维数组数据
         *  {Object}[sheet]:当前sheet对象
         *  {Object}[ctx]:当前画布的context
         */
        cellAllRenderBefore: (data, sheet, ctx) => {
            // console.log('当前工作表二维数组数据:', data);
        },
        /**行标题单元格渲染前触发,return false 则不渲染行标题
         * 参数:
         *  {String}[rowNum]:行号
         *  {Object}[position]:
         *   {Number}[r]:单元格所在行号
         *   {Number}[top]:单元格左上角的垂直坐标
         *   {Number}[width]:单元格宽度
         *   {Number}[height]:单元格高度
         * {Object}[ctx]:当前画布的context
         */
        rowTitleCellRenderBefore: () => {},
        /** 行标题单元格渲染后触发 return false 则不渲染行标题*/
        rowTitleCellRenderAfter: () => {},
        /** 列标题单元格渲染前触发 return false 则不渲染列标题 */
        columnTitleCellRenderBefore: () => {},
        /** 列标题单元格渲染后触发,return false 则不渲染列标题 */
        columnTitleCellRenderAfter: () => {},
        /** 单元格 end */

        /** 鼠标钩子 start */
        /** 单元格点击前的事件,return false 则终止之后的点击操作 */
        cellMousedownBefore: (cell, position, sheet, ctx) => {},
        /** 单元格点击后的事件,return false 则终止之后的点击操作 */
        cellMousedownAfter: (cell, position, sheet, ctx) => {},
        /** 鼠标移动事件,可通过cell判断鼠标停留在哪个单元格 */
        sheetMousemove: () => {},
        /** 鼠标按钮释放事件,可通过cell判断鼠标停留在哪个单元格 */
        sheetMouseup: () => {},
        /** 鼠标滚动事件 */
        scroll: (position) => {},
        /** 鼠标拖拽文件到Luckysheet内部的结束事件 */
        cellDragStop: () => {},
        /** 鼠标钩子 end */

        /** 选区操作 (包括单元格) start */
        /** 框选或者设置选区后触发 参数:{Object}[sheet]:当前选区对象 {Object | Array}[range]:选区范围,可能为多个选区 */
        rangeSelect: (sheet, range) => {
            console.log("选区范围:", range);
        },
        /** 移动选区前,包括单个单元格 */
        rangeMoveBefore: (range) => {},
        /** 移动选区后,包括单个单元格 */
        rangeMoveAfter: (range) => {},
        /** 选区修改前 */
        rangeEditBefore: (range, data) => {},
        /** 选区修改后 */
        rangeEditAfter: (range, oldData, newData) => {},
        /** 选区复制前 */
        rangeCopyBefore: (range, data) => {},
        /** 选区复制后 */
        rangeCopyAfter: (range, data) => {},
        /** 选区粘贴前 */
        rangePasteBefore: (range, data) => {},
        /** 选区粘贴后 */
        rangePasteAfter: (range, originData, pasteData) => {},
        /** 选区剪切前 */
        rangeCutBefore: (range, data) => {},
        /** 选区剪切后 */
        rangeCutAfter: (range, data) => {},
        /** 选区删除前 */
        rangeDeleteBefore: (range, data) => {},
        /** 选区删除后 */
        rangeDeleteAfter: (range, data) => {},
        /** 选区清除前 */
        rangeClearBefore: (range, data) => {},
        /** 选区清除后 */
        rangeClearAfter: (range, data) => {},
        /** 选区下拉前 */
        rangePullBefore: (range) => {},
        /** 选区下拉后 */
        rangePullAfter: (range) => {},
        /** 选区操作 (包括单元格) end */

        /** 工作表 start */
        /** 创建sheet页前触发,sheet页新建也包含数据透视表新建 */
        sheetCreatekBefore: () => {},
        /** 创建sheet页后触发,sheet页新建也包含数据透视表新建 */
        sheetCreateAfter: (sheet) => {},
        /** sheet移动前 */
        sheetMoveBefore: (i, order) => {},
        /** sheet移动后 */
        sheetMoveAfter: (i, oldOrder, newOrder) => {},
        /** sheet删除前 */
        sheetDeleteBefore: (sheet) => {},
        /** sheet删除后 */
        sheetDeleteAfter: (sheet) => {},
        /** sheet修改名称前 */
        sheetEditNameBefore: (i, name) => {},
        /** sheet修改名称后 */
        sheetEditNameAfter: (i, oldName, newName) => {},
        /** sheet修改颜色前 */
        sheetEditColorBefore: (i, color) => {},
        /** sheet修改颜色后 */
        sheetEditColorAfter: (i, oldColor, newColor) => {},
        /** sheet缩放前 */
        sheetZoomBefore: (i, zoom) => {},
        /** sheet缩放后 */
        sheetZoomAfter: (i, oldZoom, newZoom) => {},
        /** 激活工作表前 */
        sheetActivate: (i, isPivotInitial, isNewSheet) => {},
        /** 工作表从活动状态转为非活动状态前 */
        sheetDeactivateBefore: (i) => {},
        /** 工作表从活动状态转为非活动状态后 */
        sheetDeactivateAfter: (i) => {},
        /** 工作表 end */

        /** 工作薄 start */
        /** 表格创建之前触发 */
        workbookCreateBefore: (book) => {},
        /** 表格创建之后触发 */
        workbookCreateAfter: (book) => {},
        /** 表格销毁之前触发 */
        workbookDestroyBefore: (book) => {},
        /** 表格销毁之后触发 */
        workbookDestroyAfter: (book) => {},
        /** 协同编辑中的每次操作后执行的方法,监听表格内容变化,即客户端每执行一次表格操作,Luckysheet将这次操作存到历史记录中后触发,撤销重做时因为也算一次操作,也会触发此钩子函数 参数:{Object}[operate]:本次操作的历史记录信息,根据不同的操作,会有不同的历史记录 */
        updated: (operate) => {},
        /** resize 执行之后 */
        resized: (size) => {},
        /** 工作薄 end */

        /** 冻结 start */
        /** 设置冻结前 */
        frozenCreateBefore: (frozen) => {},
        /** 设置冻结后 */
        frozenCreateAfter: (frozen) => {},
        /** 取消冻结前 */
        frozenCancelBefore: (frozen) => {},
        /** 取消冻结后 */
        frozenCancelAfter: (frozen) => {},
        /** 冻结 end */

        /** 分页器 start */
        /** 点击分页按钮回调函数,返回当前页码 */
        onTogglePager: (page) => {},
        /** 分页器 end */
    },
    /** 协同编辑 start */
    // loadUrl: gridKey:工作簿的唯一标识, 加载luckysheet数据的地址
    // loadUrl: 'http://localhost:8081/getWorkBook?gridKey=1',
    // loadUrl: "/baseURL/excel",
    // updateUrl: '', //后台websocket地址
    allowUpdate: false,
    /** 协同编辑 end */
};
/** 整体配置 end */
/** template 模板 end */

2.4. 模板页面:页面使用

//luckysheet/template.vue
<!-- 模板填写 -->

<template>
    <div class="lucky">
        <div class="operate">
            <input
                type="file"
                style="font-size: 12px"
                ref="fileInput"
                @change="handleFileChange" />
            <el-button @click="handleSubmit" type="primary">提交模板</el-button>
        </div>
        <div id="luckysheet"></div>
    </div>
</template>

<script setup>
import LuckyExcel from "luckyexcel";
import { onMounted, reactive, onUnmounted, ref } from "vue";
import { saveAs } from "file-saver";
import { options } from "@/utils/templateConfig.js";
import axios from "axios";
import { globalName } from "../../utils/global";
import { getAuth, updateTemplate } from "@/api/api.js";
import { ElMessage } from "element-plus";

let templateData = reactive([]);

onMounted(() => {
    getTemplateJson();
});

onUnmounted(() => {
    exitEditMode();
});

// 获取模板文件 获取public文件夹下指定json文件内容
const getTemplateJson = () => {
    axios.get(`/${globalName}/json/template.json`).then((res) => {
        if (res.status === 200) {
            templateData = res.data;
            createLuckysheet();
            init();
        }
    });
};

// 创建luckysheet
const createLuckysheet = () => {
    templateData[0] = {
        ...templateData[0],
    };
    luckysheet.create({
        ...options,
        data: templateData,
    });
};
// 初始化
const init = () => {
    getAllSheets();
};

// 选择文件 将excel文件转为luckysheet的数据
const handleFileChange = (event) => {
    const file = event.target.files[0];
    LuckyExcel.transformExcelToLucky(
        file,
        (exportJson, luckysheetfile) => {
            // 转换后获取工作表数据
            console.log("excel转换为luckysheet的数据:", exportJson);
            // 销毁原来的表格
            luckysheet.destroy();
            // 重新创建表格
            luckysheet.create({
                ...options,
                data: exportJson.sheets,
                title: exportJson.info.name,
            });
        },
        (error) => {
            // 如果抛出任何错误,则处理错误
            console.log(error);
        }
    );
};

// 所有工作表的配置信息
const getAllSheets = () => {
    // 获取所有工作表的配置信息
    const allSheets = luckysheet.getAllSheets();
    console.log("所有工作表的配置信息:", allSheets);
    return allSheets;
};
// 工作表数据
const getSheetData = () => {
    const luckysheetdata = luckysheet.getSheetData({ order: 0 });
    console.log("工作表数据", luckysheetdata);
    return luckysheetdata;
};

// 处理冻结错位
const handleFrozen = () => {
    //调用luckysheet提供的API获取当前编辑的表格数据
    let allSheets = getAllSheets();
    /** 解决这三种情况下,保存数据,重新渲染时,冻结行列错位问题 start */
    // 冻结行到选区
    if (allSheets[0].frozen) {
        if (allSheets[0].frozen.type === "rangeRow") {
            allSheets[0].frozen.range.row_focus =
                allSheets[0].frozen.range.row_focus - 1;
        }
        // 冻结列到选区
        if (allSheets[0].frozen.type === "rangeColumn") {
            allSheets[0].frozen.range.column_focus =
                allSheets[0].frozen.range.column_focus - 1;
        }
        // 冻结行列到选区
        if (allSheets[0].frozen.type === "rangeBoth") {
            allSheets[0].frozen.range.column_focus =
                allSheets[0].frozen.range.column_focus - 1;
            allSheets[0].frozen.range.row_focus =
                allSheets[0].frozen.range.row_focus - 1;
        }
    }
    return allSheets;
    /** 解决这三种情况下,保存数据,重新渲染时,冻结行列错位问题 end */
};

//保存数据到本地文件 (供模拟)
const saveToLocalFile = (data) => {
    const content = JSON.stringify(allSheets);
    const blob = new Blob([content], {
        type: "text/plain;charset=utf-8",
    });
    saveAs(blob, "template.json");
};

// 退出编辑模式
const exitEditMode = () => {
    // 自动退出编辑模式的操作,主要是为了触发自动保存单元格
    luckysheet.exitEditMode();
};

// 自动保存
const autoSave = () => {
    let allSheets = handleFrozen();
    // 去除临时数据,减小体积
    for (const i in allSheets) {
        allSheets[i].data = undefined;
    }
    let params = {
        jsonData: JSON.stringify(allSheets),
        targetFilePath:
            process.env.NODE_ENV === "development"
                ? "D://hhkj_project//function-budget//public//json//template.json"
                : "D://apache-tomcat-7.0.64-8080-mis//webapps//functionalBudget//json//template.json",
    };
    updateTemplate(params).then((res) => {
        if (res.success) {
            // $(luckysheet_info_detail_save).text("已保存");
            ElMessage({
                message: "保存成功",
                type: "success",
            });
        }
    });
};

// 提交模板
const handleSubmit = () => {
    autoSave();
};

/**
 * 如何解决冻结行列区不准的问题
 * 1. 改源码 - 耗时且不准
 * 2. 假设在填写模板时,去掉设置冻结的操作
 *  在填写数据页面,把设置冻结的操作放开。然后供填写时设置方便。
 *  提交时,去除冻结的配置。以防止回显时,冻结列现实错乱的问题。
 */
/**
 * 综合考虑:
 *  模板页面不需要自动保存功能,因为会涉及到工具栏的操作。
 *  目前没有采用实时官方提供的loadurl,updateurl,allowupdate的这种方式去更新,需要用到websocket,后台数据难存储,所以,采用普通的方式保存
 */
</script>

<!-- scss配置已生效 -->

<style lang="scss" scoped>
.lucky {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;

    .operate {
        margin: 0 10px 10px;
        position: absolute;
        top: 10px;
        right: 0;
        width: 100%;
        display: flex;
        justify-content: flex-end;
    }

    #luckysheet {
        margin: 0px;
        padding: 0px;
        position: absolute;
        width: 100%;
        height: 100%;
        height: calc(100% - 50px);
        left: 0px;
        top: 50px;
    }
}
</style>

三、界面效果

  1. 模板页面:供制作填报模板,有头部工具栏
    在这里插入图片描述
  2. 填报页面:供填报 处部分数据可填写外,其它只读,且不可更改模板结构样式等。
    在这里插入图片描述
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值