luckyexcel 编辑预览excel文件

luckyexcel 编辑预览excel文件

支持后端传文件流预览编辑,也支持选择本地文件编辑预览

看效果

上代码

typescript 复制代码
<template>
  <div style="margin: 30px">
    <div class="button-box2">
      <div>
        <div style="color: red">当前弹框不要修改列名称,如需修改,请去列管理页面修改列。</div>
      </div>

      <div>
        <a-space>
          <!-- <input id="uploadBtn" type="file" @change="loadExcel3" /> -->
          <a-button type="primary" @click="downExcel">导出模板</a-button>
          <a-upload
            v-model:file-list="data.fileList"
            accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            :customRequest="customUpload"
            name="file"
            :multiple="false"
            :showUploadList="false"
          >
            <a-button :loading="data.clientLoading" type="primary">客户端导入</a-button>
          </a-upload>
          <a-button
            type="primary"
            :loading="data.serverLoading"
            @click="() => (modal.visible = true)"
            >服务端导入</a-button
          >
          <!-- <a-dropdown>
            <template v-slot:overlay>
              <a-menu>
                <a-menu-item key="1">
                  <a-upload
                    v-model:file-list="data.fileList"
                    accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                    :customRequest="customUpload"
                    name="file"
                    :multiple="false"
                    :showUploadList="false"
                  >
                    <div :loading="data.clientLoading">客户端导入</div>
                  </a-upload>
                </a-menu-item>
                
                <a-menu-item key="2">
                  <div :loading="data.serverLoading" @click="() => (modal.visible = true)">
                    服务端导入
                  </div>
                </a-menu-item>
              </a-menu>
            </template>
            <a-button type="primary">
              导入预览(如果这样写的话就要把loading放到这里,统一一个变量就好了)
              <DownOutlined />
            </a-button>
          </a-dropdown> -->
        </a-space>
      </div>
    </div>

    <div id="luckysheet"></div>
    <div class="button-box">
      <a-space>
        <a-button @click="cancel"> 取消 </a-button>
        <a-button type="primary" :loading="loading" @click="downloadExcel">确定 </a-button>
      </a-space>
    </div>
    <div class="tips">
      <div style="color: red" v-if="data.isShowDataType">
        测序方法请填写:Nanopore pod5,Nanopore fast5,Nanopore fastq,齐碳
        fastq,PacBio_CCS,PacBio_CLR,华大,illumina,ABI,其他
      </div>
      <div style="color: red" v-if="data.isShowDataType">
        类型请填写:细菌,病毒,真菌,16S,宏基因组,动植物,转录组,人源,其他
      </div>
      <div style="color: red" v-if="data.isShowDataType">
        日期请按照YYYY-MM-DD格式填写,如:2024-01-01
      </div>
    </div>
    <a-modal
      :visible="modal.visible"
      title="选择文件"
      width="1020px"
      @ok="fileOk"
      @cancel="() => (modal.visible = false)"
      zIndex="1000"
      destroyOnClose
    >
      <FileSelect />
    </a-modal>
  </div>
</template>

<script lang="ts" setup>
import { reactive, onMounted, watch } from 'vue';
import * as Api from '/@/serve/api/samples/samples';
import { exportExcel } from '/@/utils/utils/exceljs';
import FileSelect from '/@/components/FileSelector/index.vue';
import { DownOutlined } from '@ant-design/icons-vue';
import LuckyExcel from 'luckyexcel';
import xlsx from 'node-xlsx';

import { message } from 'ant-design-vue';
const emit = defineEmits(['cancel', 'batchAddData']);
const props = defineProps({
  headers: {
    type: Array,
    default: () => [],
  },
  loading: {
    type: Boolean,
    default: false,
  },
});
const modal = reactive({
  visible: false,
});
const cancel = () => {
  luckysheet.exitEditMode(); // 退出编辑模式
  emit('cancel', '');
};
//服务器导入确定
const fileOk = () => {
  let selectPath = localStorage.getItem('nowSelector');
  if (selectPath) {
    if (selectPath.indexOf(' ') != -1) {
      message.warning('所选路径不能包含空格');
      return;
    }
    if (selectPath.slice(-5).includes('.xlsx')) {
      loadExcel2(selectPath);
      modal.visible = false;
    } else {
      message.info('请选择xlsx文件');
    }
  }
};
const loadExcel2 = (filePath: string) => {
  let params = { filePath };
  Api.xlsxFile(params)
    .then((res: any) => {
      const blob = new Blob([res], {
        // 设置返回的文件类型
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      });
      let file = new window.File([blob], 'fileName.xlsx', {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      });
      data.serverLoading = true;
      loadExcel([file]);
    })
    .catch(() => {
      message.error('error');
    });
};
const loadExcel3 = (evt: any) => {
  loadExcel(evt.target.files);
};
//自定义上传
const customUpload = (info: any) => {
  data.clientLoading = true;
  loadExcel([info.file]);
};

const isFunction = (val: Function) => {
  return Object.prototype.toString.call(val).slice(8, -1) === 'Function';
};

const loadExcel = (files: any) => {
  //通过接口获取文件流,将文件流转为file文件,
  if (files == null || files.length == 0) {
    message.info('没有文件等待导入');
    return;
  }
  let name = files[0].name;
  let suffixArr = name.split('.'),
    suffix = suffixArr[suffixArr.length - 1];
  if (suffix != 'xlsx') {
    message.error('目前只支持导入xlsx文件');
    return;
  }
  LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {
    if (exportJson.sheets == null || exportJson.sheets.length == 0) {
      message.error('读取excel文件内容失败,目前不支持xls文件!');
      return;
    }
    //销毁原有的表格
    isFunction(luckysheet?.destroy) && luckysheet.destroy();
    luckysheet.create({
      container: 'luckysheet', //luckysheet is the container id
      lang: 'zh', // 设定表格语言
      showinfobar: false,
      data: exportJson.sheets,
      title: exportJson.info.name,
      userInfo: exportJson.info.name.creator,
    });
    console.log('start loading');
    data.clientLoading = false;
    data.serverLoading = false;
  });
};
const downExcel = () => {
  let result = xlsx.build([{ name: 'sheet1', data: [props.headers] }]);
  const ab = Buffer.from(result, 'binary');
  const blob = new Blob([ab]);
  const blobUrl = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = blobUrl;
  a.download = '模版.xlsx';
  a.click();
  window.URL.revokeObjectURL(blobUrl);
  // exportExcel(luckysheet.getAllSheets(), '模板');
};
const downloadExcel = () => {
  // console.log(luckysheet.getAllSheets());
  // exportExcel(luckysheet.getAllSheets(), '下载');
  luckysheet.exitEditMode(); // 退出编辑模式
  let data = luckysheet.getAllSheets()[0].celldata;
  console.log(data);

  let result: any[] = [];
  data.forEach((item: any) => {
    if (result.length > item.r) {
      let tempArray = result[item.r];
      // console.log('item.c', item.c);

      tempArray[item.c] = item.v.m;
    } else {
      let tempArray = [];
      for (let i = 0; i < props.headers.length; i++) {
        tempArray.push(undefined);
      }
      tempArray[item.c] = item.v.m;
      result.push(tempArray);
    }
  });
  // let result2 = result.map((item: any) => {
  //   return item.slice(0, props.headers.length);
  // });
  // console.log(result2);
  emit('batchAddData', result);
};

const data = reactive({
  options: {},
  fileList: [],
  clientLoading: false,
  serverLoading: false,
  isShowDataType: false,
});
// !!! create luckysheet after mounted
onMounted(() => {
  drawSheet();
});

watch(
  () => props.headers,
  (n) => {
    drawSheet();
  },
);

const drawSheet = () => {
  console.log('111');

  data.isShowDataType = props.headers.includes('测序方法');
  let celldata = props.headers.map((item: any, index: number) => {
    let con = {
      r: 0,
      c: index,
      v: {
        bl: 1,
        ct: { fa: 'General', t: 'g' },
        ht: 0,
        v: item,
        m: item,
      },
    };
    return con;
  });
  data.options = {
    container: 'luckysheet',
    lang: 'zh', // 设定表格语言
    showinfobar: false,
    data: [
      {
        name: 'Sheet1', //工作表名称
        color: '', //工作表颜色
        index: 0, //工作表索引
        status: 1, //激活状态
        order: 0, //工作表的下标
        hide: 0, //是否隐藏
        row: 36, //行数
        column: 36, //列数
        defaultRowHeight: 39, //自定义行高
        defaultColWidth: 146, //自定义列宽
        celldata, //初始化使用的单元格数据
        config: {
          merge: {}, //合并单元格
          rowlen: {}, //表格行高
          columnlen: {}, //表格列宽
          rowhidden: {}, //隐藏行
          colhidden: {}, //隐藏列
          borderInfo: {}, //边框
          authority: {}, //工作表保护
        },
        scrollLeft: 0, //左右滚动条位置
        scrollTop: 0, //上下滚动条位置
        luckysheet_select_save: [], //选中的区域
        calcChain: [], //公式链
        isPivotTable: false, //是否数据透视表
        pivotTable: {}, //数据透视表设置
        filter_select: {}, //筛选范围
        filter: null, //筛选配置
        luckysheet_alternateformat_save: [], //交替颜色
        luckysheet_alternateformat_save_modelCustom: [], //自定义交替颜色
        luckysheet_conditionformat_save: {}, //条件格式
        frozen: {}, //冻结行列配置
        chart: [], //图表配置
        zoomRatio: 1, // 缩放比例
        image: [], //图片
        showGridLines: 1, //是否显示网格线
        dataVerification: {}, //数据验证配置
      },
      // {
      //   name: 'Sheet2',
      //   color: '',
      //   index: 1,
      //   status: 0,
      //   order: 1,
      // celldata: [
      //     {
      //       r: 0,
      //       c: 0,
      //       v: {
      //         v: '暂时淀粉',
      //         m: '暂时淀粉',
      //       },
      //     },
      //     {
      //       r: 0,
      //       c: 1,
      //       v: {
      //         v: 1,
      //         ct: {
      //           fa: 'General',
      //           t: 'n',
      //         },
      //         m: '1',
      //       },
      //     },
      //     {
      //       r: 1,
      //       c: 0,
      //       v: {
      //         v: 10,
      //         ct: {
      //           fa: 'General',
      //           t: 'n',
      //         },
      //         m: '10',
      //       },
      //     },
      //     {
      //       r: 1,
      //       c: 1,
      //       v: {
      //         v: 11,
      //         ct: {
      //           fa: 'General',
      //           t: 'n',
      //         },
      //         m: '11',
      //       },
      //     },
      //   ],
      //   config: {},
      // },
    ],
  };

  isFunction(luckysheet?.destroy) && luckysheet.destroy();
  luckysheet.create(data.options);
};
</script>

<style scoped>
#luckysheet {
  /* position: relative; */
  width: 79vw;
  height: 70vh;
}
.button-box {
  /* margin: 20px; */
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
}
.button-box2 {
  margin: 20px;
  display: flex;
  /* justify-content: flex-end; */
  justify-content: space-between;
}
.tips {
  margin-top: -30px;
}
:deep(.luckysheet-wa-editor) {
  overflow: hidden;
}
:deep(.luckysheet-stat-area) {
  background-color: transparent;
}
</style>
<style>
#chat-assistant-container {
  display: none;
}
</style>
相关推荐
tyler_download5 分钟前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~5 分钟前
qt5将程序打包并使用
开发语言·qt
hlsd#5 分钟前
go mod 依赖管理
开发语言·后端·golang
小春学渗透7 分钟前
Day107:代码审计-PHP模型开发篇&MVC层&RCE执行&文件对比法&1day分析&0day验证
开发语言·安全·web安全·php·mvc
四喜花露水8 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
杜杜的man9 分钟前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang
亦世凡华、10 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
前端Hardy17 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
测试界的酸菜鱼24 分钟前
C# NUnit 框架:高效使用指南
开发语言·c#·log4j
GDAL24 分钟前
lua入门教程 :模块和包
开发语言·junit·lua