项目介绍
第二个大屏可视化
,整个项目利用scale
进行按比例适配。 图表更加复杂,涉及到图表的叠加,mark,地图,g6流程图的能等
始终保持比例适配(本项目方案),始终满屏适配(项目一).
echarts绘制较为复杂图表,更深入锻炼echarts.
按比例用scale适配思路
- 开发项目直接使用设计图量出的尺寸填写大小
- 页面打开,读取当前屏幕宽,高
- 按当前屏幕宽高,通过scale缩放整个项目
- 让项目高度,横向竖向始终居中
最终实现效果
使用vite搭建vue项目
然后在项目根目录下 index.html
<body><div id="app"></div><script>function setScale(){const designWidth=4352;const designHeight=1536;const nowWidth=document.documentElement.clientWidth //window.screnn.widthconst nowHeight=document.documentElement.clientHeightconst designRatio=designWidth/designHeight;const nowRatio=nowWidth/nowHeight;const app=document.getElementById("app");//根据宽高比,决定按宽度进行scale还是高度进行scaleconst scale=nowRatio<designRatio?nowWidth/designWidth:nowHeight/designHeightapp.style.transform=`scale(${scale}) translate(-50%,-50%)`}setScale();window.addEventListener("resize",setScale)</script><script type="module" src="/src/main.js"></script></body>
在assets静态目录下创建main.css 并在main.js中引入
#app{transform-origin: 0 0;width: 4352px;height: 1536px;background: url('./backf.jpg');background-size: 100% 100%;/* 让他居中 */position: fixed;top:50%;left:50%}
整体布局和基本组件
//App.vue
<script setup>import { onMounted } from 'vue';import produceChart from "@/components/chartComponent/produceChart.vue"import monthPlanChart from "@/components/chartComponent/monthPlanChart.vue"import mapChart from "@/components/chartComponent/mapChart.vue"import qualityChart from "@/components/chartComponent/qualityChart.vue"import carType from "@/components/chartComponent/carType.vue"import complaintTable from "@/components/chartComponent/complaintTable.vue"import majorChart from "@/components/chartComponent/majorChart.vue"import g6chart from "@/components/chartComponent/g6chart.vue"</script><template><div class="container"><div class="title">作战指挥室</div><div class="column-one"><produceChart></produceChart><monthPlanChart></monthPlanChart></div><div class="column-two"><mapChart></mapChart><qualityChart></qualityChart></div><div class="column-three"><carType></carType><complaintTable></complaintTable></div><div class="column-four"><majorChart></majorChart><g6chart></g6chart></div></div></template><style scoped lang="less">.container {padding: 24px 55px;width: 100%;height: 100%;box-sizing: border-box;display: flex;justify-content: space-between;position: relative;.title {position: absolute;left: 50%;top: 20;font-size: 48px;color: red;font-weight: 800;transform: translateX(-50%);}.column-one,.column-four,.column-two,.column-three {display: flex;flex-direction: column;justify-content: space-between;}.column-one,.column-four {width: 1005px;height: 100%;}.column-two,.column-three {width: 1036px;height: 100%;}}</style>
将布局容器作为基础组件
// src/components/baseComponent/bottomChartContent.vue
<script setup>const { title } = defineProps(['title'])</script><template><div class="bottomChartContent"><div class="title"> {{ title }}</div><div class="soltContainer"><slot></slot></div></div></template><style scoped lang="less">.bottomChartContent {width: 100%;height: 558px;background: url('../../assets/backpart2.png');background-size: 100% 100%;display: flex;flex-direction: column;.title {font-size: 32px;color: white;width: 100%;text-align: center;}.soltContainer {width: 100%;flex: 1}}</style>
//components/baseComponent/topChartContent.vue
<script setup>const { title } = defineProps(['title'])</script><template><div class="topChartContainer"><div class="title"> {{ title }}</div><div class="soltContainer"><slot></slot></div></div></template><style scoped lang="less">.topChartContainer {width: 100%;height: 878px;background: url('../../assets/backpart1.png');background-size: 100% 100%;display: flex;flex-direction: column;.title {font-size: 32px;color: white;width: 100%;text-align: center;padding-top: 78px;}.soltContainer {width: 100%;flex: 1}}</style>
请求封装
// request/index.js
import axios from "axios";const request = axios.create({timeout: 3000,baseURL: import.meta.env.VITE_BASE_URL,})request.interceptors.request.use((config) => {const token = localStorage.getItem("token");if (token) {config.auth = token;}return config})request.interceptors.response.use((res) => {if (res.data.code !== 200) {alert(res.data.msg || '请求失败')}return res;}, () => {alert("请求失败")})export default request// api/index.js
import request from "@/request";export function getProduceStatus() {return request("/produceStatus");}export function getPlanData() {return request("/planData");}export function getMapData() {return request("/mapData");}export function getQualityData() {return request("/qualityData");}export function getCarSaleData() {return request("/carSaleData");}export function getComplainData() {return request("/complainData");}export function getMajorData() {return request("/majorData");}
几个echarts最基本的属性
import * as echarts from 'echarts';// 基于准备好的dom,初始化echarts实例 需要一个容器
var myChart = echarts.init(document.getElementById('main'));// 绘制图表
myChart.setOption({//标题title: {text: 'ECharts 入门示例'},//图例,说明legend: {top: '5%',left: 'center'},//提示tooltip: {trigger: 'item'},//x轴数据xAxis: {data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']},//y轴数据yAxis: {},//内容series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]
});
复杂的嵌套圆环图
<script setup>import topChartContent from '../baseComponent/topChartContent.vue';import { getProduceStatus } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"const ringnameArr = ['计划生产', '已接订单', '已经完成']function drawRing(data, dom, name) {//已完成const finish = data.finish;//未完成就是总数处以完成const unfinish = data.total - data.finish;const opt = {title: {text: name + '\n' + data.finish,left: 'center',top: 'center',textStyle: {color: "white",fontSize: 32}},series: [{type: "pie",left: 20,right: 20,radius: ['60%', '80%'],label: {show: false},data: [{value: finish,itemStyle: {color: "#017ed8"}}, {value: unfinish,itemStyle: {color: "rgba(6,34,73,0.8)"}}]},{name: "out",type: "pie",radius: ['75%', '78%'],label: {show: false,},data: [100],itemStyle: {color: "rgba(8,67,120,0.5)"}},{name: "out",type: "pie",radius: ['48%', '50%'],label: {show: false,},data: [100],itemStyle: {color: "rgba(8,67,120,0.5)"}}]}const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getProduceStatus().then((res) => {const data = res.data.data;data.forEach((ringData, index) => {drawRing(ringData, document.getElementById('ring' + index), ringnameArr[index])})})})</script><template><topChartContent title="生产销售完成情况"><div class="produceChart"><div class="ring" v-for="(item, index) in ringnameArr" :id="'ring' + index"></div></div></topChartContent></template><style scoped lang="less">.produceChart {width: 100%;height: 100%;display: flex;.ring {flex: 1}}</style>
复杂的折线图
<script setup>import bottomChartContent from '../baseComponent/bottomChartContent.vue';import { getPlanData } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"import { axisBaseStyle } from '@/chartConfig.js'const commonLineStyle = {type: "line",smooth: true,lineStyle: {width: 5}}function drawLine(data, dom) {const finishArr = [];const targetArr = [];const xAxisData = [];const markLine = []data.forEach((month) => {xAxisData.push(month.month);finishArr.push(month.finish);targetArr.push(month.target);markLine.push({type: "line",lineStyle: {color: '#537598',width: 5},data: [[month.month, month.target],[month.month, month.finish]]})})const opt = {color: ["#2499f8", "#ff9900"],tooltip: {formatter(obj) {const month = obj.data[0];const dataitem = data.find((item) => {if (item.month === month) {return item;}})let _target = dataitem.target;let _finish = dataitem.finish;let percent = (_finish / _target) * 100return `${month} 完成率 ${percent.toFixed(0)}%`},textStyle: {fontSize: 26}},grid: {top: "20%",bottom: "30%",left: '15%'},legend: {top: '35px',textStyle: {color: "white",fontSize: 26}},yAxis: {...axisBaseStyle,splitLine: {lineStyle: {color: "#045597",width: 4}}},xAxis: {...axisBaseStyle,type: "category",data: xAxisData},series: [{...commonLineStyle,name: "计划生产",data: targetArr,},{...commonLineStyle,name: "实际生产",data: finishArr,},...markLine]}const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getPlanData().then((res) => {drawLine(res.data.data, document.getElementById('monthPlan'))})})</script><template><bottomChartContent title="月计划完成情况"><div id="monthPlan"></div></bottomChartContent></template><style scoped lang="less">#monthPlan {width: 100%;height: 100%;}</style>
echart绘制地图效果
拿到数据。 搜索阿里云datav https://datav.aliyun.com/portal/school/atlas/area_selector?spm=a2crr.23498931.0.0.176a15ddAwYvpV
点击可视化学院复制数据
<script setup>import { getMapData } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"import chinajson from "../../assets/china.json"echarts.registerMap("china", chinajson)function drawMap(data, dom) {let _sortData = data.sort((pre, now) => {return now.value - pre.value;})_sortData = _sortData.map((item) => {return [...item.position,item.value,item.name]})const top5Arr = _sortData.splice(0, 5);const top5Marker = top5Arr.map((item, index) => {return {name: item[3],coord: [item[0], item[1]],value: index}})const opt = {geo: {top: "25%",map: "china",scaleLimit: {min: 1.5,max: 10},emphasis: {itemStyle: {areaColor: "#016bb5"},label: {show: false}},itemStyle: {areaColor: "#016bb5"},roam: true,},series: [{type: "scatter",coordinateSystem: "geo",symbolSize: (dataItem) => {const value = Number(dataItem[2]);return value / 30;},itemStyle: {color: 'rgba(255,255,0,0.8)',},data: _sortData},{type: "effectScatter",coordinateSystem: "geo",markPoint: {symbol: "pin",itemStyle: {color: "red"},symbolSize: 60,label: {fontSize: 26,formatter(val) {return val.data.value + 1;}},data: top5Marker},symbolSize: 30,itemStyle: {color: 'white',},data: top5Arr}]}const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getMapData().then((res) => {drawMap(res.data.data, document.getElementById("mapContent"))})})</script><template><div id="mapContent"></div></template><style scoped lang="less">#mapContent {width: 100%;height: 878px;padding-top: 88px;box-sizing: border-box;}</style>
echarts绘制等高堆叠柱状图
<script setup>import bottomChartContent from '../baseComponent/bottomChartContent.vue';import { getQualityData } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"import { axisBaseStyle } from '@/chartConfig.js'function drawBar(data, dom) {const goodArr = [];const badArr = []const xAxisData = [];data.forEach((item) => {xAxisData.push(item.month);const total = item.good + item.bad;goodArr.push(item.good / total);badArr.push(item.bad / total)})const opt = {color: ["#2499f8", '#fe9901'],grid: {bottom: "30%"},xAxis: {...axisBaseStyle,type: "category",data: xAxisData},yAxis: {...axisBaseStyle,splitLine: {show: false},axisLabel: {fontSize: 28,color: "white",formatter(val) {return val * 1000}}},series: [{type: "bar",stack: "total",data: goodArr,label: {show: true,color: "white",fontSize: 26,formatter(value) {const originData = data.find((item) => {if (item.month === value.name) {return item;}})return originData.good}}},{type: 'bar',stack: "total",data: badArr,label: {show: true,fontSize: 26,color: "red",formatter(value) {const originData = data.find((item) => {if (item.month === value.name) {return item;}})return originData.bad}}}]}const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getQualityData().then((res) => {drawBar(res.data.data, document.getElementById('quality'))})})</script><template><bottomChartContent title="质量指标分析"><div id="quality"></div></bottomChartContent></template><style scoped lang="less">#quality {width: 100%;height: 100%;}</style>
echarts旭日图和表格
<script setup>import { getCarSaleData } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"import chinajson from "../../assets/china.json"echarts.registerMap("china", chinajson)function drawCarType(data, dom) {let _data = [];_data = data.map((item) => {return {name: item.carType,value: item.value,children: item.children}})const opt = {color: ['#ff9900', '#99cc66', '#0066ff'],series: [{type: "sunburst",radius: ['40%', '80%'],label: {rotate: 'tangential',color: "white",fontSize: 24},data: _data}]}const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getCarSaleData().then((res) => {drawCarType(res.data.data, document.getElementById("carType"))})})</script><template><div id="carType"></div></template><style scoped lang="less">#carType {width: 100%;height: 878px;padding-top: 88px;box-sizing: border-box;}</style>
横向进度图
<script setup>import topChartContent from '../baseComponent/topChartContent.vue';import { getMajorData } from '@/api'import { onMounted } from 'vue';import * as echarts from "echarts"import { axisBaseStyle } from '@/chartConfig.js'function drawMajor(data, dom) {const yAxisData = [];const arr1 = [];const arr2 = [];const arr3 = [];const arr4 = [];const allArr = [arr1, arr2, arr3, arr4]const nameArr = ['项目投标', '投标进度', '项目进行', '项目交付']data.forEach((item) => {const process = item.processState;yAxisData.push(item.name);process.forEach((processItem) => {const category = {"项目投标": (val) => {arr1.push({value: val,label: {show: val > 0}})},"投标进度": (val) => {arr2.push({value: val,label: {show: val > 0}})},"项目进行": (val) => {arr3.push({value: val,label: {show: val > 0}})},"项目交付": (val) => {arr4.push({value: val,label: {show: val > 0}})}}category[processItem.stepName](processItem.finishPercent)})})const opt = {legend: {bottom: 120,textStyle: {color: 'white',fontSize: 32},itemHeight: 30,itemWidth: 50,itemGap: 50},grid: {left: "15%",bottom: '25%'},yAxis: {...axisBaseStyle,type: "category",data: yAxisData},xAxis: {splitLine: {show: false},axisLabel: {show: false}},series: []}for (let i = 0; i < 4; i++) {opt.series.push({type: "bar",name: nameArr[i],label: {show: true,formatter(val) {if (val.value !== 0) {return val.value + "%"}},fontSize: 28,color: 'white'},stack: 'total',data: allArr[i]})}console.log(opt);const echartobj = echarts.init(dom);echartobj.setOption(opt)}onMounted(() => {getMajorData().then((res) => {drawMajor(res.data.data, document.getElementById("majorContainer"))})})</script><template><topChartContent title="重大业务进度"><div id="majorContainer"></div></topChartContent></template><style scoped lang="less">#majorContainer {width: 100%;height: 100%;}</style>
g6关系图绘制
<script setup>import bottomChartContent from '../baseComponent/bottomChartContent.vue';import { onMounted } from 'vue';import g6 from "@antv/g6"const baseNodeConfig = {type: "rect",labelCfg: {style: {fill: "white",fontSize: 24}},style: {fill: '#1967af'},size: [120, 50]}const lineStyle = {stroke: '#ff9900',lineWidth: 5,endArrow: {path: g6.Arrow.vee(10, 10, 10),d: 10}}onMounted(() => {const data = {nodes: [{...baseNodeConfig,x: 100,y: 100,type: "rect",id: "接单",label: "接单"},{...baseNodeConfig,x: 300,y: 100,type: "rect",id: '生产',label: "生产",anchorPoints: [[1, 0.5],[1, 1],[0, 0.5]],},{...baseNodeConfig,x: 500,y: 100,type: "rect",id: '检测',label: "检测"},{...baseNodeConfig,x: 700,y: 100,type: "rect",id: '销售',label: "销售"},{...baseNodeConfig,x: 900,y: 100,type: "rect",id: '售后',label: "售后",anchorPoints: [[0, 0.5],[1, 0.5]],},{...baseNodeConfig,x: 800,y: 300,type: "rect",anchorPoints: [[1, 0],[0, 0]],id: '回厂',label: "回厂"},],edges: [{source: "接单",target: '生产',style: {...lineStyle}},{source: "生产",target: '检测',style: {...lineStyle}},{source: "检测",target: '销售',style: {...lineStyle}},{source: "销售",target: '售后',style: {...lineStyle}},{source: "售后",target: '回厂',sourceAnchor: 1,targetAnchor: 0,type: "arc",style: {...lineStyle,endArrow: {path: g6.Arrow.vee(10, -10, 2)}}},{source: "回厂",target: '生产',sourceAnchor: 1,targetAnchor: 1,type: "arc",style: {...lineStyle,endArrow: {path: g6.Arrow.vee(10, -4, 2)}}}]}const graph = new g6.Graph({container: 'g6container'})graph.data(data);graph.render();})</script><template><bottomChartContent title="产线示意图"><div id="g6container"></div></bottomChartContent></template><style scoped lang="less">#g6container {width: 100%;height: 100%;}</style>
如果对你有所帮助 就点个关注吧
本篇文章是https://www.bilibili.com/video/BV1PE421w7iJ/?p=7&spm_id_from=pageDriver&vd_source=e73709c9a1618b4c6dfd58c6c40d8986
的笔记