Vue3實現(xiàn)動態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法詳解
1. 前言
在開發(fā)工作過程中,我們會遇到各種各樣的表格數(shù)據(jù)導(dǎo)入,大部分我們的解決方案:提供一個模板前端進行下載,然后按照這個模板要求進行數(shù)據(jù)填充,最后上傳導(dǎo)入,這是其中一種解決方案。個人認(rèn)為還有另外一種解決方案,不一定會前面的方案好,方便,但是可以減少人為操作,減少出錯,更為通用,就是進行動態(tài)數(shù)據(jù)導(dǎo)入,前端進行自定義導(dǎo)入數(shù)據(jù),配置數(shù)據(jù)對應(yīng)關(guān)系,最后提交導(dǎo)入結(jié)果。
2. 如何實現(xiàn)
2.1使用技術(shù)
后臺使用.Net6.0,前端使用Vue3
2.2實現(xiàn)思路
- 前端通過Excel表格數(shù)據(jù)導(dǎo)入,導(dǎo)入后客戶可以進行數(shù)據(jù)編輯(未實現(xiàn))客戶數(shù)據(jù)確認(rèn)后進行數(shù)據(jù)回寫主頁面
- 設(shè)置數(shù)據(jù)對應(yīng)關(guān)系,也就是后臺所需數(shù)據(jù)格式和前臺數(shù)據(jù)格式進行綁定
- 數(shù)據(jù)校驗,進行數(shù)據(jù)提交
2.3具體實現(xiàn)方案
2.3.1導(dǎo)入Excel數(shù)據(jù)
創(chuàng)建列表頁面,頁面主要布局如下
Table需要綁定的列是未知的,所以el-table綁定的el-table-column是需要動態(tài)進行加載,動態(tài)定義表格以及變量
<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: ""}], });
點擊導(dǎo)入報名數(shù)據(jù),彈出上傳數(shù)據(jù)頁面
這一塊的功能在之前的一篇文章有寫過:.Net6.0+Vue3實現(xiàn)數(shù)據(jù)簡易導(dǎo)入功能全過程
子組件的核心代碼
頁面布局:
<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'>確認(rèn)導(dǎo)入</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: [{}],//臨時存儲全部數(shù)據(jù) searchInput: { PageIndex: 1, PageSize: 10 }, tableData: { data: [{}], total: 0 },//表格加載當(dāng)前頁面數(shù)據(jù) dialogVisible: false, colunm: [{ key: '', lable: '' }] });
父頁面調(diào)用方法,進行表格列頭加載,初始化表格數(shù)據(jù):
const childMethod = (data) => { state.colunm = data; state.tableData.data = []; state.dialogVisible = true; }
綁定表格方法,前端進行分頁數(shù)據(jù)處理:(這里可能會有性能問題,暫時沒有仔細(xì)探究)
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; }
提交數(shù)據(jù)回寫父組件方法
const submit = () => { console.log(state.tempTableData); context.emit('childClick', state.tempTableData,state.colunm) }
上傳Excel讀取數(shù)據(jù)方法,主要動態(tài)綁定列以及導(dǎo)入的數(shù)據(jù)
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(); }
這里導(dǎo)入數(shù)據(jù)后,會調(diào)用主組件方法(dataUploadchildClick)用于回寫主組件表格數(shù)據(jù),列頭等數(shù)據(jù)
const dataUploadchildClick = (data, colunm) => { state.colunm = colunm; state.tempData = data; getData(); dataUpload.value.cancel(); };
2.3.2設(shè)置數(shù)據(jù)對應(yīng)關(guān)系
定義后臺需要的列
SelectData: [ {key: 'zkzh', type: '', value: '', selectValue: '', title: '準(zhǔn)考證號'}, {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: '考區(qū)'}, {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: '場次'} ]
創(chuàng)建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>
接受參數(shù)定義
props: { itemKey: String, name: String, type: String, value: String, selectValue: String, colunm: Object },
頁面變量定義,這里定義的默認(rèn)option主要有三個
- 0 固定值,這一列為固定值,不從表格中讀取
- 1 下拉框,從導(dǎo)入數(shù)據(jù)中選擇
- 2 自動生成,這里主要是特殊業(yè)務(wù),可以進行自定義擴展
const state = reactive({ value: ref(''), type: ref(''), selectValue: ref([]), ValueOptions:[{}], options: [ { value: '0', label: '固定值', }, { value: '1', label: '下拉框', }, { value: '2', label: '自動生成', }, ], });
監(jiān)聽下拉框選擇類型:
watch(() => state.type, (newVal) => { context.emit('update:type', newVal); } );
監(jiān)聽下拉框選擇固定,文本框輸入的值:
watch(() => state.value, (newVal) => { context.emit('update:value', newVal) })
監(jiān)聽下拉框選擇下拉框,后面下拉框選擇的值:
watch(() => state.selectValue, (newVal) => { context.emit('update:selectValue', newVal) })
監(jiān)聽表格的列,動態(tài)加載下拉框綁定的值:
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進行數(shù)據(jù)提交
提交數(shù)據(jù)到后臺進行處理,這里可以根據(jù)自己的業(yè)務(wù)進行驗證,或則進行其它擴展
const Save = () => { if (state.tempData.length == 0) { state.active=1; ElMessage.warning('請導(dǎo)入考生數(shù)據(jù)'); return; } let CheckSelectData = true; state.SelectData.forEach((value, index) => { if (!value.type) { CheckSelectData = false } }); if (!CheckSelectData) { state.active=2; ElMessage.warning('請設(shè)置完成數(shù)據(jù)對應(yīng)關(guān)系'); return; } if (state.tableTimeData.data.length==0){ state.active=3; ElMessage.warning('請?zhí)砑訄龃螖?shù)據(jù)'); return; } if (!state.ExamData.jiancheng||!state.ExamData.kaikaonianyue||!state.ExamData.quancheng){ state.active=4; ElMessage.warning('請設(shè)置任務(wù)相關(guān)信息'); 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
定義實體對前臺導(dǎo)入數(shù)據(jù)進行接收:
public class GenerateCheckTemplate { /// <summary> /// 考試任務(wù)對象 /// </summary> public ExamData ExamData { get; set; } /// <summary> /// 數(shù)據(jù)對應(yīng)關(guān)系對象 /// </summary> public List<Correspondence> SelectData { get; set; } /// <summary> /// 導(dǎo)入數(shù)據(jù) /// </summary> public List<Dictionary<string, string>> tempData { get; set; } /// <summary> /// 導(dǎo)入數(shù)據(jù)列集合 /// </summary> public List<Colunm> colunm { get; set; } /// <summary> /// 場次對象 /// </summary> public tableTimeData tableTimeData { get; set; } } /// <summary> /// 考試任務(wù)對象 /// </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> /// 數(shù)據(jù)對應(yīng)關(guān)系對象 /// </summary> public class Correspondence { /// <summary> /// key /// </summary> public string key { get; set; } /// <summary> /// 下拉選擇數(shù)據(jù) /// </summary> public string selectValue { get; set; } /// <summary> /// 標(biāo)題 /// </summary> public string title { get; set; } /// <summary> /// 類型 0 固定值 1下拉框選擇 /// </summary> public string type { get; set; } /// <summary> /// 固定值數(shù)據(jù) /// </summary> public string value { get; set; } } /// <summary> /// 表格對象 /// </summary> public class tableTimeData { /// <summary> /// 提交表格數(shù)據(jù) /// </summary> public List<ExamTime> data { get; set; } /// <summary> /// 總數(shù) /// </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> /// 結(jié)束時間 /// </summary> public string endTime { get; set; } /// <summary> /// 是否編輯狀態(tài) /// </summary> public bool Edit { get; set; } } /// <summary> /// 導(dǎo)入數(shù)據(jù)列對象 /// </summary> public class Colunm { /// <summary> /// 鍵值 /// </summary> public string key { get; set; } /// <summary> /// 值 /// </summary> public string lable { get; set; } }
然后處理前端傳過來的數(shù)據(jù)轉(zhuǎn)換成需要的數(shù)據(jù),這里type需要修改成枚舉類型,類型可以根據(jù)需求進行擴展
/// <summary> /// 處理前端傳過來的數(shù)據(jù) /// </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); } }
整體的功能到這里基本上實現(xiàn)了,具體的細(xì)節(jié)可能需要根據(jù)不同的項目進行優(yōu)化,有更多的方案可以一起進行交流
附上源碼地址:https://gitee.com/wyf854861085/file-upload.git
到此這篇關(guān)于Vue3實現(xiàn)動態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法詳解的文章就介紹到這了,更多相關(guān)Vue動態(tài)導(dǎo)入Excel數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue2.0 + element UI 中 el-table 數(shù)據(jù)導(dǎo)出Excel的方法
- vue項目預(yù)覽excel表格功能(file-viewer插件)
- vue項目使用luckyexcel預(yù)覽excel表格功能(心路歷程)
- 使用SpringBoot+EasyExcel+Vue實現(xiàn)excel表格的導(dǎo)入和導(dǎo)出詳解
- vue使用axios導(dǎo)出后臺返回的文件流為excel表格詳解
- vue項目中常見的三種文件類型在線預(yù)覽實現(xiàn)(pdf/word/excel表格)
- vue中el-table前端如何導(dǎo)出excel數(shù)據(jù)表格
相關(guān)文章
Vue.js中使用${}實現(xiàn)變量和字符串的拼接方式
這篇文章主要介紹了Vue.js中使用${}實現(xiàn)變量和字符串的拼接方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Vue如何獲取new Date().getTime()時間戳
在Web開發(fā)中,前端使用Vue.js獲取的是毫秒級時間戳,而PHP后端則是秒級時間戳,處理此類問題時,需要將PHP的時間戳乘以1000轉(zhuǎn)換為毫秒級,以保證數(shù)據(jù)的一致性和正確的邏輯判斷2024-10-10vue?parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽
這篇文章主要介紹了vue?parseHTML函數(shù)源碼解析之析器遇到結(jié)束標(biāo)簽的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07