WebUI——领导驾驶舱

WebUI领导驾驶舱

1、效果预览

在这里插入图片描述
“领导驾驶舱”可视化界面当下比较流行,因此以“档案数据管理为例”画了画界面,先附上效果,技术栈:angular + G2 + L7

需要装几个包: 
//G2 Chart
npm install @antv/g2 --save

// L7 依赖
npm install --save @antv/l7

// 第三方底图依赖
npm install --save @antv/l7-maps; 
参考:
GitHub:https://github.com/liuhuawu/data_visualization_cockpit
GeoJson:http://datav.aliyun.com/tools/atlas/#&lat=30.316551722910077&lng=106.68898666525287&zoom=3.5

附上代码:
archives1.component.html

<!-- archives1.component.html -->
<div class="head">
    <!-- header头部 -->
    <h1>档案管理数据驾驶舱</h1>
    <div class="weather">
        <span id="showTime">{{now|date:'yyyy-MM-dd'}} {{now|date:'HH:mm:ss'}}</span>
    </div>
</div>

<!-- 中部内容 -->
<div class="mainbox">
    <ul class="clearfix">
        <li>
            <div class="boxall">
                <div class="alltitle">2020各分院项目归档情况</div>
                <div id="container1"></div>
                <div class="boxfoot"></div>
            </div>
            <div class="boxall" style="height: 22rem;">
                <div class="alltitle">2020各分院项目小类</div>
                <div id="container2"></div>
                <div class="boxfoot"></div>
            </div>
        </li>

        <li>
            <div class="bar">
                <div class="barbox">
                    <ul class="clearfix">
                        <li>690</li>
                        <li>610</li>
                    </ul>
                </div>
                <div class="barbox2">
                    <ul class="clearfix">
                        <li style="color: white;">2020年总项目数</li>
                        <li style="color: white;">2020年已归档数</li>
                    </ul>
                </div>
            </div>
            <div class="map">
                <!-- <div class="map1"><img style="width: 80%;" src="../../../../assets/archives1/lbx.png"></div>
                <div class="map2"><img style="width: 75%;" src="../../../../assets/archives1/jt.png"></div>
                <div class="map3"><img style="width: 70%;" src="../../../../assets/archives1/map.png"></div> -->
                <div id="map4"></div>
            </div>
        </li>

        <li>
            <div class="boxall">
                <div class="alltitle">2020各分院年产值(百万)</div>
                <div id="container3"></div>
                <div class="boxfoot"></div>
            </div>
            <div class="boxall" style="height: 22rem;">
                <div class="alltitle">2020各月份归档/调晒情况</div>
                <div id="container4"></div>
                <div class="boxfoot"></div>
            </div>
        </li>
    </ul>
</div>

archives1.component.css

@font-face {
    font-family: electronicFont;
    src: url(../../../../font/DS-DIGIT.TTF)
}

@keyframes myfirst2 {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(359deg);
    }
}

@keyframes myfirst {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(-359deg);
    }
}

.head {
    height: 4rem;
    background: url(../../../../assets/archives1/head_bg.png) no-repeat center center;
    background-size: 100% 100%;
    position: relative;
    z-index: 100;
}

.head h1 {
    color: #fff;
    text-align: center;
    font-size: 1.5rem;
    line-height: 3rem;
}

.weather {
    position: absolute;
    right: 1rem;
    top: 0;
    line-height: .75rem;
}

.weather span {
    color: rgba(255, 255, 255, .7);
    font-size: 1rem;
    padding-right: .5rem;
    line-height: 2.6rem;
}

.mainbox {
    width: 100%;
    height: 100%;
}

li {
    list-style-type: none;
}

table {}

ol,
ul,
p,
h1,
h2,
h3,
h4,
h5,
h6 {
    padding: 0;
    margin: 0
}

.mainbox>ul {
    /* list-style: none; */
}

.mainbox>ul>li {
    float: left;
    width: 30%;
    padding: 0 .4rem;
}

.boxall {
    height: 14rem;
    border: 1px solid rgba(25, 186, 139, .17);
    padding: 0 .2rem .4rem .15rem;
    background: rgba(255, 255, 255, .04) url(../../../../assets/archives1/line.png);
    background-size: 100% auto;
    position: relative;
    margin-bottom: 2rem;
    z-index: 10;
}

.alltitle {
    padding: .4rem 0rem;
    font-size: 0.9rem;
    color: #fff;
    text-align: center;
    line-height: 1.6rem;
}

.allnav {
    height: calc(100% - 30px);
}

.bar {
    height: 8rem;
    background: rgba(101, 132, 226, .1);
    padding: .5rem;
    margin: 0rem .5rem;
}

.barbox {
    border: 1px solid rgba(25, 186, 139, .17);
    position: relative;
    height: 60%;
    margin: 1rem 2rem 0rem 2rem;
}

.barbox2 {
    margin: 0.4rem 2rem 0rem 2rem;
}

.barbox>ul,
.barbox2>ul {
    list-style: none;
}

.barbox li {
    font-size: 4rem;
    color: #ffeb7b;
    padding: .05rem 0;
    font-family: electronicFont;
    font-weight: bold;
}

.barbox li,
.barbox2 li {
    float: left;
    width: 50%;
    text-align: center;
    position: relative;
    z-index: 100;
}

.mainbox>ul>li:nth-child(1) {
    width: 30%;
    padding: 0;
}

.mainbox>ul>li:nth-child(2) {
    width: 40%;
    padding: 0;
}

.mainbox>ul>li:nth-child(3) {
    width: 30%;
    padding: 0;
}

.barbox:before,
.barbox:after {
    position: absolute;
    width: 2rem;
    height: .5rem;
    content: "";
}

.barbox:before {
    border-left: 2px solid #02a6b5;
    left: 0;
    border-top: 2px solid #02a6b5;
}

.barbox:after {
    border-right: 2px solid #02a6b5;
    right: 0;
    bottom: 0;
    border-bottom: 2px solid #02a6b5;
}

.barbox li:first-child:before {
    position: absolute;
    content: "";
    height: 50%;
    width: 1px;
    background: rgba(255, 255, 255, .2);
    right: 0;
    top: 25%;
}

.boxall:before,
.boxall:after {
    position: absolute;
    width: .5rem;
    height: .5rem;
    content: "";
    border-top: 2px solid #02a6b5;
    top: 0;
}

.boxall:before,
.boxfoot:before {
    border-left: 2px solid #02a6b5;
    left: 0;
}

.boxall:after,
.boxfoot:after {
    border-right: 2px solid #02a6b5;
    right: 0;
}

.map {
    position: relative;
    height: 30rem;
    z-index: 9;
}

.map1,
.map2,
.map3 {
    position: absolute;
    opacity: .5
}

.map1 {
    text-align: center;
    vertical-align: middle;
    width: 100%;
    height: 100%;
    z-index: 2;
    animation: myfirst2 15s infinite linear;
}

.map2 {
    text-align: center;
    vertical-align: middle;
    width: 100%;
    height: 100%;
    z-index: 3;
    animation: myfirst 10s infinite linear;
}

.map3 {
    text-align: center;
    vertical-align: middle;
    width: 100%;
    height: 100%;
    z-index: 1;
}

.map1 img,
.map2 img,
.map3 img {
    left: 50%;
    top: 50%;
    position: absolute;
    transform: translate(-50%, -50%);
}

.clearfix:after,
.clearfix:before {
    display: table;
    content: " "
}

.clearfix:after {
    clear: both
}

.boxfoot:before {
    border-left: 2px solid #02a6b5;
    left: 0;
}

.boxfoot:after {
    border-right: 2px solid #02a6b5;
    right: 0;
}

.boxfoot {
    position: absolute;
    bottom: 0;
    width: 100%;
    left: 0;
}

.boxfoot:before,
.boxfoot:after {
    position: absolute;
    width: 0.5rem;
    height: 0.5rem;
    content: "";
    border-bottom: 2px solid #02a6b5;
    bottom: 0;
}

archives1.component.ts

import { Component, OnInit } from '@angular/core';
import { Chart } from '@antv/g2';
import { DataSet } from '@antv/data-set/build/data-set';

//地图相关
import { Scene } from '@antv/l7';
import { ProvinceLayer } from '@antv/l7-district';
import { CityLayer } from '@antv/l7-district';
import { Mapbox } from '@antv/l7-maps';



@Component({
  selector: 'app-archives1',
  templateUrl: './archives1.component.html',
  styleUrls: ['./archives1.component.css']
})
export class Archives1Component implements OnInit {

  now: Date;

  //全局变量
  data1 = [
    { country: '未归档', year: '市政', value: 163 },
    { country: '未归档', year: '建筑', value: 203 },
    { country: '未归档', year: '规划', value: 276 },
    { country: '未归档', year: '交通', value: 408 },
    { country: '未归档', year: '园林', value: 547 },
    { country: '未归档', year: '水务', value: 729 },
    { country: '已归档', year: '市政', value: 502 },
    { country: '已归档', year: '建筑', value: 635 },
    { country: '已归档', year: '规划', value: 809 },
    { country: '已归档', year: '交通', value: 947 },
    { country: '已归档', year: '园林', value: 1402 },
    { country: '已归档', year: '水务', value: 3634 },
  ];

  data2 = [
    { name: '研究', type: '市政', value: 11 },
    { name: '咨询', type: '建筑', value: 10 },
    { name: '其他', type: '规划', value: 10 },
    { name: '专项', type: '交通', value: 14 },
    { name: '咨询', type: '园林', value: 7 },
    { name: '研究', type: '水务', value: 7 },
    { name: '其他', type: '市政', value: 14 },
    { name: '研究', type: '规划', value: 3 },
    { name: '其他', type: '水务', value: 3 },
    { name: '研究', type: '园林', value: 11 },
    { name: '咨询', type: '园林', value: 5 },
    { name: '研究', type: '建筑', value: 5 },
  ];

  data3 = [
    { type: '市政', value: 34 },
    { type: '建筑', value: 85 },
    { type: '规划', value: 103 },
    { type: '交通', value: 142 },
    { type: '园林', value: 251 },
    { type: '水务', value: 367 },
  ];

  data4 = [
    { month: '1月', city: '归档', temperature: 7 },
    { month: '1月', city: '调晒', temperature: 5 },
    { month: '2月', city: '归档', temperature: 7 },
    { month: '2月', city: '调晒', temperature: 4 },
    { month: '3月', city: '归档', temperature: 9 },
    { month: '3月', city: '调晒', temperature: 5 },
    { month: '4月', city: '归档', temperature: 14 },
    { month: '4月', city: '调晒', temperature: 8 },
    { month: '5月', city: '归档', temperature: 18 },
    { month: '5月', city: '调晒', temperature: 11 },
    { month: '6月', city: '归档', temperature: 21 },
    { month: '6月', city: '调晒', temperature: 15 },
    { month: '7月', city: '归档', temperature: 25 },
    { month: '7月', city: '调晒', temperature: 17 },
    { month: '8月', city: '归档', temperature: 26 },
    { month: '8月', city: '调晒', temperature: 16 },
    { month: '9月', city: '归档', temperature: 23 },
    { month: '9月', city: '调晒', temperature: 14 },
    { month: '10月', city: '归档', temperature: 18 },
    { month: '10月', city: '调晒', temperature: 10 },
    { month: '11月', city: '归档', temperature: 13 },
    { month: '11月', city: '调晒', temperature: 6 },
    { month: '12月', city: '归档', temperature: 9 },
    { month: '12月', city: '调晒', temperature: 5 },
  ];

  colors = ['#1A4397', '#2555B7', '#3165D1', '#467BE8', '#6296FE', '#7EA6F9'];

  initChar1(): void {
    // 计算每个柱子的占比
    const ds = new DataSet();
    const dv = ds
      .createView()
      .source(this.data1)
      .transform({
        type: 'percent',
        field: 'value', // 统计销量
        dimension: 'country', // 每年的占比
        groupBy: ['year'], // 以不同产品类别为分组
        as: 'percent',
      });

    const chart = new Chart({
      container: 'container1',
      autoFit: true,
      height: 180,
    });

    chart.data(dv.rows);
    chart.scale({
      percent: {
        min: 0,
        formatter(val) {
          return (val * 100).toFixed(2) + '%';
        },
      }
    });

    chart.tooltip({
      shared: true,
      showMarkers: false,
    });

    chart
      .interval()
      .position('year*percent')
      .color('country')
      .adjust('stack');

    chart.interaction('active-region');

    chart.render();
  }

  initChar2(): void {
    const ds = new DataSet();
    const dv = ds.createView();
    dv.source(this.data2).transform({
      type: 'percent',
      field: 'value',
      dimension: 'type',
      as: 'percent',
    });

    const colorMap = {
      市政: '#5B8FF9',
      建筑: '#61DDAA',
      规划: '#65789B',
      交通: '#F6BD16',
      园林: '#6F5EF9',
      水务: '#78D3F8',
    };

    const chart = new Chart({
      container: 'container2',
      autoFit: true,
      height: 350,
    });
    chart.data(dv.rows);
    chart.legend(false);
    chart.coordinate('theta', {
      radius: 0.5,
      innerRadius: 0.3,
    });
    chart.tooltip({
      showMarkers: false
    });
    chart
      .interval()
      .adjust('stack')
      .position('percent')
      .color('type', (val) => colorMap[val])
      .style({
        stroke: 'white',
        lineWidth: 1,
      })
      .label('type', {
        offset: -5,
        style: {
          fill: 'white',
          shadowBlur: 2,
          shadowColor: 'rgba(0, 0, 0, .45)',
        },
      });

    const ds2 = new DataSet();
    const dv2 = ds2.createView();
    dv2.source(this.data2).transform({
      type: 'percent',
      field: 'value',
      dimension: 'name',
      as: 'percent',
    });
    const outterView = chart.createView();
    outterView.data(dv2.rows);
    outterView.coordinate('theta', {
      innerRadius: 0.5 / 0.8,
      radius: 0.8,
    });
    outterView
      .interval()
      .adjust('stack')
      .position('percent')
      .color('type*name', (type, name) => colorMap[type])
      .style({
        stroke: 'white',
        lineWidth: 1,
      })
      .label('name', {
        offset: -10,
        style: {
          fill: 'white',
          shadowBlur: 2,
          shadowColor: 'rgba(0, 0, 0, .45)',
        },
      });

    chart.interaction('element-active')

    chart.render();
  }

  initChar3(): void {
    const chart = new Chart({
      container: 'container3',
      autoFit: true,
      height: 180,
    });
    chart.data(this.data3);
    chart.scale({
      value: {
        max: 400,
        min: 0,
        alias: '产值(百万)',
      },
    });
    chart.axis('type', {
      title: null,
      tickLine: null,
      line: null,
    });

    chart.axis('value', {
      label: null,
      title: {
        offset: 30,
        style: {
          fontSize: 12,
          fontWeight: 300,
        },
      },
    });
    chart.legend(false);
    chart.coordinate().transpose();
    chart
      .interval()
      .position('type*value')
      .size(16)
      .label('value', {
        style: {
          fill: '#8d8d8d',
        },
        offset: 10,
      });
    chart.interaction('element-active');
    chart.render();
  }

  initChar4(): void {
    const chart = new Chart({
      container: 'container4',
      autoFit: true,
      height: 300,
    });

    chart.data(this.data4);
    chart.scale({
      month: {
        range: [0, 1],
      },
      temperature: {
        nice: true,
      },
    });

    chart.tooltip({
      showCrosshairs: true,
      shared: true,
    });

    chart.axis('temperature', {
      label: {
        formatter: (val) => {
          return val + '个';
        },
      },
    });

    chart
      .line()
      .position('month*temperature')
      .color('city')
      .shape('smooth');

    chart
      .point()
      .position('month*temperature')
      .color('city')
      .shape('circle')
      .style({
        stroke: '#fff',
        lineWidth: 1,
      });

    chart.render();
  }

  async initMap() {
    const response = await fetch(
      'https://geo.datav.aliyun.com/areas_v2/bound/440000_full.json'

    );

    const result = await response.json();

    console.log(result.features[0].properties);

    const data = [];

    for (let i = 0; i < result.features.length; i++) {
      console.log(result.features[i].properties);

      let ft = {
        code: result.features[i].properties.adcode,
        name: result.features[i].properties.name,
        pop: result.features[i].properties.adcode
      };

      data.push(ft);
    }



    console.log(data);

    const scene = new Scene({
      id: 'map4',
      map: new Mapbox({
        center: [113.5502, 22.27],
        pitch: 0,
        style: 'blank',
        zoom: 3,
        minZoom: 3,
        maxZoom: 10
      })
    });
    scene.on('loaded', () => {
      new ProvinceLayer(scene, {
        data,
        joinBy: ['adcode', 'code'],
        adcode: ['440000'],
        depth: 2,
        label: {
          field: 'NAME_CHN',
          textAllowOverlap: false
        },
        fill: {
          color: {
            field: 'pop',
            values: this.colors
          }
        },
        popup: {
          enable: true,
          Html: props => {
            return `<span>${props.NAME_CHN}:</span><span>${props.pop}</span>`;
          }
        }
      });
    });

  }

  setTime(): void {

    this.now = new Date();
    setInterval(() => {

      this.now = new Date();

    }, 1000);
  }


  constructor() { }

  ngOnInit(): void {
    this.initChar1();
    this.initChar2();
    this.initChar3();
    this.initChar4();
    this.initMap();
    this.setTime();
  }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚大龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值