本项目是一个基于Vue3+ECharts5+datav打造的一个每日疫情数据大屏可视化开源项目;通过nginx部署在阿里云服务器上
效果展示
在线预览地址:http://lj520zz.top/ 服务器已过期
请大家动动手点点star
项目前台源码地址:https://github.com/qdfudimo/vue3-screen
项目后台源码地址:https://github.com/qdfudimo/serve-screen
技术栈添加链接描述
前台技术栈
-
vue3
-
vite
-
echarts5 官方社区停用 以下几个社区链接
社区链接 http://www.ppchart.com/#/
https://www.isqqw.com/#/homepage
http://chart.majh.top/
http://analysis.datains.cn/finance-admin/index.html#/chartLib/all
https://www.isqqw.com/ -
datav datav组件库地址
大屏适配方案
案例中采用了css3的缩放transform: scale(X,y)属性,改变分辨率时,scale的值是变化的。 通过获取当前屏幕大小screen和当前窗口大小,我们只要监听浏览器窗口大的小,同时控制变化的比例就可以了。
项目中通过创建一个ScaleBox缩放组件包裹住所有组件
scalebox组件采用css3的缩放transform: scale(X,y)
const { width, height } = screenconst w = window.innerWidth / widthconst h = window.innerHeight / height
this.style.transform = 'scale(' + x +"," + y ') translate(-50%, -50%)'
后续添加了屏幕宽度小于800高度小于600时的完整显示
let getScale = () => {// const wh = window.innerHeight / height;// const ww = window.innerWidth / width;// return [wh,ww];const w = window.innerWidth / widthconst h = window.innerHeight / heightif (window.innerWidth <= 800 || window.innerHeight <= 600) {let scale = w < h ? w : h;return [scale, scale]}return [h, w]
}
let setScale = (e) => {// 缩放比scale.h = getScale()[0];scale.w = getScale()[1];
}
let reCalc = debounce(setScale)
onMounted(() => {setScale();window.addEventListener("resize", reCalc);
})
onUnmounted(() => {window.removeEventListener("resize", reCalc)
})
也可以使用vue3的css样式绑定 https://cn.vuejs.org/api/sfc-css-features.html#v-bind-in-css
<script setup>
const theme = {color: 'red'
}
</script>
<style scoped>
p {color: v-bind('theme.color');
}
</style>
也使用了阿里的字体图标
通过vite-plugin-svg-icons 使用SVG图片
- 安装 vite-plugin-svg-icons
npm i vite-plugin-svg-icons -D
// 或者
yarn add vite-plugin-svg-icons -D
- main.js引入
import 'virtual:svg-icons-register';
- 创建svg图片文件夹
- 在vite.config.js中配置
import {createSvgIconsPlugin
} from 'vite-plugin-svg-icons'
import path from 'path'
defineConfig({server: {//反向代理proxy: {// 选项写法// '/api': {// target: 'http://jsonplaceholder.typicode.com',// changeOrigin: true,// rewrite: (path) => path.replace(/^\/api/, '')// },// // 选项写法[config.VITE_API_BASE_URL]: {target: config.VITE_APP_BASE_SERVER,changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, '')},// Proxying websockets or socket.io// '/socket.io': {// target: 'ws://localhost:3000',// ws: true// }}},plugins: [createSvgIconsPlugin({// Specify the icon folder to be cachediconDirs: [path.resolve(process.cwd(), 'src/icons')],// Specify symbolId formatsymbolId: 'icon-[dir]-[name]',})],
})
5.新建svg组件
<template><svg :class="svgClass" v-bind="$attrs"><use :xlink:href="iconName"></use></svg>
</template><script setup lang="ts">
import { computed } from 'vue'
const props = defineProps({name: {type: String,required: true},color: {type: String,default: '#e6e6e6'}
})
const iconName = computed(() => `#icon-${props.name}`)
const svgClass = computed(() => {if (props.name) return `svg-icon icon-${props.name}`return 'svg-icon'
})
</script><style scoped>
.svg-icon {width: 1em;height: 1em;fill: currentColor;vertical-align: middle;
}
</style>
- main.js全局引入使用
import SvgIcon from '@/components/SvgIcon.vue'
const app = createApp(App)
app.component("svg-icon",SvgIcon)
组件中使用
//name svg文件名
<svg-icon name="empty" style="fontSize:120px"></svg-icon>
后台数据
koa2+axios 将爬取网易新闻的每日接口
通过定时任务每日爬取网易新闻的接口数据,保存为json文件,然后读取文件数据,请求数据时返回给前台。
每天定时下午先判断今日是否有数据,有则删除前一日的json数据,无则不删除
const schedule = require('node-schedule');
const {getData
} = require("../api")
const {formatBeforetTime,formatTime
} = require("../util/moment")
const {checkDirFile,emptyDir,
} = require("../util/index")
/*** 定时任务调接口*/
function scheduleCronstyle() {schedule.scheduleJob('30 0 10 * * *', function () {console.log('scheduleCronstyle:' + new Date());getData()});schedule.scheduleJob('30 0 14 * * *', function () {console.log('scheduleCronstyle:' + new Date());getData()});
}
/*** 定时任务删除上一天json文件*/
function scheduleDelFile() {schedule.scheduleJob('30 3 16 * * *', function () {let fileName = formatBeforetTime();let fileNamepre = formatTime();if (checkDirFile("areaData", fileNamepre) && checkDirFile("areaData", fileName)) {emptyDir("areaData", fileName)console.log(`删除文件areaData${fileName}`);}else {console.log(`没有删除文件areaData`);}if (checkDirFile("chinaData", fileNamepre) && checkDirFile("chinaData", fileName)) {emptyDir("chinaData", fileName)console.log(`删除文件chinaData${fileName}`);}});