vue3+three.js實現(xiàn)疫情可視化功能
前言
自成都九月份以來疫情原因被封了一兩周,居家著實無聊,每天都是盯著微信公眾號發(fā)布的疫情數(shù)據(jù)看,那種頁面,就我一個前端仔來說,看著是真的丑?。?⊙_⊙)?既然丑,那就自己動手開整!項目是2022.9.5開始的,截止2022.9.12我完成了大概有八成。主要是想讓數(shù)據(jù)更加直觀,而且可離線下載(當然還有裝逼!┑( ̄Д  ̄)┍)。
項目描述
為證明是有料的,先看效果圖(提前裝逼!┗|`O′|┛ 嗷~~):
項目我是公開了的( ̄m ̄)有興趣的可以下下來玩玩,這是我第一次使用vue3+ts構建項目,肯定還有不足的地方(比如ts中瘋狂的:any,一直any一直爽^o^/)。
這里是在線鏈接
這里是項目鏈接(歡迎star!歡迎star!歡迎star!(●'?'●)嘿嘿嘿~)
項目中使用到的技術有:vue3、TypeScript、Three.js、Echarts、elementPlus。
項目目標
1、以為3D形式展示全球疫情分布。
2、顯示實時疫情數(shù)值。
3、以圖表形式分析疫情數(shù)據(jù)。
4、允許下載各地疫情excel表格。
5、自動獲取用戶位置。
6、分析當?shù)匾咔閿?shù)據(jù)。
7、生成當?shù)匾咔閣ord報告。
api說明
本項目數(shù)據(jù)來源:新浪公共疫情api(新浪的數(shù)據(jù)來源于國家衛(wèi)健委、各省市區(qū)衛(wèi)健委、各省市區(qū)政府、港澳臺官方渠道等公開數(shù)據(jù)。這也是夠權威官方了)。我主要使用了兩個新浪的api和一個太平洋網(wǎng)絡ip地址查詢web接口。
1、https://news.sina.com.cn/project/fymap/ncp2020_full_data.json
get方式,無入?yún)ⅰT揳pi可獲取全球各國大致疫情數(shù)據(jù),以及國內的詳情疫情數(shù)據(jù),這里就api中的字段做一下說明,字段是我自己推測出來的含義,不會100%全而準(→_→):
{ "add_daily(國內今日數(shù)據(jù))": { "addcon(今日確診新增數(shù))": "", "addcure(今日治愈新增)": "", "adddeath(今日死亡新增)": "", "addjwsr(今日境外輸入新增)": "", "addlocIncrNum(今日本土新增)" }, "cachetime(數(shù)據(jù)緩存時間)": "", "curetotal(國內治愈總數(shù))": "", "deathtotal(國內死亡總數(shù))": "", "gntotal(國內確診總數(shù))": "", "highAndMiddle(中高風險地列表)": [ { "allname(全名)": "", "list(城市列表)": "", "province(省名)": "", "province_high_areas(高風險區(qū)域)": "", "province_middle_areas(中風險區(qū)域)": "", "province_high_num(高風險區(qū)域數(shù))": "", "province_middle_num(中風險區(qū)域數(shù))": "", "province_total(風險地總數(shù))": "" } ], "historylist(國內疫情歷史數(shù)據(jù))": [ { "cn_conNum(確診總數(shù))": "", "cn_cureNum(治愈總數(shù))": "", "cn_deathNum(死亡數(shù))": "", "cn_jwsrNum(境外輸入)": "", "ymd(當前時間)": "" } ], "jwsrTop(境外數(shù)據(jù)前10列表)": [], "list(全國各省疫情數(shù)據(jù)列表)": [ { "asymptomNum(較昨日新增數(shù))": "", "city(城市列表)": [], "cureNum(治愈數(shù))": "", "deathNum(死亡數(shù))": "", "econNum(現(xiàn)存確診數(shù))": "", "ename(英文省名)": "", "jwsrNum(境外輸入數(shù))": "", "name(省名)": "", "value(累計數(shù))": "" } ], "locIncrProTop(本土新增前十列表)": [], "othertotal(其他總數(shù))": { "certain(全球現(xiàn)存確診)": "", "certain_inc(今日確診新增數(shù))": "", "die(全球死亡數(shù))": "", "die_inc(死亡新增數(shù))": "", "ecertain(全球治愈數(shù))": "", "ecertain_inc(治愈新增數(shù))": "" }, "times(數(shù)據(jù)截止時間)": "", "worldlist(世界各國疫情列表)": [ { "name(國名)": "", "value(累計數(shù))": "", "econNum(確診數(shù))": "", "deathNum(死亡數(shù))": "", "cureNum(治愈數(shù))": "" } ] }
2、https://gwpre.sina.cn/interface/news/ncp/data.d.json
get方式,入?yún)ⅲ?/p>
{ mod:"province", province(英文省名):"" }
該api可獲取國內指定省份的疫情數(shù)據(jù),字段我就不推斷了,可自行根據(jù)上一個api和部分英文單詞大概推斷出來(沒錯!就是我不想打字了!太TM累了!?_?其實這還不算折磨人的,后面使用api的時候那才叫個曲折)。
3、https://whois.pconline.com.cn/ipJson.jsp
get方式,無入?yún)?。該api可獲取使用者ip地址、省份、城市。返回結果如下:
數(shù)據(jù)使用
剛開始開發(fā)的時候,我跟以前項目開發(fā)一樣,跨域嘛,直接整個vue代理(不會vue代理模式的看這兒)不就完事兒了,果然,數(shù)據(jù)一經(jīng)過代理,回來是回來了,但漢字全是\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; };
這下應該可以了吧?一切很順利,開發(fā)差不多了,npm run build、git add . 、git commit -m""、git push,行云流水!直接上gitee Pages部署發(fā)布,完成!打開頁面一看?臥槽?f12。404?直到后面我又在網(wǎng)上扒拉后才明白,vue的代理在打包成dist后會被抽離失效,在gitee Pages中是不能使用vue的代理模式獲取數(shù)據(jù)的!接下來就是各種嘗試跨域,直到看到跨域兩個字人都麻了。最后發(fā)現(xiàn)不同域下,使用jsonp的方式來處理跨域最為簡單,jsonp原理和使用方法在這里。
項目開始
項目是vue3的,首先你得創(chuàng)建啊。這里建議使用vue腳手架的圖形化界面創(chuàng)建項目,命令為:vue ui
選擇手動配置:
打開TypeScript支持:
選擇vue3選項:
安裝依賴
1、npm install echarts@4.9.0(安裝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、創(chuàng)建宇宙(叼不叼!是不是感覺自己就是創(chuàng)世主!( ̄_, ̄ )):初始化場景時一定記得設置alpha: true。這里創(chuàng)建宇宙我使用了這篇文章創(chuàng)建背景的第三種方法。
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); //創(chuàng)建透視相機(視場、長寬比、近面、遠面) camera.position.set(0, 0, 270); //設置相機位置 camera.lookAt(0, 0, 0); //創(chuàng)建渲染器 renderer = new THREE.WebGLRenderer({ antialias: true, //抗鋸齒 alpha: true, //透明 }); renderer.setClearColor(0x000000, 0.1); //設置場景透明度 renderer.setSize(width, height); //設置渲染區(qū)域尺寸 dom.appendChild(renderer.domElement); //將渲染器添加到dom中形成canvas createUniverse(); //創(chuàng)建宇宙 createStars(); //創(chuàng)建星辰 createLight(); //創(chuàng)建光源 createSphere(data); //創(chuàng)建球體 createOrbitControls(); render(); }; //創(chuàng)建宇宙(球形宇宙) function createUniverse() { let universeGeometry = new THREE.SphereGeometry(500, 100, 100); let universeMaterial = new THREE.MeshLambertMaterial({ //高光材質 map: new THREE.TextureLoader().load(universeImg), side: THREE.DoubleSide, //雙面顯示 }); //宇宙網(wǎng)格 let universeMesh = new THREE.Mesh(universeGeometry, universeMaterial); universeMesh.name = "宇宙"; scene.add(universeMesh); };
2、創(chuàng)建光源:為了效果,我使用了環(huán)境光與平行光源,這兩種光都會影響貼圖原本顏色,建議光源顏色設置為白色。
//創(chuàng)建光源 function createLight() { let lightColor = new THREE.Color(0xffffff); let ambient = new THREE.AmbientLight(lightColor); //環(huán)境光 ambient.name = "環(huán)境光"; 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、創(chuàng)建球體:
//創(chuàng)建球體 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 //區(qū)分晝夜紋理 ), 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); //地球網(wǎng)格 earthMesh.name = "地球"; earthGroup.add(earthMesh); //將地球網(wǎng)格添加到地球組中 earthGroup.name = "地球組"; scene.add(earthGroup); createVirus(data, earthSize); //創(chuàng)建球面病毒 };
放大項目中的地球你會發(fā)現(xiàn)球體表面是有凹凸而且反光的(就像稀泥巴一樣≡(▔﹏▔)≡),這是因為使用了three中MeshPhongMaterial材質同時設置了屬性normalScale與normalMap。
4、渲染:創(chuàng)建完成后記得渲染,否則是不會生效的。
//渲染 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 ,它的參數(shù)可以自己設置,這是鼠標控制的方法:
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; //創(chuàng)建鼠標控件 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;//鼠標左鍵控制旋轉 };
創(chuàng)建病毒
被瞎說!covid19可不是我創(chuàng)造的?。ǎ?acute;д`)ゞ
//創(chuàng)建病毒 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]); //根據(jù)病毒數(shù)賦予不同顏色 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]; //經(jīng)度 let s = latLongToVector3(lat, lon, earthSize, 1); //坐標轉換 Sprite.position.set(s.x, s.y, s.z); //設置點的位置 Sprite.dotData = e; //將點的數(shù)據(jù)添加到dotData屬性中 Sprite.name = "病毒"; pointsGroup.add(Sprite); //添加進點的組中 } }); pointsGroup.name = "病毒組"; scene.add(pointsGroup); //點的組添加到旋轉組中 };
球面上那些紅的、粉的、白的玩意兒就是病毒。其實就是利用three的SpriteMaterial材質加入點精靈Sprite中,再遍歷病毒的坐標列表數(shù)據(jù),循環(huán)設置Sprite的position屬性,最后再將創(chuàng)建好的病毒組添加到宇宙場景中,值得注意的是,你一般獲取到的數(shù)據(jù)均為經(jīng)緯度坐標,需要轉換為three能用的三維向量坐標,這是坐標轉換的方法:
//經(jīng)緯度坐標變換(傳入e:緯度、a經(jīng)度、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); //計算三維向量 };
數(shù)值增加動畫
在首頁右側和“國內分析”右側,有一排數(shù)字,那個數(shù)字在加載時是有數(shù)值增加動畫的。
本來想直接使用vue-count-to或者vue-countupjs的,但網(wǎng)上我扒拉了一下,發(fā)現(xiàn)原理好像不是很難,無非就是利用vue的數(shù)據(jù)響應式原理,但我看到的大多封裝的組件都是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);//響應式的數(shù)值 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是動畫終止時數(shù)值,time是動畫時間,thousandSign表示是否添加千分位符,必須保證value與time類型為Number,這是使用方法:
<addNumber class="addcure-div" :value="addcure" :time="10" :thousandSign="true" />
表格展示
頁面上的所有表格都是使用的element-plus的表格組件。這是具體使用方法。當表格做出來之后發(fā)現(xiàn)樣式顏色之類的并不是自己想要的。其實可以更改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="累計數(shù)" sortable /> <el-table-column prop="deathNum" label="死亡數(shù)" sortable /> <el-table-column prop="cureNum" label="治愈數(shù)" sortable /> <el-table-column prop="citycode" label="地區(qū)代碼" /> <el-table-column label="坐標"> <template #default="scope">{{ scope.row.position ? scope.row.position : "-" }}</template> </el-table-column> </el-table>
其中的css變量名,你可以f12獲取到。
橫向柱狀圖
首頁左側和“國內分析”這樣的橫向柱狀圖。
在echarts里面配置項是這樣的:
//柱狀圖數(shù)據(jù) 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}", }, } ] }
顏色、位置、標題可自己設置。
中國地圖
在“國內分析”中有一個中國地圖,用來展示國內現(xiàn)存確診分布,支持縮放拖拽。
如果你下載的最新的echarts的話,那么你得折騰下,自己另外單獨找chain.js下載,然后引入項目,4.9.0是內置地圖的,可以直接使用。這是地圖的配置項:
let option: any = { title: { text: '國內各省現(xiàn)存分布', 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;'>現(xiàn)存:" + 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: '接入醫(yī)院數(shù)量', 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',//鼠標滑過區(qū)域顏色 label: { show: true } } }, label: { normal: { //靜態(tài)的時候展示樣式 show: true, //是否顯示地圖省份得名稱 textStyle: { color: "#000", fontSize: 12 } }, emphasis: { //動態(tài)展示的樣式 color: '#fff', }, }, data: [] }] };
formatter也就是hover出來的信息框,可以自定義。
歷史分析
“國內分析”是有個歷史數(shù)據(jù)展示的折線圖的,它是允許縮放調節(jié)的。
這是它的echarts配置項:
let option: any = { // backgroundColor: "", grid: { // top: "15%", // left: "5%", // right: "5%", // bottom: "10%", }, title: { text: '國內歷史數(shù)據(jù)', 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: ['確診數(shù)', '治愈數(shù)', '死亡數(shù)', '境外輸入'], 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: '確診數(shù)', type: 'line', lineStyle: { color: '#f4c25e' }, itemStyle: { color: '#f4c25e' }, data: lineData.map(function (item: any) { return item.cn_conNum; }), zlevel: 1, z: 1, }, { name: '治愈數(shù)', type: 'line', lineStyle: { color: '#48c56b' }, itemStyle: { color: '#48c56b' }, data: lineData.map(function (item: any) { return item.cn_cureNum; }), zlevel: 1, z: 1, }, { name: '死亡數(shù)', 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插件,好用得很,直接傳入數(shù)據(jù)就出表格了,這是我的使用方法:
import * as XLSX from "xlsx"; //入?yún)⑹纠? let eg = { fileName: "測試",//文件名 tabHead: ["國家", "人口", "測試"],//表頭列表 keyList: ["name", "population", "test"],//表頭對應的屬性名,順序必須與表頭對應 tabData: [ { name: "中國", population: "11", test: "t1" }, { name: "美國", population: "22", test: "t2" }, { name: "日本", population: "35", test: "t3" } ]//對象數(shù)組 } //導出數(shù)據(jù)表格 export default async function downloadXlsx(tabObj: any) { let aoaList: any = []; aoaList[0] = tabObj.tabHead; //賦值表頭列表 tabObj.tabData.forEach((tabItem: any, tabIndex: number) => { aoaList[tabIndex + 1] = [];//該二維度數(shù)組必須多加一個元素,因為表頭占第一個元素 tabObj.keyList.forEach((keyItem: any, keyIndex: number) => { let val = tabItem[keyItem];//獲取表格屬性的值 ((typeof val == "undefined") || (val == "")) ? (aoaList[tabIndex + 1][keyIndex] = "-") ://數(shù)據(jù)未定義或者為空則用"-"代替 (aoaList[tabIndex + 1][keyIndex] = val + "");//添加空字符串,防類型為非字符串 }) }); let workSheet = null; workSheet = XLSX.utils.aoa_to_sheet(aoaList); //將列表數(shù)據(jù)添加到工作表 let workBook = XLSX.utils.book_new(); //創(chuàng)建一個工作薄 XLSX.utils.book_append_sheet(workBook, workSheet, "1"); //將工作表添加到工作薄中 await XLSX.writeFile(workBook, tabObj.fileName + ".xlsx"); //寫入文件,下載工作薄 };
結語
我做這個其實還是學到了很多東西,主要是vue2與vue3的區(qū)別。以前的this算是可以徹底拋棄了,還有就是組合式api配合setup語法糖,爽?。【瓦B組件引入后都不用注冊了,直接使用。不過也有注意點,用ref()聲明的響應式變量使用時需要加.vaule。至于ts,我感覺自己還是很菜啊,一直any一直爽。。。。。。ts類型系統(tǒng)直接被我無視了,后面還得觀摩觀摩其他大佬咋寫的。截止到現(xiàn)在2022.9.13成都疫情好轉要復工了,項目其實都是沒有完成的,主要還差“省內分析”和“下載當?shù)匾咔閳蟾?rdquo;,我做了alert提示,反正數(shù)據(jù)獲取到了,后面在搞,我項目是完全開源了的,有牛逼的可以直接clone下來開發(fā)完成(我想要白嫖!(╯▔皿▔)╯)。
到此這篇關于vue3+three.js實現(xiàn)疫情可視化的文章就介紹到這了,更多相關vue3疫情可視化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue3成功創(chuàng)建項目后?run?serve啟動項目報錯的解決
這篇文章主要介紹了vue3成功創(chuàng)建項目后?run?serve啟動項目報錯的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03elementUI的table表格改變數(shù)據(jù)不更新問題解決
最近在做vue的項目時發(fā)現(xiàn)了一個問題,今天就來解決一下,本文主要介紹了elementUI的table表格改變數(shù)據(jù)不更新問題解決,感興趣的可以了解一下2022-02-02