Vue3實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法詳解
1. 前言
在開發(fā)工作過程中,我們會(huì)遇到各種各樣的表格數(shù)據(jù)導(dǎo)入,大部分我們的解決方案:提供一個(gè)模板前端進(jìn)行下載,然后按照這個(gè)模板要求進(jìn)行數(shù)據(jù)填充,最后上傳導(dǎo)入,這是其中一種解決方案。個(gè)人認(rèn)為還有另外一種解決方案,不一定會(huì)前面的方案好,方便,但是可以減少人為操作,減少出錯(cuò),更為通用,就是進(jìn)行動(dòng)態(tài)數(shù)據(jù)導(dǎo)入,前端進(jìn)行自定義導(dǎo)入數(shù)據(jù),配置數(shù)據(jù)對(duì)應(yīng)關(guān)系,最后提交導(dǎo)入結(jié)果。
2. 如何實(shí)現(xiàn)
2.1使用技術(shù)
后臺(tái)使用.Net6.0,前端使用Vue3
2.2實(shí)現(xiàn)思路
- 前端通過Excel表格數(shù)據(jù)導(dǎo)入,導(dǎo)入后客戶可以進(jìn)行數(shù)據(jù)編輯(未實(shí)現(xiàn))客戶數(shù)據(jù)確認(rèn)后進(jìn)行數(shù)據(jù)回寫主頁(yè)面
- 設(shè)置數(shù)據(jù)對(duì)應(yīng)關(guān)系,也就是后臺(tái)所需數(shù)據(jù)格式和前臺(tái)數(shù)據(jù)格式進(jìn)行綁定
- 數(shù)據(jù)校驗(yàn),進(jìn)行數(shù)據(jù)提交
2.3具體實(shí)現(xiàn)方案
2.3.1導(dǎo)入Excel數(shù)據(jù)
創(chuàng)建列表頁(yè)面,頁(yè)面主要布局如下

Table需要綁定的列是未知的,所以el-table綁定的el-table-column是需要?jiǎng)討B(tài)進(jìn)行加載,動(dòng)態(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: ""}],
});點(diǎn)擊導(dǎo)入報(bào)名數(shù)據(jù),彈出上傳數(shù)據(jù)頁(yè)面

這一塊的功能在之前的一篇文章有寫過:.Net6.0+Vue3實(shí)現(xiàn)數(shù)據(jù)簡(jiǎn)易導(dǎo)入功能全過程
子組件的核心代碼
頁(yè)面布局:
<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í)存儲(chǔ)全部數(shù)據(jù)
searchInput: { PageIndex: 1, PageSize: 10 },
tableData: { data: [{}], total: 0 },//表格加載當(dāng)前頁(yè)面數(shù)據(jù)
dialogVisible: false,
colunm: [{ key: '', lable: '' }]
});父頁(yè)面調(diào)用方法,進(jìn)行表格列頭加載,初始化表格數(shù)據(jù):
const childMethod = (data) => {
state.colunm = data;
state.tableData.data = [];
state.dialogVisible = true;
}
綁定表格方法,前端進(jìn)行分頁(yè)數(shù)據(jù)處理:(這里可能會(huì)有性能問題,暫時(shí)沒有仔細(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ù)方法,主要?jiǎng)討B(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ù)后,會(huì)調(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ù)對(duì)應(yīng)關(guān)系
定義后臺(tái)需要的列
SelectData: [
{key: 'zkzh', type: '', value: '', selectValue: '', title: '準(zhǔn)考證號(hào)'},
{key: 'zjlb', type: '', value: '', selectValue: '', title: '證件類別'},
{key: 'zjhm', type: '', value: '', selectValue: '', title: '證件號(hào)碼'},
{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: '考點(diǎn)號(hào)'},
{key: 'kdmc', type: '', value: '', selectValue: '', title: '考點(diǎn)名稱'},
{key: 'kch', type: '', value: '', selectValue: '', title: '考場(chǎng)號(hào)'},
{key: 'kchdz', type: '', value: '', selectValue: '', title: '考場(chǎng)地址'},
{key: 'zwh', type: '', value: '', selectValue: '', title: '座位號(hào)'},
{key: 'chc', type: '', value: '', selectValue: '', title: '場(chǎng)次'}
]
創(chuàng)建Select組件,組件核心代碼
頁(yè)面布局部分
<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
},
頁(yè)面變量定義,這里定義的默認(rèn)option主要有三個(gè)
- 0 固定值,這一列為固定值,不從表格中讀取
- 1 下拉框,從導(dǎo)入數(shù)據(jù)中選擇
- 2 自動(dòng)生成,這里主要是特殊業(yè)務(wù),可以進(jìn)行自定義擴(kuò)展
const state = reactive({
value: ref(''),
type: ref(''),
selectValue: ref([]),
ValueOptions:[{}],
options: [
{
value: '0',
label: '固定值',
},
{
value: '1',
label: '下拉框',
},
{
value: '2',
label: '自動(dòng)生成',
},
],
});
監(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)聽表格的列,動(dòng)態(tài)加載下拉框綁定的值:
watch(() => props.colunm, (newVal: any) => {
state.ValueOptions=newVal;
})
最終的效果:

父頁(yè)面進(jìn)行引用
<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進(jìn)行數(shù)據(jù)提交
提交數(shù)據(jù)到后臺(tái)進(jìn)行處理,這里可以根據(jù)自己的業(yè)務(wù)進(jìn)行驗(yàn)證,或則進(jìn)行其它擴(kuò)展
const Save = () => {
if (state.tempData.length == 0) {
state.active=1;
ElMessage.warning('請(qǐng)導(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('請(qǐng)?jiān)O(shè)置完成數(shù)據(jù)對(duì)應(yīng)關(guān)系');
return;
}
if (state.tableTimeData.data.length==0){
state.active=3;
ElMessage.warning('請(qǐng)?zhí)砑訄?chǎng)次數(shù)據(jù)');
return;
}
if (!state.ExamData.jiancheng||!state.ExamData.kaikaonianyue||!state.ExamData.quancheng){
state.active=4;
ElMessage.warning('請(qǐng)?jiān)O(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)
}
})
}
后臺(tái)定義接口:/GenerateCheckTemplate
定義實(shí)體對(duì)前臺(tái)導(dǎo)入數(shù)據(jù)進(jìn)行接收:
public class GenerateCheckTemplate
{
/// <summary>
/// 考試任務(wù)對(duì)象
/// </summary>
public ExamData ExamData { get; set; }
/// <summary>
/// 數(shù)據(jù)對(duì)應(yīng)關(guān)系對(duì)象
/// </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>
/// 場(chǎng)次對(duì)象
/// </summary>
public tableTimeData tableTimeData { get; set; }
}
/// <summary>
/// 考試任務(wù)對(duì)象
/// </summary>
public class ExamData
{
/// <summary>
/// 簡(jiǎn)稱
/// </summary>
public string jiancheng { get; set; }
/// <summary>
/// 考試年月
/// </summary>
public string kaikaonianyue { get; set; }
/// <summary>
/// 簡(jiǎn)稱
/// </summary>
public string quancheng { get; set; }
}
/// <summary>
/// 數(shù)據(jù)對(duì)應(yīng)關(guān)系對(duì)象
/// </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>
/// 表格對(duì)象
/// </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>
/// 場(chǎng)次編碼
/// </summary>
public int? ExamTimeCode { get; set; }
/// <summary>
/// 場(chǎng)次名稱
/// </summary>
public string ExamTimeName { get; set; }
/// <summary>
/// 開始時(shí)間
/// </summary>
public string startTime { get; set; }
/// <summary>
/// 結(jié)束時(shí)間
/// </summary>
public string endTime { get; set; }
/// <summary>
/// 是否編輯狀態(tài)
/// </summary>
public bool Edit { get; set; }
}
/// <summary>
/// 導(dǎo)入數(shù)據(jù)列對(duì)象
/// </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ù)需求進(jìn)行擴(kuò)展
/// <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);
}
}整體的功能到這里基本上實(shí)現(xiàn)了,具體的細(xì)節(jié)可能需要根據(jù)不同的項(xiàng)目進(jìn)行優(yōu)化,有更多的方案可以一起進(jìn)行交流
附上源碼地址:https://gitee.com/wyf854861085/file-upload.git
到此這篇關(guān)于Vue3實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法詳解的文章就介紹到這了,更多相關(guān)Vue動(dòng)態(tài)導(dǎo)入Excel數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue2.0 + element UI 中 el-table 數(shù)據(jù)導(dǎo)出Excel的方法
- vue項(xiàng)目預(yù)覽excel表格功能(file-viewer插件)
- vue項(xiàng)目使用luckyexcel預(yù)覽excel表格功能(心路歷程)
- 使用SpringBoot+EasyExcel+Vue實(shí)現(xiàn)excel表格的導(dǎo)入和導(dǎo)出詳解
- vue使用axios導(dǎo)出后臺(tái)返回的文件流為excel表格詳解
- vue項(xiàng)目中常見的三種文件類型在線預(yù)覽實(shí)現(xiàn)(pdf/word/excel表格)
- vue中el-table前端如何導(dǎo)出excel數(shù)據(jù)表格
相關(guān)文章
基于Vue實(shí)現(xiàn)本地消息隊(duì)列MQ的示例詳解
這篇文章為大家詳細(xì)主要介紹了如何基于Vue實(shí)現(xiàn)本地消息隊(duì)列MQ, 讓消息延遲消費(fèi)或者做緩存,文中的示例代碼講解詳細(xì),需要的可以參考一下2024-02-02
Vue.js中使用${}實(shí)現(xiàn)變量和字符串的拼接方式
這篇文章主要介紹了Vue.js中使用${}實(shí)現(xiàn)變量和字符串的拼接方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Vue2中無法檢測(cè)到數(shù)組變動(dòng)的原因及解決
由于某些限制,vue2不能檢測(cè)到某些情況下數(shù)組的變動(dòng),本文就將具體講解這兩種限制的解決思路2021-06-06
Vue源碼探究之虛擬節(jié)點(diǎn)的實(shí)現(xiàn)
這篇文章主要介紹了Vue源碼探究之虛擬節(jié)點(diǎn)的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04
Vue如何獲取new Date().getTime()時(shí)間戳
在Web開發(fā)中,前端使用Vue.js獲取的是毫秒級(jí)時(shí)間戳,而PHP后端則是秒級(jí)時(shí)間戳,處理此類問題時(shí),需要將PHP的時(shí)間戳乘以1000轉(zhuǎn)換為毫秒級(jí),以保證數(shù)據(jù)的一致性和正確的邏輯判斷2024-10-10
vue?parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽
這篇文章主要介紹了vue?parseHTML函數(shù)源碼解析之析器遇到結(jié)束標(biāo)簽的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Vue-cli3生成的Vue項(xiàng)目加載Mxgraph方法示例
這篇文章主要介紹了Vue-cli3生成的Vue項(xiàng)目加載Mxgraph方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

