构建左侧信息展示

1、App 组件 修改

/App.vue

<template>
  <div class="castle side-r-wrap">
    <TopSide :title="title"></TopSide>
    <TopMid></TopMid>
    <Left></Left>
  </div>
</template>

<script>
import TopSide from '@/components/TopSide'
import TopMid from '@/components/TopMid'
import Left from '@/components/Left'

export default {
  name: 'Castle',
  data () {
    return {
      title: '公安警情可视化'
    }
  },
  components: {
    TopSide,
    TopMid,
    Left
  }
}
</script>
<style scoped>
</style>
2、构建 Left.vue

/src/components/Left.vue

<template>
  <div class="left">
    <div class="diandongche">
      <h2 class="chart-title">年度刑事案件数
      </h2>
      <ul class="alarm-list">
        <li>
          <p class="alarm-list-name">
            <span>今年</span>
          </p>
          <p class="alarm-list-value">{{thisyear}}</p>
        </li>
        <li>
          <p class="alarm-list-name">
            <span>环比</span>
          </p>
          <p class="alarm-list-value" :data-state="hbstate">{{huanbi}}</p>
        </li>
        <li>
          <p class="alarm-list-name">
            <span>去年</span>
          </p>
          <p class="alarm-list-value">{{lastyear}}</p>
        </li>
      </ul>
    </div>
    <div class="caseResolved">
      <h2 class="chart-title">年度出警数</h2>
      <div class="case-resolved-chart" id="caseResolvedChart"></div>
    </div>
  </div>
</template>
<script>
  import axios from 'axios'
  import api from '../assets/scripts/tool/api'
  import Data from '../data/fantasy/castle/left'
  import MultiPie from '../assets/scripts/charts/multiPie'
  Data()

  export default {
    name: 'left',
    data () {
      return {
        thisyear: '',
        hbstate: '',
        huanbi: '',
        lastyear: ''
      }
    },
    mounted () {
      const self = this
      axios.get(api.iotalarm)
        .then(response => {
          const data = response.data.result
          self.dealDDC(data.diandongche)
          self.dealCase(data.caseResolved)
        })
        .catch(error => {
          console.error(error)
        })
    },
    methods: {
      dealDDC (data) {
        let hbstate
        let hbValue = data.huanbi
        if (hbValue < 0) {
          hbstate = 'down'
          hbValue = Math.abs(hbValue)
        } else if (hbValue === 0) {
          hbstate = 'level'
          hbValue = '- 0'
        } else if (hbValue > 0) {
          hbstate = 'up'
          hbValue = Math.abs(hbValue)
        }
        this.thisyear = data.thisyear
        this.hbstate = hbstate
        this.huanbi = hbValue
        this.lastyear = data.lastyear
      },
      dealCase (data) {
        const config = {}
        const multiPie = new MultiPie('.case-resolved-chart', config)
        multiPie.render(data)
      }
    }
  }
</script>
<style scoped>
  .diandongche {
    position: absolute;
    top: 220px;
    left: 60px;
    width: 680px;
    height: 600px;
    background: url(../assets/images/common/tip-title-bg.png) no-repeat top left;
  }

  .alarm-list {
    position: absolute;
    top: 120px;
    left: 0px;
    width: 100%;
    height: 100%;
    font-size: 0;
  }

  .alarm-list li {
    width: 50%;
    height: 190px;
    overflow: hidden;
    display: inline-block;
    margin-bottom: 10px;
  }

  .alarm-list-name span {
    font-size: 50px;
    color: #b4c7f9;
    position: relative;
    display: inline-block;
  }

  .alarm-list-name span:after {
    content: "件";
    display: inline-block;
    position: absolute;
    top: 4px;
    right: -80px;
    text-align: center;
    font-size: 30px;
    line-height: 46px;
    color: #8da5e4;
    height: 46px;
    width: 60px;
    background: #0c3f87;
    border: 1px solid #443cba;
  }

  .alarm-list li:nth-child(2) .alarm-list-name span:after {
    display: none;
  }

  .alarm-list-value {
    font-size: 60px;
    color: #1aac4e;
    position: relative;
    display: inline-block;
    margin-top:20px;
  }

  .alarm-list li:first-child .alarm-list-value {
    font-size: 90px;
    color: #44ff86;
  }

  .alarm-list li:nth-child(2) .alarm-list-value {
    margin-top: 34px;
    text-indent: 40px;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state="up"] {
    color: #ff4444;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state="level"] {
    color: #b4c7f9;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state="down"] {
    color: #44ff86;
  }

  .alarm-list li:nth-child(2) .alarm-list-value:after {
    content: "%";
    display: inline-block;
    position: absolute;
    bottom: 4px;
    right: -24px;
    font-size: 30px;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state]:before {
    content: "";
    display: inline-block;
    position: absolute;
    width: 25px;
    height: 26px;
    top: 26px;
    left: 0px;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state="up"]:before {
    background: url(../assets/images/common/huanbi-up.png) no-repeat;
  }

  .alarm-list li:nth-child(2) .alarm-list-value[data-state="down"]:before {
    background: url(../assets/images/common/huanbi-down.png) no-repeat;
  }

  .caseResolved {
    position: absolute;
    top: 830px;
    left: 60px;
    background: url(../assets/images/common/tip-title-bg.png) no-repeat top left;
  }

  .case-resolved-chart {
    width: 900px;
    height: 270px;
    margin-top: 130px;
  }
</style>
/src/data/fantasy/castle/left.js
import {
  urlReg
} from '../../../assets/scripts/tool/utils'

import Mock from 'mockjs'

const data = () => {
  Mock.mock(urlReg('/iot/overview/alarm'), {
    'code': 1,
    'msg': 'success',
    'result': {
      'diandongche': {
        'lastyear': '@natural(1,2000)',
        'thisyear': '@natural(1,2000)',
        'huanbi': '@integer(-100,100)'
      },
      'caseResolved|3': [{
        'name|+1': ['部门一', '部门二', '部门三'],
        'value': '@natural(1,2000)'
      }]
    }
  })
}
export default data
/src/assets/scripts/charts/multiPie.js
import * as d3 from 'd3'

export default class MultiPie {
  /**
   *  默认配置项
   *  @return   {[Object]}  [默认配置项]
   */
  defaultSetting() {
    return {
      width: 900,
      height: 270,
      radius: [50, 66], // [innerRadius,outerRadius]
      gap: 100, // 相邻两个图形的间距
      margin: { // 多个图形布局:从左往右,竖直方向按容器高度居中放置,故只设置左侧距离left即可
        left: 10
      },
      label: { // 名称文本样式
        normal: {
          fontSize: 32,
          color: '#46aaff',
          anchor: 'middle',
          cursor: 'pointer',
          top: 46 // 名称文本距离图案顶部的距离
        },
        emphasis: {
          fontSize: 32,
          color: '#74ffd3',
          anchor: 'middle',
          cursor: 'pointer',
          top: 46 // 名称文本距离图案顶部的距离
        }
      },
      itemStyle: {
        label: { // value值文本样式
          fontSize: 32,
          color: '#46aaff',
          anchor: 'middle',
          cursor: 'pointer',
          top: 10 // value值文本距离容器中线的偏移距离,默认放在饼图正中间
        },
        color: [ // 饼图填充色
          ['#4a8ce5', 'black'],
          ['#44ff86', 'black'],
          ['#dccc5c', 'black']
        ]
      }
    }
  }
  /**
   *  初始化,创建容器
   *  @param    {String}  selector 图表容器,支持class或id
   *  @param    {Object}  option   配置项,控制图形样式
   *  @return   {[type]}  [description]
   */
  constructor(selector, option = {}) {
    const defaultSetting = this.defaultSetting()
    this.config = Object.assign(defaultSetting, option)
    const {
      width,
      height
    } = this.config
    // 创建svg
    this.svg = d3.select(selector)
      .append('svg')
      .attr('width', width)
      .attr('height', height)
  }
  /**
   *  处理原始数据,获取pie布局转换后的数据
   *  @param    {Array}  data    原始数据
   *  @return   {Array}  dataset 转换后的数据
   */
  getDataset(data) {
    let dataset = []
    const clockwisePie = d3.pie() // 顺时针,针对数据类型:[small,bigger]
    const anticlockwisePie = d3.pie() // 逆时针,针对数据类型:[bigger,small]
      .startAngle(0)
      .endAngle(-2 * Math.PI)
    // 求取总数:sum
    let sum = 0
    data.map(d => {
      sum += parseInt(d.value, 10)
    })
    data.map((d) => {
      let value = d.value
      let rate = Math.max(Math.floor(value * 100 / sum), 1)
      let rateData = [rate, 100 - rate]
      let dealData = rate >= 50 ? clockwisePie(rateData) : anticlockwisePie(rateData)
      dataset.push(dealData)
    })
    return dataset
  }
  /**
   *  绘制图案底部的名称文本
   *  @param    {Object}  chart 包裹文本的外层g容器
   *  @param    {Object}  info  单组原始数据,包括name和value
   *  @return   {[type]}  [description]
   */
  renderName(chart, info) {
    const {
      radius: [, outerRadius],
      label: {
        normal: {
          fontSize: fontSizeNor,
          color: colorNor,
          anchor: anchorNor,
          top: topNor,
          cursor: cursorNor
        },
        emphasis: {
          fontSize: fontSizeEmp,
          color: colorEmp,
          anchor: anchorEmp,
          top: topEmp,
          cursor: cursorEmp
        }
      }
    } = this.config
    chart.select('.pie-name')
      .attr('font-size', fontSizeNor)
      .attr('fill', colorNor)
      .attr('text-anchor', anchorNor)
      .attr('transform', `translate(0, ${outerRadius + topNor})`)
      .attr('cursor', cursorNor)
      .text(info.name)
      .on('mouseover', function () {
        d3.select(this)
          .attr('font-size', fontSizeEmp)
          .attr('fill', colorEmp)
          .attr('text-anchor', anchorEmp)
          .attr('transform', `translate(0, ${outerRadius + topEmp})`)
          .attr('cursor', cursorEmp)
      })
      .on('mouseout', function () {
        d3.select(this)
          .attr('font-size', fontSizeNor)
          .attr('fill', colorNor)
          .attr('text-anchor', anchorNor)
          .attr('transform', `translate(0, ${outerRadius + topNor})`)
          .attr('cursor', cursorNor)
      })
  }
  /**
   *  绘制图案中间的value值文本
   *  @param    {Object}  chart 包裹文本的外层g容器
   *  @param    {[type]}  info  单组原始数据,包括name和value
   *  @return   {[type]}  [description]
   */
  renderValue(chart, info) {
    const {
      itemStyle: {
        label: {
          fontSize,
          color,
          anchor,
          cursor,
          top
        }
      }
    } = this.config
    chart.select('.pie-value')
      .attr('font-size', fontSize)
      .attr('fill', color)
      .attr('text-anchor', anchor)
      .attr('transform', `translate(0,${top})`)
      .attr('cursor', cursor)
      .text(info.value)
  }
  /**
   *  绘制单个Pie图案
   *  @param    {Objec}  chartName  单个图案的外层g容器
   *  @param    {Array}   pieData   绘制饼图的数据(已经过布局处理)
   *  @param    {Object}  info      该图案的原始数据,包括name和value
   *  @param    {Array}   color     填充饼图的两个颜色值
   *  @return   {[type]}  [description]
   */
  creatPie(chartName, pieData, info, color) {
    const {
      radius: [innerRadius, outerRadius]
    } = this.config
    const arc = d3.arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius)
    const chart = this.svg.select(chartName)
    const update = chart.selectAll('path').data(pieData)
    const enter = update.enter()
    update.exit().remove()
    // 绘制饼图图案
    enter.append('path')
    chart.selectAll('path').data(pieData)
      .attr('fill', (d, i) => {
        return color[i]
      })
      .attr('d', d => {
        return arc(d)
      })
    // 绘制名称--name
    enter.append('text').attr('class', 'pie-name')
    this.renderName(chart, info)

    // 绘制value值
    enter.append('text').attr('class', 'pie-value')
    this.renderValue(chart, info)
  }
  render(data) {
    let dataset = this.getDataset(data)
    const update = this.svg.selectAll('.item')
      .data(dataset)
    update.enter().append('g').attr('class', 'item')
    update.exit().remove()
    // 多个图形布局:从左往右,相邻图形间隔为配置项----config.gap
    const {
      height,
      radius: [, R],
      gap,
      margin: {
        left
      },
      itemStyle: {
        color
      }
    } = this.config
    this.svg.selectAll('.item').data(dataset)
      .attr('transform', (d, i) => {
        return `translate(${R + left + 2 * R * i + i * gap},${height / 2})`
      })
      .attr('class', (d, i) => {
        return `item${i} item`
      })
    // 逐个绘制饼图
    dataset.map((d, i) => {
      this.creatPie(`.item${i}`, d, data[i], color[i])
    })
  }
}

results matching ""

    No results matching ""