前言 自成都九月份以來疫情原因被封了一兩周,居家著實無聊,每天都是盯著微信公眾號發佈的疫情數據看,那種頁面,就我一個前端仔來說,看著是真的醜啊!(⊙_⊙)?既然醜,那就自己動手開整!項目是2022.9.5開始的,截止2022.9.12我完成了大概有八成。主要是想讓數據更加直觀,而且可離線下載(當然還 ...
前言
自成都九月份以來疫情原因被封了一兩周,居家著實無聊,每天都是盯著微信公眾號發佈的疫情數據看,那種頁面,就我一個前端仔來說,看著是真的醜啊!(⊙_⊙)?既然醜,那就自己動手開整!項目是2022.9.5開始的,截止2022.9.12我完成了大概有八成。主要是想讓數據更加直觀,而且可離線下載(當然還有裝逼!┑( ̄Д  ̄)┍)。
項目描述
為證明是有料的,先看效果圖(提前裝逼!┗|`O′|┛ 嗷~~):
項目我是公開了的( ̄m ̄)有興趣的可以下下來玩玩,這是我第一次使用vue3+ts構建項目,肯定還有不足的地方(比如ts中瘋狂的:any,一直any一直爽^o^/)。
這裡是線上鏈接
這裡是項目鏈接(歡迎star!歡迎star!歡迎star!(●'◡'●)嘿嘿嘿~)
項目中使用到的技術有:vue3、TypeScript、Three.js、Echarts、elementPlus。
項目目標
1、以為3D形式展示全球疫情分佈。
2、顯示實時疫情數值。
3、以圖表形式分析疫情數據。
4、允許下載各地疫情excel表格。
5、自動獲取用戶位置。
6、分析當地疫情數據。
7、生成當地疫情word報告。
api說明
本項目數據來源:新浪公共疫情api(新浪的數據來源於國家衛健委、各省市區衛健委、各省市區政府、港澳台官方渠道等公開數據。這也是夠權威官方了)。我主要使用了兩個新浪的api和一個太平洋網路ip地址查詢web介面。
1、https://news.sina.com.cn/project/fymap/ncp2020_full_data.json
get方式,無入參。該api可獲取全球各國大致疫情數據,以及國內的詳情疫情數據,這裡就api中的欄位做一下說明,欄位是我自己推測出來的含義,不會100%全而準(→_→):
{
"add_daily(國內今日數據)": {
"addcon(今日確診新增數)": "",
"addcure(今日治愈新增)": "",
"adddeath(今日死亡新增)": "",
"addjwsr(今日境外輸入新增)": "",
"addlocIncrNum(今日本土新增)"
},
"cachetime(數據緩存時間)": "",
"curetotal(國內治愈總數)": "",
"deathtotal(國內死亡總數)": "",
"gntotal(國內確診總數)": "",
"highAndMiddle(中高風險地列表)": [
{
"allname(全名)": "",
"list(城市列表)": "",
"province(省名)": "",
"province_high_areas(高風險區域)": "",
"province_middle_areas(中風險區域)": "",
"province_high_num(高風險區域數)": "",
"province_middle_num(中風險區域數)": "",
"province_total(風險地總數)": ""
}
],
"historylist(國內疫情歷史數據)": [
{
"cn_conNum(確診總數)": "",
"cn_cureNum(治愈總數)": "",
"cn_deathNum(死亡數)": "",
"cn_jwsrNum(境外輸入)": "",
"ymd(當前時間)": ""
}
],
"jwsrTop(境外數據前10列表)": [],
"list(全國各省疫情數據列表)": [
{
"asymptomNum(較昨日新增數)": "",
"city(城市列表)": [],
"cureNum(治愈數)": "",
"deathNum(死亡數)": "",
"econNum(現存確診數)": "",
"ename(英文省名)": "",
"jwsrNum(境外輸入數)": "",
"name(省名)": "",
"value(累計數)": ""
}
],
"locIncrProTop(本土新增前十列表)": [],
"othertotal(其他總數)": {
"certain(全球現存確診)": "",
"certain_inc(今日確診新增數)": "",
"die(全球死亡數)": "",
"die_inc(死亡新增數)": "",
"ecertain(全球治愈數)": "",
"ecertain_inc(治愈新增數)": ""
},
"times(數據截止時間)": "",
"worldlist(世界各國疫情列表)": [
{
"name(國名)": "",
"value(累計數)": "",
"econNum(確診數)": "",
"deathNum(死亡數)": "",
"cureNum(治愈數)": ""
}
]
}
2、https://gwpre.sina.cn/interface/news/ncp/data.d.json
get方式,入參:
{
mod:"province",
province(英文省名):""
}
該api可獲取國內指定省份的疫情數據,欄位我就不推斷了,可自行根據上一個api和部分英文單詞大概推斷出來(沒錯!就是我不想打字了!太TM累了!ಥ_ಥ其實這還不算折磨人的,後面使用api的時候那才叫個曲折)。
3、https://whois.pconline.com.cn/ipJson.jsp
get方式,無入參。該api可獲取使用者ip地址、省份、城市。返回結果如下:
數據使用
剛開始開發的時候,我跟以前項目開發一樣,跨域嘛,直接整個vue代理(不會vue代理模式的看這兒)不就完事兒了,果然,數據一經過代理,回來是回來了,但漢字全是\n什麼什麼鬼?亂碼?費了一番功夫查了下,不是亂碼,是unicode解碼的問題。然後我又整了個解碼的方法:
//解碼返回的unicode
function decodingStr(str: any) {
let repStr: any = str.replace(/\\/g, "%");//用%替換\
let str1 = repStr.split("jsoncallback(")[1]
let str2 = str1.split(");")[0]//截取出需要的字元串
let unStr = unescape(str2);//解碼出漢字
let jsonObj = JSON.parse(unStr);//轉換成json對象
return jsonObj;
};
這下應該可以了吧?一切很順利,開發差不多了,npm run build、git add . 、git commit -m""、git push,行雲流水!直接上gitee Pages部署發佈,完成!打開頁面一看?卧槽?f12。404?直到後面我又在網上扒拉後才明白,vue的代理在打包成dist後會被抽離失效,在gitee Pages中是不能使用vue的代理模式獲取數據的!接下來就是各種嘗試跨域,直到看到跨域兩個字人都麻了。最後發現不同域下,使用jsonp的方式來處理跨域最為簡單,jsonp原理和使用方法在這裡。
項目開始
項目是vue3的,首先你得創建啊。這裡建議使用vue腳手架的圖形化界面創建項目,命令為:vue ui
選擇手動配置:
打開TypeScript支持:
選擇vue3選項:
安裝依賴
1、npm install [email protected](安裝echarts的指定版本,因為項目中需要使用中國地圖,在4.9.0之後echarts官方移除了地圖支持,之後的版本需要下載chain.js,還得手動下載引入一遍,麻煩,這裡直接用老版本)
2、npm i element-plus(vue3對應的element-ui就是element-plus,這是官方使用文檔)
3、npm i three(看見首頁那個大地球了吧?沒錯,它就是three.js做的,感興趣的可以看這兒,還有個太陽系)
4、npm i xlsx(這個是下載excel表格的必備插件,具體使用方法看這裡)
首頁球體
1、創建宇宙(叼不叼!是不是感覺自己就是創世主!( ̄_, ̄ )):初始化場景時一定記得設置alpha: true。這裡創建宇宙我使用了這篇文章創建背景的第三種方法。
import * as THREE from "three";
//初始化球體
function init(data: any) {
dom = document.getElementById("sphereDiv"); //獲取dom
let width = dom.clientWidth;
let height = dom.clientHeight;
scene = new THREE.Scene(); //場景場景
camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000); //創建透視相機(視場、長寬比、近面、遠面)
camera.position.set(0, 0, 270); //設置相機位置
camera.lookAt(0, 0, 0);
//創建渲染器
renderer = new THREE.WebGLRenderer({
antialias: true, //抗鋸齒
alpha: true, //透明
});
renderer.setClearColor(0x000000, 0.1); //設置場景透明度
renderer.setSize(width, height); //設置渲染區域尺寸
dom.appendChild(renderer.domElement); //將渲染器添加到dom中形成canvas
createUniverse(); //創建宇宙
createStars(); //創建星辰
createLight(); //創建光源
createSphere(data); //創建球體
createOrbitControls();
render();
};
//創建宇宙(球形宇宙)
function createUniverse() {
let universeGeometry = new THREE.SphereGeometry(500, 100, 100);
let universeMaterial = new THREE.MeshLambertMaterial({
//高光材質
map: new THREE.TextureLoader().load(universeImg),
side: THREE.DoubleSide, //雙面顯示
});
//宇宙網格
let universeMesh = new THREE.Mesh(universeGeometry, universeMaterial);
universeMesh.name = "宇宙";
scene.add(universeMesh);
};
2、創建光源:為了效果,我使用了環境光與平行光源,這兩種光都會影響貼圖原本顏色,建議光源顏色設置為白色。
//創建光源
function createLight() {
let lightColor = new THREE.Color(0xffffff);
let ambient = new THREE.AmbientLight(lightColor); //環境光
ambient.name = "環境光";
scene.add(ambient);
let directionalLight1 = new THREE.DirectionalLight(lightColor);
directionalLight1.position.set(0, 0, 1000);
scene.add(directionalLight1); //平行光源添加到場景中
let directionalLight2 = new THREE.DirectionalLight(lightColor);
directionalLight2.position.set(0, 0, -1000);
scene.add(directionalLight2); //平行光源添加到場景中
let directionalLight3 = new THREE.DirectionalLight(lightColor);
directionalLight3.position.set(1000, 0, 0);
scene.add(directionalLight3); //平行光源添加到場景中
let directionalLight4 = new THREE.DirectionalLight(lightColor);
directionalLight4.position.set(-1000, 0, 0);
scene.add(directionalLight4); //平行光源添加到場景中
let directionalLight5 = new THREE.DirectionalLight(lightColor);
directionalLight5.position.set(0, 1000, 0);
scene.add(directionalLight5); //平行光源添加到場景中
let directionalLight6 = new THREE.DirectionalLight(lightColor);
directionalLight6.position.set(0, -1000, 0);
scene.add(directionalLight6); //平行光源添加到場景中
};
3、創建球體:
//創建球體
function createSphere(data: any) {
let earthSize = 100; //地球尺寸
let earthGroup = new THREE.Group(); //地球的組
let earthGeometry = new THREE.SphereGeometry(earthSize, 100, 100); //地球幾何體
let nightColor = new THREE.Color(0x999999);
let dayColor = new THREE.Color(0x444444);
//地球材質
let earthMaterial = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load(
isDay ? earthImg : earthNightImg //區分晝夜紋理
),
color: isDay ? dayColor : nightColor,
// metalness: 1, //生鏽的金屬外觀(MeshStandardMaterial材質時使用)
// roughness: 0.5, // 材料的粗糙程度(MeshStandardMaterial材質時使用)
normalScale: new THREE.Vector2(0, 5), //凹凸深度
normalMap: new THREE.TextureLoader().load(normalImg), //法線貼圖
});
let earthMesh = new THREE.Mesh(earthGeometry, earthMaterial); //地球網格
earthMesh.name = "地球";
earthGroup.add(earthMesh); //將地球網格添加到地球組中
earthGroup.name = "地球組";
scene.add(earthGroup);
createVirus(data, earthSize); //創建球面病毒
};
放大項目中的地球你會發現球體錶面是有凹凸而且反光的(就像稀泥巴一樣≡(▔﹏▔)≡),這是因為使用了three中MeshPhongMaterial材質同時設置了屬性normalScale與normalMap。
4、渲染:創建完成後記得渲染,否則是不會生效的。
//渲染
function render() {
anId.value = requestAnimationFrame(render);
document.getElementById("sphereDiv") &&
document
.getElementById("sphereDiv")!
.addEventListener("mousemove", onMousemove, false);
orbitControls.update(); //滑鼠控制項實時更新
renderer.render(scene, camera);
};
控制球體
首頁的3D球體是可以進行滑鼠控制的。
這是使用的three.js自帶的滑鼠控制項OrbitControls ,它的參數可以自己設置,這是滑鼠控制的方法:
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
//創建滑鼠控制項
function createOrbitControls() {
orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.enablePan = false; //右鍵平移拖拽
orbitControls.enableZoom = true; //滑鼠縮放
orbitControls.enableDamping = true; //滑動阻尼
orbitControls.dampingFactor = 0.05; //(預設.25)
orbitControls.minDistance = 150; //相機距離目標最小距離
orbitControls.maxDistance = 500; //相機距離目標最大距離
orbitControls.autoRotate = true; //自轉(相機)
orbitControls.autoRotateSpeed = 1; //自轉速度
orbitControls.enableRotate = true;//滑鼠左鍵控制旋轉
};
創建病毒
被瞎說!covid19可不是我創造的!(;´д`)ゞ
//創建病毒
function createVirus(data: any, earthSize: any) {
let colors = [
new THREE.Color(0xf9b8b8),
new THREE.Color(0xfe4242),
new THREE.Color(0xff0000),
]; //病毒顏色列表
let virSize = 4; //病毒大小
let pointsGroup = new THREE.Group(); //點的組
let list = JSON.parse(JSON.stringify(data));
list.forEach((e: { value: number; color: any; position: any[]; }) => {
e.value >= 10000000 && (e.color = colors[2]); //根據病毒數賦予不同顏色
e.value >= 500000 && e.value < 10000000 && (e.color = colors[1]);
e.value < 500000 && (e.color = colors[0]);
if (e.position) {
let virusMaterial = new THREE.SpriteMaterial({
color: e.color,
map: new THREE.TextureLoader().load(virusImg),
side: THREE.FrontSide, //只顯示前面
}); //病毒材質
let Sprite = new THREE.Sprite(virusMaterial); //點精靈材質
Sprite.scale.set(virSize, virSize, 1); //點大小
let lat = e.position[1]; //緯度
let lon = e.position[0]; //經度
let s = latLongToVector3(lat, lon, earthSize, 1); //坐標轉換
Sprite.position.set(s.x, s.y, s.z); //設置點的位置
Sprite.dotData = e; //將點的數據添加到dotData屬性中
Sprite.name = "病毒";
pointsGroup.add(Sprite); //添加進點的組中
}
});
pointsGroup.name = "病毒組";
scene.add(pointsGroup); //點的組添加到旋轉組中
};
球面上那些紅的、粉的、白的玩意兒就是病毒。其實就是利用three的SpriteMaterial材質加入點精靈Sprite中,再遍歷病毒的坐標列表數據,迴圈設置Sprite的position屬性,最後再將創建好的病毒組添加到宇宙場景中,值得註意的是,你一般獲取到的數據均為經緯度坐標,需要轉換為three能用的三維向量坐標,這是坐標轉換的方法:
//經緯度坐標變換(傳入e:緯度、a經度、t球半徑、o球額外距離)
function latLongToVector3(e: any, a: any, t: any, o: any) {
var r = (e * Math.PI) / 180,
i = ((a - 180) * Math.PI) / 180,
n = -(t + o) * Math.cos(r) * Math.cos(i),
s = (t + o) * Math.sin(r),
l = (t + o) * Math.cos(r) * Math.sin(i);
return new THREE.Vector3(n, s, l); //計算三維向量
};
數值增加動畫
在首頁右側和“國內分析”右側,有一排數字,那個數字在載入時是有數值增加動畫的。
本來想直接使用vue-count-to或者vue-countupjs的,但網上我扒拉了一下,發現原理好像不是很難,無非就是利用vue的數據響應式原理,但我看到的大多封裝的組件都是vue2的,vue3好像沒有。這裡我就整一個vue3+ts的版本:
<template>
<span :data-time="time" :data-value="value">{{addNum}}</span>
</template>
<script lang='ts' setup>
import { ref, computed, watch, onMounted } from 'vue';
let props = defineProps({
//動畫時間
time: {
type: Number,
default: 2
},
//停止時的值
value: {
type: Number,
default: 0
},
//千位的逗號
thousandSign: {
type: Boolean,
default: () => false
}
}),
oldValue: any = ref(0),
addNum: any = ref(0);//響應式的數值
watch(
() => props.value,
() => {
startAnimation();//值改變時開始動畫
})
function startAnimation() {
let value: number = props.value - oldValue.value;
let step = (value * 10) / (props.time * 100);
let current = 0;
let start = oldValue.value;
//定時器
let t: any = setInterval(() => {
start += step;
if (start > value) {
clearInterval(t);
start = value;
t = null;
}
if (current === start) {
return;
}
current = Math.floor(start);//取整
oldValue.value = current;
if (props.thousandSign) {
addNum.value = current.toString().replace(/(\d)(?=(?:\d{3}[+]?)+$)/g, '$1,');//添加千位符
} else {
addNum.value = current.toString();//無千位符
}
}, 10)
}
onMounted(() => {
startAnimation();
})
</script>
value是動畫終止時數值,time是動畫時間,thousandSign表示是否添加千分位符,必須保證value與time類型為Number,這是使用方法:
<addNumber class="addcure-div" :value="addcure" :time="10" :thousandSign="true" />
表格展示
頁面上的所有表格都是使用的element-plus的表格組件。這是具體使用方法。當表格做出來之後發現樣式顏色之類的並不是自己想要的。其實可以更改css變數。註意,不是全局修改,是局部修改樣式。比如你想讓表格變成這樣透明的:
那麼你可以這樣做:
<el-table :data="tabData" style="width: 100%;height: calc(100vh - 100px);
--el-table-bg-color:rgba(0,0,0,.8);
--el-table-tr-bg-color:transparent;
--el-table-header-bg-color:#333;
--el-table-header-text-color:#fff;
--el-table-text-color:#fff;
--el-table-row-hover-bg-color:#333;
--el-table-border-color:#333">
<el-table-column type="index" label="序號" width="100" />
<el-table-column prop="name" label="國家" />
<el-table-column prop="value" label="累計數" sortable />
<el-table-column prop="deathNum" label="死亡數" sortable />
<el-table-column prop="cureNum" label="治愈數" sortable />
<el-table-column prop="citycode" label="地區代碼" />
<el-table-column label="坐標">
<template #default="scope">{{ scope.row.position ? scope.row.position : "-" }}</template>
</el-table-column>
</el-table>
其中的css變數名,你可以f12獲取到。
橫向柱狀圖
首頁左側和“國內分析”這樣的橫向柱狀圖。
在echarts裡面配置項是這樣的:
//柱狀圖數據
let option: any = {
title: {
text: titName + sliceNum,
left: "center",
textStyle: {
color: "#fff",
},
},
tooltip: {
backgroundColor: "rgba(0,0,0,.5)",
borderWidth: "0",
trigger: 'axis',
textStyle: {
color: "#fff",
fontWeight: "bolder"
},
},
grid: {
top: "10%",
left: "10%",
right: "10%",
bottom: "0%",
},
xAxis: {
type: 'value',
show: false,
},
yAxis: {
type: 'category',
axisLabel: {
color: "#fff",
},
data: [],
},
series: [
{
data: [],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
},
itemStyle: {
color: color,
},
label: {
color: "#fff",
fontWeight: "bolder",
show: true,
align: "left",
formatter: "{c}",
},
}
]
}
顏色、位置、標題可自己設置。
中國地圖
在“國內分析”中有一個中國地圖,用來展示國內現存確診分佈,支持縮放拖拽。
如果你下載的最新的echarts的話,那麼你得折騰下,自己另外單獨找chain.js下載,然後引入項目,4.9.0是內置地圖的,可以直接使用。這是地圖的配置項:
let option: any = {
title: {
text: '國內各省現存分佈',
left: "center",
top: '1%',
textStyle: {
color: "#fff",
},
},
visualMap: {
min: 0,
max: 500,
left: '5%',
bottom: '5%',
text: ['高', '低'],
textStyle: {
color: '#fff',
},
calculable: true,
inRange: {
color: ['#fff', '#f00'],//顏色範圍
},
},
tooltip: {
padding: 10,
enterable: true,
transitionDuration: 0,//動畫時間
backgroundColor: "rgb(0,0,0,.8)",
borderRadius: 20,
textStyle: {
color: '#fff',
},
formatter: function (params: any) {
let tipString = "";
if (params.data.value) {
tipString =
"<div style='font-size:25px;font-weight:900;margin:10px 0px'>" + params.data.name + "</div>" +
"<div style='color:#f00;font-weight:900;'>現存:" + params.data.value + "</div>" +
"<div style='color:#888;font-weight:900;'>累計:" + params.data.allNum + "</div>" +
"<div style='color:#888;font-weight:900;'>死亡:" + params.data.deathNum + "</div>" +
"<div style='color:#888;font-weight:900;'>治愈:" + params.data.cureNum + "</div>" +
"<div style='color:#888;font-weight:900;'>較昨日新增:" + params.data.asymptomNum + "</div>" +
"<div style='color:#888;font-weight:900;'>境外輸入:" + params.data.jwsrNum + "</div>"
}
return tipString;
}
},
series: [{
name: '接入醫院數量',
type: 'map',
mapType: 'china',
zoom: 1.2,//縮放
roam: true,
scaleLimit: {
min: 1.2,//縮放限制
max: 2
},
itemStyle: {
normal: {
label: {
show: true
}
},
emphasis: {
show: true,
areaColor: '#6eb5ff',//滑鼠滑過區域顏色
label: {
show: true
}
}
},
label: {
normal: { //靜態的時候展示樣式
show: true, //是否顯示地圖省份得名稱
textStyle: {
color: "#000",
fontSize: 12
}
},
emphasis: { //動態展示的樣式
color: '#fff',
},
},
data: []
}]
};
formatter也就是hover出來的信息框,可以自定義。
歷史分析
“國內分析”是有個歷史數據展示的折線圖的,它是允許縮放調節的。
這是它的echarts配置項:
let option: any = {
// backgroundColor: "",
grid: {
// top: "15%",
// left: "5%",
// right: "5%",
// bottom: "10%",
},
title: {
text: '國內歷史數據',
left: "center",
top: '5%',
textStyle: {
color: "#fff",
},
},
tooltip: {
backgroundColor: "rgba(0,0,0,.5)",
borderWidth: "0",
trigger: 'axis',
textStyle: {
color: "#fff",
fontWeight: "bolder"
},
axisPointer: {
type: 'cross'
},
},
legend: {
data: ['確診數', '治愈數', '死亡數', '境外輸入'],
textStyle: {
color: "#fff"
},
orient: "vertical",
top: "15%",
right: "2%"
},
xAxis: {
data: lineData.map(function (item: any) {
return item.ymd;
}),
textStyle: {
color: "#fff"
}
},
yAxis: {
textStyle: {
color: "#fff",
},
},
dataZoom: [
{
startValue: ''
},
{
type: 'inside'
}
],
series: [
{
name: '確診數',
type: 'line',
lineStyle: {
color: '#f4c25e'
},
itemStyle: {
color: '#f4c25e'
},
data: lineData.map(function (item: any) {
return item.cn_conNum;
}),
zlevel: 1,
z: 1,
},
{
name: '治愈數',
type: 'line',
lineStyle: {
color: '#48c56b'
},
itemStyle: {
color: '#48c56b'
},
data: lineData.map(function (item: any) {
return item.cn_cureNum;
}),
zlevel: 1,
z: 1,
},
{
name: '死亡數',
type: 'line',
lineStyle: {
color: '#f00'
},
itemStyle: {
color: '#f00'
},
data: lineData.map(function (item: any) {
return item.cn_deathNum;
}),
zlevel: 1,
z: 1
},
{
name: '境外輸入',
type: 'line',
lineStyle: {
color: '#8903ba'
},
itemStyle: {
color: '#8903ba'
},
data: lineData.map(function (item: any) {
return item.cn_jwsrNum;
}),
zlevel: 1,
z: 1
}
]
};
表格下載
主要是利用xlsx插件,好用得很,直接傳入數據就出表格了,這是我的使用方法:
import * as XLSX from "xlsx";
//入參示例
let eg = {
fileName: "測試",//文件名
tabHead: ["國家", "人口", "測試"],//表頭列表
keyList: ["name", "population", "test"],//表頭對應的屬性名,順序必須與表頭對應
tabData: [
{ name: "中國", population: "11", test: "t1" },
{ name: "美國", population: "22", test: "t2" },
{ name: "日本", population: "35", test: "t3" }
]//對象數組
}
//導出數據表格
export default async function downloadXlsx(tabObj: any) {
let aoaList: any = [];
aoaList[0] = tabObj.tabHead; //賦值表頭列表
tabObj.tabData.forEach((tabItem: any, tabIndex: number) => {
aoaList[tabIndex + 1] = [];//該二維度數組必須多加一個元素,因為表頭占第一個元素
tabObj.keyList.forEach((keyItem: any, keyIndex: number) => {
let val = tabItem[keyItem];//獲取表格屬性的值
((typeof val == "undefined") || (val == "")) ?
(aoaList[tabIndex + 1][keyIndex] = "-") ://數據未定義或者為空則用"-"代替
(aoaList[tabIndex + 1][keyIndex] = val + "");//添加空字元串,防類型為非字元串
})
});
let workSheet = null;
workSheet = XLSX.utils.aoa_to_sheet(aoaList); //將列表數據添加到工作表
let workBook = XLSX.utils.book_new(); //創建一個工作薄
XLSX.utils.book_append_sheet(workBook, workSheet, "1"); //將工作表添加到工作薄中
await XLSX.writeFile(workBook, tabObj.fileName + ".xlsx"); //寫入文件,下載工作薄
};
結語
我做這個其實還是學到了很多東西,主要是vue2與vue3的區別。以前的this算是可以徹底拋棄了,還有就是組合式api配合setup語法糖,爽啊!就連組件引入後都不用註冊了,直接使用。不過也有註意點,用ref()聲明的響應式變數使用時需要加.vaule。至於ts,我感覺自己還是很菜啊,一直any一直爽。。。。。。ts類型系統直接被我無視了,後面還得觀摩觀摩其他大佬咋寫的。截止到現在2022.9.13成都疫情好轉要復工了,項目其實都是沒有完成的,主要還差“省內分析”和“下載當地疫情報告”,我做了alert提示,反正數據獲取到了,後面在搞,我項目是完全開源了的,有牛逼的可以直接clone下來開發完成(我想要白嫖!(╯▔皿▔)╯)。