我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue3实现动态导入Excel表格数据的方法详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue3实现动态导入Excel表格数据的方法详解

1.  前言

在开发工作过程中,我们会遇到各种各样的表格数据导入,大部分我们的解决方案:提供一个模板前端进行下载,然后按照这个模板要求进行数据填充,最后上传导入,这是其中一种解决方案。个人认为还有另外一种解决方案,不一定会前面的方案好,方便,但是可以减少人为操作,减少出错,更为通用,就是进行动态数据导入,前端进行自定义导入数据,配置数据对应关系,最后提交导入结果。

2.  如何实现

2.1使用技术

后台使用.Net6.0,前端使用Vue3

2.2实现思路

  • 前端通过Excel表格数据导入,导入后客户可以进行数据编辑(未实现)客户数据确认后进行数据回写主页面
  • 设置数据对应关系,也就是后台所需数据格式和前台数据格式进行绑定
  • 数据校验,进行数据提交

2.3具体实现方案

2.3.1导入Excel数据

创建列表页面,页面主要布局如下

Table需要绑定的列是未知的,所以el-table绑定的el-table-column是需要动态进行加载,动态定义表格以及变量

<el-table :data="state.tableData.data">
  <el-table-column
      v-for="item in state.colunm"
      :prop="item.key"
      :key="item.key"
      :label="item.lable"
  >
  </el-table-column>
</el-table>
const state = reactive({
colunm: [{key: "", lable: ""}],
});

点击导入报名数据,弹出上传数据页面

这一块的功能在之前的一篇文章有写过:.Net6.0+Vue3实现数据简易导入功能全过程

子组件的核心代码

页面布局:

<template>
  <el-dialog :close-on-click-modal="false" v-model="state.dialogVisible" :title="title" width="70%" >
    <el-upload
        class="upload-demo"
        :auto-upload="false"
        :on-change="uploadChange"  style="text-align: left;"  >
      <el-button type="primary">上传文件</el-button>
    </el-upload>
      <el-form-item>
        <el-button @click='submit'>确认导入</el-button>
      </el-form-item>
    <el-table :data="state.tableData.data" max-height="500px">
      <el-table-column v-for="item in state.colunm" :prop="item.key" :key="item.key" :label="item.lable">
      </el-table-column>
    </el-table>
    <div class='block flex justify-end' v-if='state.tableData.total > 0'>
      <el-pagination v-model:currentPage="state.searchInput.PageIndex" v-model:page-size="state.searchInput.PageSize"
        :page-sizes="[10, 50, 200, 1000]" layout="total, sizes, prev, pager, next, jumper" @size-change="getData"
        @current-change="getData" :total="state.tableData.total" />
    </div>
  </el-dialog>
</template>

变量定义:

const state = reactive({
  tempTableData: [{}],//临时存储全部数据
  searchInput: { PageIndex: 1, PageSize: 10 },
  tableData: { data: [{}], total: 0 },//表格加载当前页面数据
  dialogVisible: false,
  colunm: [{ key: '', lable: '' }]
});

父页面调用方法,进行表格列头加载,初始化表格数据:

const childMethod = (data) => {
  state.colunm = data;
  state.tableData.data = [];
  state.dialogVisible = true;
}

绑定表格方法,前端进行分页数据处理:(这里可能会有性能问题,暂时没有仔细探究)

const getData = () => {
  const tempData: any = [];
  state.tempTableData.forEach((value, index) => {
    if (index >= ((state.searchInput.PageIndex - 1) * state.searchInput.PageSize) && index < ((state.searchInput.PageIndex) * state.searchInput.PageSize)) {
      tempData.push(value);
    }
  });
  state.tableData.data = tempData;
  state.tableData.total = state.tempTableData.length;
}

提交数据回写父组件方法

const submit = () => {
  console.log(state.tempTableData);
  context.emit('childClick', state.tempTableData,state.colunm)
}

上传Excel读取数据方法,主要动态绑定列以及导入的数据

const uploadChange = async (file) => {
  let dataBinary = await readFile(file.raw)
  let workBook = XLSX.read(dataBinary, { type: 'binary', cellDates: true })
  let workSheet = workBook.Sheets[workBook.SheetNames[0]]
  let data: any = XLSX.utils.sheet_to_json(workSheet)

  let mycolunm={};
  Object.setPrototypeOf(mycolunm,data[0]);
  state.colunm=[];
  for(let key in mycolunm){
    state.colunm.push( { lable: key, key: key })
  }
  
  let tHeader = state.colunm.map(obj => obj.lable)
  let filterVal = state.colunm.map(obj => obj.key)
  tHeader.map(val => filterVal.map(obj => val[obj]))
  const tempData: any = [];
  data.forEach((value) => {
    const ob = {};
    tHeader.forEach((item, index) => {
      ob[filterVal[index]] = value[item].toString();
    })
    tempData.push(ob);
  })
  state.tempTableData = tempData;
  getData();
}

这里导入数据后,会调用主组件方法(dataUploadchildClick)用于回写主组件表格数据,列头等数据

const dataUploadchildClick = (data, colunm) => {
  state.colunm = colunm;
  state.tempData = data;
  getData();
  dataUpload.value.cancel();
};

2.3.2设置数据对应关系

定义后台需要的列

SelectData: [
  {key: 'zkzh', type: '', value: '', selectValue: '', title: '准考证号'},
  {key: 'zjlb', type: '', value: '', selectValue: '', title: '证件类别'},
  {key: 'zjhm', type: '', value: '', selectValue: '', title: '证件号码'},
  {key: 'ksxm', type: '', value: '', selectValue: '', title: '考生姓名'},
  {key: 'xb', type: '', value: '', selectValue: '', title: '性别'},
  {key: 'ss', type: '', value: '', selectValue: '', title: '省市'},
  {key: 'kq', type: '', value: '', selectValue: '', title: '考区'},
  {key: 'kdh', type: '', value: '', selectValue: '', title: '考点号'},
  {key: 'kdmc', type: '', value: '', selectValue: '', title: '考点名称'},
  {key: 'kch', type: '', value: '', selectValue: '', title: '考场号'},
  {key: 'kchdz', type: '', value: '', selectValue: '', title: '考场地址'},
  {key: 'zwh', type: '', value: '', selectValue: '', title: '座位号'},
  {key: 'chc', type: '', value: '', selectValue: '', title: '场次'}
]

创建Select组件,组件核心代码

页面布局部分

<label style="width: 50px;display: inline-block;">{{ itemKey }}:</label>
<el-select v-model="state.type" :placeholder="name" size="large" clearable>
  <el-option
      v-for="item in state.options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
  />
</el-select>
<el-input v-model="state.value" style="width: 200px;" :placeholder="name" size="large" v-if="state.type=='0'"/>
<el-select v-model="state.selectValue" style="width: 200px;" :placeholder="name" size="large" clearable
           v-if="state.type=='1'||state.type=='2'">
  <el-option
      v-for="item in state.ValueOptions"
      :key="item.key"
      :label="item.lable"
      :value="item.key"
  />
</el-select>

接受参数定义

props: {
  itemKey: String,
  name: String,
  type: String,
  value: String,
  selectValue: String,
  colunm: Object
},

页面变量定义,这里定义的默认option主要有三个

  • 0 固定值,这一列为固定值,不从表格中读取
  • 1 下拉框,从导入数据中选择
  • 2 自动生成,这里主要是特殊业务,可以进行自定义扩展
const state = reactive({
  value: ref(''),
  type: ref(''),
  selectValue: ref([]),
  ValueOptions:[{}],
  options: [
    {
      value: '0',
      label: '固定值',
    },
    {
      value: '1',
      label: '下拉框',
    },
    {
      value: '2',
      label: '自动生成',
    },
  ],
});

监听下拉框选择类型:

watch(() => state.type, (newVal) => {
      context.emit('update:type', newVal);
    }
);

监听下拉框选择固定,文本框输入的值:

watch(() => state.value, (newVal) => {
  context.emit('update:value', newVal)
})

监听下拉框选择下拉框,后面下拉框选择的值:

watch(() => state.selectValue, (newVal) => {
  context.emit('update:selectValue', newVal)
})

监听表格的列,动态加载下拉框绑定的值:

watch(() => props.colunm, (newVal: any) => {
  state.ValueOptions=newVal;
})

最终的效果:

父页面进行引用

<el-row :gutter="24" justify="start" style="text-align: left;">
          <div v-for="(item,index) in state.SelectData" :key='index' style="margin-top: 5px;width: 100%;">
            <el-col :span="24"><Select
                :itemKey="item.key"
                v-model:type="item.type"
                v-model:value="item.value"
                v-model:selectValue="item.selectValue"
                :name="item.title"
                :colunm="state.colunm"/></el-col>
          </div>
        </el-row>

2.3.3进行数据提交

提交数据到后台进行处理,这里可以根据自己的业务进行验证,或则进行其它扩展

const Save = () => {
  if (state.tempData.length == 0) {
    state.active=1;
    ElMessage.warning('请导入考生数据');
    return;
  }
  let CheckSelectData = true;
  state.SelectData.forEach((value, index) => {
    if (!value.type) {
      CheckSelectData = false
    }
  });
  if (!CheckSelectData) {
    state.active=2;
    ElMessage.warning('请设置完成数据对应关系');
    return;
  }
  if (state.tableTimeData.data.length==0){
    state.active=3;
    ElMessage.warning('请添加场次数据');
    return;
  }
  if (!state.ExamData.jiancheng||!state.ExamData.kaikaonianyue||!state.ExamData.quancheng){
    state.active=4;
    ElMessage.warning('请设置任务相关信息');
    return;
  }
  axios.post('/GenerateCheckTemplate', state)
      .then(function (response) {
        if (response.status == 200) {
          ElMessage.success(response.data);
          dataUpload.value.cancel();
          getData();
        } else {
          ElMessage.error(response.data)
        }
      })
}

后台定义接口:/GenerateCheckTemplate

定义实体对前台导入数据进行接收:

public class GenerateCheckTemplate
    {
        /// <summary>
        /// 考试任务对象
        /// </summary>
        public ExamData ExamData { get; set; }

        /// <summary>
        /// 数据对应关系对象
        /// </summary>
        public List<Correspondence> SelectData { get; set; }

        /// <summary>
        /// 导入数据
        /// </summary>
        public List<Dictionary<string, string>> tempData { get; set; }

        /// <summary>
        /// 导入数据列集合
        /// </summary>
        public List<Colunm> colunm { get; set; }

        /// <summary>
        /// 场次对象
        /// </summary>
        public tableTimeData tableTimeData { get; set; }
}

    /// <summary>
    /// 考试任务对象
    /// </summary>
    public class ExamData
    {
        /// <summary>
        /// 简称
        /// </summary>
        public string jiancheng { get; set; }
        /// <summary>
        /// 考试年月
        /// </summary>
        public string kaikaonianyue { get; set; }
        /// <summary>
        /// 简称
        /// </summary>
        public string quancheng { get; set; }
    }
    /// <summary>
    /// 数据对应关系对象
    /// </summary>
    public class Correspondence
    {
        /// <summary>
        /// key
        /// </summary>
        public string key { get; set; }

        /// <summary>
        /// 下拉选择数据
        /// </summary>
        public string selectValue { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string title { get; set; }

        /// <summary>
        /// 类型 0 固定值 1下拉框选择
        /// </summary>
        public string type { get; set; }

        /// <summary>
        /// 固定值数据
        /// </summary>
        public string value { get; set; }
    }

    /// <summary>
    /// 表格对象
    /// </summary>
    public class tableTimeData
    {
        /// <summary>
        /// 提交表格数据
        /// </summary>
        public List<ExamTime> data { get; set; }
        /// <summary>
        /// 总数
        /// </summary>
        public int total { get; set; }
    }

    public class ExamTime
    {
        /// <summary>
        /// 场次编码
        /// </summary>
        public int? ExamTimeCode { get; set; }
        /// <summary>
        /// 场次名称
        /// </summary>

        public string ExamTimeName { get; set; }

        /// <summary>
        /// 开始时间
        /// </summary>
        public string startTime { get; set; }
        /// <summary>
        /// 结束时间
        /// </summary>
        public string endTime { get; set; }

        /// <summary>
        /// 是否编辑状态
        /// </summary>
        public bool Edit { get; set; }
    }
    /// <summary>
    /// 导入数据列对象
    /// </summary>
    public class Colunm
    {
        /// <summary>
        /// 键值
        /// </summary>
        public string key { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string lable { get; set; }
    }

然后处理前端传过来的数据转换成需要的数据,这里type需要修改成枚举类型,类型可以根据需求进行扩展

/// <summary>
        /// 处理前端传过来的数据
        /// </summary>
        /// <param name="companiesInput"></param>
        private void ProcessingData(GenerateCheckTemplate companiesInput, DataTable dataTable)
        {
            Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
            foreach (var data in companiesInput.tempData)
            {
                DataRow dataRow = dataTable.NewRow();

                foreach (Correspondence correspondence in companiesInput.SelectData)
                {
                    if (correspondence.type == "0")
                    {
                        dataRow[correspondence.key] = correspondence.value;
                    }
                    else if (correspondence.type == "1")
                    {
                        dataRow[correspondence.key] = data[correspondence.selectValue];
                    }
                    else if (correspondence.type == "2")
                    {
                        string value = data[correspondence.selectValue];
                        if (keyValuePairs.ContainsKey(value))
                        {
                            dataRow[correspondence.key] = keyValuePairs[value];
                        }
                        else
                        {
                            keyValuePairs.Add(value, (keyValuePairs.Count + 1).ToString().PadLeft(2, '0'));
                            dataRow[correspondence.key] = keyValuePairs[value];
                        }
                    }
                }

                dataTable.Rows.Add(dataRow);
            }
        }

整体的功能到这里基本上实现了,具体的细节可能需要根据不同的项目进行优化,有更多的方案可以一起进行交流

附上源码地址:https://gitee.com/wyf854861085/file-upload.git

到此这篇关于Vue3实现动态导入Excel表格数据的方法详解的文章就介绍到这了,更多相关Vue动态导入Excel数据内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue3实现动态导入Excel表格数据的方法详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Vue3实现动态导入Excel表格数据的方法详解

在开发工作过程中,我们会遇到各种各样的表格数据导入,动态数据导入可以减少人为操作,减少出错。本文为大家介绍了Vue3实现动态导入Excel表格数据的方法,需要的可以参考一下
2022-11-16

利用Vue3实现可复制表格的方法详解

表格是前端非常常用的一个控件,本文主要为大家介绍了Vue3如何实现一个简易的可以复制的表格,文中的示例代码讲解详细,需要的可以参考一下
2022-12-12

Python导入Excel表格数据并以字典dict格式保存的操作方法

本文介绍基于Python语言,将一个Excel表格文件中的数据导入到Python中,并将其通过字典格式来存储的方法,感兴趣的朋友一起看看吧
2023-01-28

Java技术:实现pdf和Excel的生成及数据动态插入、导出

本篇带给大家关于Java技术,实现pdf和Excel的生成及数据动态插入、导出,希望对你有所帮助!

Java怎么实现pdf和Excel的生成及数据动态插入与导出

这篇文章主要介绍“Java怎么实现pdf和Excel的生成及数据动态插入与导出”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java怎么实现pdf和Excel的生成及数据动态插入与导出”文章能帮助大
2023-06-27

python脚本实现数据导出excel格式的简单方法(推荐)

实习期间,服务器的一位师兄让我帮忙整理一下服务器的log数据,最终我用Python实现了数据的提取并将其用Excel格式导出。下面是我Python实现的源码,可以自动遍历某一文件目录下的所有文本文件,并将总的数据导出到Excel文件中,导出
2022-06-04

C#实现Oracle批量写入数据的方法详解

往数据库批量写入数据,这个功能使用频率相对还是比较高的,特别是在做一些导入等功能的时候。本文为大家介绍了C#实现Oracle批量写入数据的方法,需要的可以参考一下
2022-11-13

编程热搜

目录