Browse Source

Signed-off-by: 1004749546@qq.com <1004749546@qq.com>
首页增加图表

1004749546@qq.com 4 năm trước cách đây
mục cha
commit
73969d2410

+ 13 - 0
package-lock.json

@@ -5656,6 +5656,14 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "echarts": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz",
+      "integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==",
+      "requires": {
+        "zrender": "4.3.2"
+      }
+    },
     "editorconfig": {
       "version": "0.15.3",
       "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
@@ -17358,6 +17366,11 @@
           "dev": true
         }
       }
+    },
+    "zrender": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz",
+      "integrity": "sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g=="
     }
   }
 }

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "ant-design-vue": "^1.5.2",
     "axios": "^0.19.0",
     "core-js": "2.6.9",
+    "echarts": "^4.9.0",
     "enquire.js": "^2.1.6",
     "lodash.get": "^4.4.2",
     "lodash.pick": "^4.4.0",

+ 172 - 0
src/components/Echarts/bar.vue

@@ -0,0 +1,172 @@
+<template>
+  <div ref="dom" class="charts chart-bar"></div>
+</template>
+
+<script>
+  import echarts from 'echarts'
+  import tdTheme from './theme.json'
+  import {
+    on,
+    off
+  } from '@/libs/tools'
+
+  echarts.registerTheme('tdTheme', tdTheme)
+  export default {
+    name: 'ChartBar',
+    props: {
+      value: Object, // 单列柱状图,key为x轴,value为y轴
+      text: String,
+      subtext: String,
+      color: String,
+      // 当有多列柱状图展示时的X轴坐标数据
+      xAxisData: {
+        type: Array,
+        default: function () {
+          return []
+        }
+      },
+      // y轴坐标最大值
+      yMax: {
+        type: Number,
+        default: 100
+      },
+      // 当有多列柱状图展示时的y轴坐标数据
+      seriesData: {
+        type: Array,
+        default: function () {
+          return []
+        }
+      },
+      // x轴标签旋转角度
+      xAxisRotate: {
+        type: Number,
+        default: 0
+      }
+    },
+    watch: {
+      xAxisData: {
+        handler (nVal, oVal) {
+          // console.log(nVal, 'nnnnnnnn')
+          if (nVal) {
+            // 根据x轴时间长度控制滚动条位置及显示状态 x轴长度大于25时,显示滚动条并且滚动条结束位置在80%
+            if (nVal.length > 25) {
+              this.isShowZoom = true
+              this.xZoomEnd = 80
+            }
+            if (nVal.length > 30) {
+              this.xZoomEnd = 70
+            }
+            if (nVal.length > 35) {
+              this.xZoomEnd = 60
+            }
+            if (nVal.length > 40) {
+              this.xZoomEnd = 50
+            }
+            if (nVal.length > 45) {
+              this.xZoomEnd = 40
+            }
+            if (nVal.length > 50) {
+              this.xZoomEnd = 30
+            }
+            this.pageInit()
+          }
+        },
+        deep: true,
+        immediate: true // 代表如果在 wacth 里声明了 xAxisData 之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行
+      },
+      xAxisRotate: {
+        handler (nVal, oVal) {
+          if (nVal !== '') {
+            this.pageInit()
+          }
+        },
+        deep: true,
+        immediate: true
+      }
+    },
+    data () {
+      return {
+        dom: null,
+        isShowZoom: false, // 是否展示滚动条
+        xZoomEnd: 100 // x轴滚动条结束位置
+      }
+    },
+    methods: {
+      resize () {
+        this.dom.resize()
+      },
+      pageInit () {
+        let legend = this.seriesData.map(_ => _.name)
+        this.$nextTick(() => {
+          let xAxisData = this.value ? Object.keys(this.value) : this.xAxisData
+          let seriesData = this.value ? Object.values(this.value) : this.seriesData
+          let option = {
+            color: [this.color],
+            title: {
+              text: this.text,
+              subtext: this.subtext,
+              x: 'left'
+            },
+            legend: {
+              data: legend
+            },
+            tooltip: {
+              trigger: 'axis'
+            },
+            dataZoom: [{
+              type: 'slider',
+              show: this.isShowZoom, // flase直接隐藏图形
+              xAxisIndex: [0],
+              left: '9%', // 滚动条靠左侧的百分比
+              bottom: -5,
+              start: 0, // 滚动条的起始位置
+              end: this.xZoomEnd, // 滚动条的截止位置(按比例分割你的柱状图x轴长度)
+              zoomLock: true // 锁定区域禁止缩放(鼠标滚动会缩放,所以禁止)
+            }],
+            xAxis: {
+              type: 'category',
+              data: xAxisData,
+              axisLabel: {
+                interval: 0,
+                rotate: this.xAxisRotate // 刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠。旋转的角度从 -90 度到 90 度。
+              }
+            },
+            yAxis: {
+              type: 'value',
+              min: 0,
+              max: this.yMax,
+              // interval: 2,
+              position: 'left'
+            }
+          }
+          if (this.value) {
+            option.series = [{
+              data: seriesData,
+              type: 'bar',
+              barWidth: 35,
+              label: {
+                normal: {
+                  show: true,
+                  position: 'top'
+                }
+              }
+            }]
+          } else {
+            // console.log(this.seriesData, '111111111')
+            option.series = this.seriesData
+          }
+          // console.log(option.series, 'ppppppp')
+          this.dom = echarts.init(this.$refs.dom, 'tdTheme')
+          this.dom.setOption(option)
+          on(window, 'resize', this.resize)
+        })
+      }
+    },
+    mounted () {
+      this.pageInit()
+    },
+    beforeDestroy () {
+      off(window, 'resize', this.resize)
+    }
+  }
+</script>

+ 4 - 0
src/components/Echarts/index.js

@@ -0,0 +1,4 @@
+import ChartPie from './pie.vue'
+import ChartBar from './bar.vue'
+import ChartLine from './line.vue'
+export { ChartPie, ChartBar, ChartLine }

+ 189 - 0
src/components/Echarts/line.vue

@@ -0,0 +1,189 @@
+<template>
+  <div ref="dom" class="charts chart-line"></div>
+</template>
+
+<script>
+  import echarts from 'echarts'
+  import tdTheme from './theme.json'
+  import {
+    on,
+    off
+  } from '@/libs/tools'
+
+  echarts.registerTheme('tdTheme', tdTheme)
+  export default {
+    name: 'ChartLine',
+    props: {
+      value: Object, // 单列折线图,key为x轴,value为y轴
+      text: String,
+      subtext: String,
+      color: String,
+      // 当有多列折线图展示时的X轴坐标数据
+      xAxisData: {
+        type: Array,
+        default: function () {
+          return []
+        }
+      },
+      // 当有多列折线图展示时的y轴坐标数据
+      seriesData: {
+        type: Array,
+        default: function () {
+          return []
+        }
+      },
+      // y轴坐标最大值
+      yMax: {
+        type: Number,
+        default: 100
+      },
+      // y轴坐标单位
+      yUnit: {
+        type: String,
+        default: '元'
+      },
+      // x轴标签旋转角度
+      xAxisRotate: {
+        type: Number,
+        default: 0
+      }
+    },
+    watch: {
+      xAxisData: {
+        handler (nVal, oVal) {
+          // console.log(nVal, 'nnnnnnnn')
+          if (nVal) {
+            // 根据x轴时间长度控制滚动条位置及显示状态 营业额x轴长度大于15 或者 检测率x轴长度大于25时,显示滚动条并且滚动条结束位置在80%
+            // this.yMax > 100 为营业数据 营业数据宽近为右侧屏幕的1/2
+            if ((nVal.length > 10 && this.yMax > 100) || nVal.length > 25) {
+              this.isShowZoom = true
+              this.xZoomEnd = 80
+            }
+            // 营业额x轴长度大于20 滚动条结束位置在70%
+            if ((nVal.length > 15 && this.yMax > 100) || nVal.length > 30) {
+              this.xZoomEnd = 70
+            }
+            // 营业额x轴长度大于25 滚动条结束位置在60%
+            if ((nVal.length >= 25 && this.yMax > 100) || nVal.length > 35) {
+              this.xZoomEnd = 60
+            }
+            if (nVal.length > 40 || (nVal.length >= 30 && this.yMax > 100)) {
+              this.xZoomEnd = 50
+            }
+            if (nVal.length > 45 || (nVal.length >= 35 && this.yMax > 100)) {
+              this.xZoomEnd = 40
+            }
+            if (nVal.length > 50 || (nVal.length >= 40 && this.yMax > 100)) {
+              this.xZoomEnd = 30
+            }
+            this.pageInit()
+          }
+        },
+        deep: true,
+        immediate: true // 代表如果在 wacth 里声明了 xAxisData 之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行
+      },
+      xAxisRotate: {
+        handler (nVal, oVal) {
+          if (nVal !== '') {
+            this.pageInit()
+          }
+        },
+        deep: true,
+        immediate: true
+      }
+    },
+    data () {
+      return {
+        dom: null,
+        isShowZoom: false, // 是否展示滚动条
+        xZoomEnd: 100 // x轴滚动条结束位置
+      }
+    },
+    methods: {
+      resize () {
+        this.dom.resize()
+      },
+      pageInit () {
+        let legend = this.seriesData.map(_ => _.name)
+        this.$nextTick(() => {
+          let xAxisData = this.value ? Object.keys(this.value) : this.xAxisData
+          let seriesData = this.value ? Object.values(this.value) : this.seriesData
+          let option = {
+            color: [this.color],
+            title: {
+              text: this.text,
+              subtext: this.subtext,
+              x: 'left'
+            },
+            legend: {
+              data: legend
+            },
+            // 鼠标放上去遮罩层提示信息
+            tooltip: {
+              trigger: 'axis'
+            },
+            dataZoom: [{ // Y轴固定,让内容滚动
+              type: 'slider',
+              show: this.isShowZoom, // flase直接隐藏图形
+              xAxisIndex: [0],
+              left: '9%', // 滚动条靠左侧的百分比
+              bottom: -5,
+              start: 0, // 滚动条的起始位置
+              end: this.xZoomEnd, // 滚动条的截止位置(按比例分割你的柱状图x轴长度)
+              zoomLock: true // 锁定区域禁止缩放(鼠标滚动会缩放,所以禁止)
+            }],
+            xAxis: {
+              type: 'category',
+              boundaryGap: false, // 两边是否留白
+              data: xAxisData,
+              axisLabel: {
+                interval: 0,
+                rotate: this.xAxisRotate
+              },
+              axisTick: {
+                inside: true,
+                lignWithLabel: true // 这两行代码可以让刻度正对刻度名
+              }
+            },
+            yAxis: {
+              type: 'value',
+              min: 0,
+              max: this.yMax,
+              axisLabel: {
+                  formatter: `{value} ${this.yUnit}`
+              },
+              // interval: 2,
+              position: 'left'
+            }
+          }
+          if (this.value) {
+            option.series = [{
+              data: seriesData,
+              type: 'line',
+              barWidth: 35,
+              label: {
+                normal: {
+                  show: true,
+                  position: 'top'
+                }
+              }
+            }]
+          } else {
+            // console.log(this.seriesData, '111111111')
+            option.series = this.seriesData
+          }
+          // console.log(option.series, 'ppppppp')
+          this.dom = echarts.init(this.$refs.dom, 'tdTheme')
+          this.dom.setOption(option)
+          on(window, 'resize', this.resize)
+        })
+      }
+    },
+    mounted () {
+      this.pageInit()
+    },
+    beforeDestroy () {
+      off(window, 'resize', this.resize)
+    }
+  }
+</script>

+ 190 - 0
src/components/Echarts/pie.vue

@@ -0,0 +1,190 @@
+<template>
+  <div ref="dom" class="charts chart-pie"></div>
+</template>
+
+<script>
+import echarts from 'echarts'
+import tdTheme from './theme.json'
+import { on, off } from '@/libs/tools'
+echarts.registerTheme('tdTheme', tdTheme)
+export default {
+  name: 'ChartPie',
+  props: {
+    subtext: String,
+    // 图表标题
+    text: {
+      type: String,
+      default: ''
+    },
+    // 数据值
+    value: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 查询时间
+    searchData: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 圆环中心标题
+    title: {
+      type: String,
+      default: ''
+    },
+    // 圆环中心金额
+    total: {
+      type: Number,
+      default: 0
+    },
+    // 圆环各类型颜色
+    color: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // x轴标签旋转角度
+    xAxisRotate: {
+      type: Number,
+      default: 0
+    }
+  },
+  watch: {
+    total: {
+      handler (nVal, oVal) {
+        // console.log(nVal, 'total111111111')
+        // 为防止当为0时不执行函数
+        if (nVal !== '') {
+          this.pageInit()
+        }
+      },
+      deep: true,
+      immediate: true // 代表如果在 wacth 里声明了 xAxisData 之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行
+    }
+  },
+  data () {
+    return {
+      dom: null
+    }
+  },
+  methods: {
+    resize () {
+      this.dom.resize()
+    },
+    pageInit () {
+      this.$nextTick(() => {
+        let legend = this.value.map(_ => _.name)
+        let option = {
+          color: this.color,
+          title: [{
+              text: '{name|' + this.title + '}\n{val|' + this.formatNumber(this.total) + '}',
+              top: 'center',
+              left: 'center',
+              textStyle: {
+                  rich: {
+                      name: {
+                          fontSize: 14,
+                          fontWeight: 'normal',
+                          color: '#666666',
+                          padding: [10, 0]
+                      },
+                      val: {
+                          fontSize: 32,
+                          fontWeight: 'bold',
+                          color: '#333333'
+                      }
+                  }
+              }
+          }],
+          tooltip: {
+            trigger: 'item',
+            formatter: '{b} : {c} ({d}%)'
+          },
+          legend: {
+            orient: 'horizontal',
+            left: 'center',
+            bottom: 'bottom',
+            padding: 5,
+            data: legend
+          },
+          series: [
+            {
+              type: 'pie',
+              radius: ['35%', '50%'],
+              center: ['50%', '50%'],
+              avoidLabelOverlap: true, // 在标签拥挤重叠的情况下会挪动各个标签的位置,防止标签间的重叠
+              hoverAnimation: false,
+              label: {
+                normal: {
+                    formatter: params => {
+                        return (
+                            '{icon|●}{name|' + params.name + '}{value|' +
+                            this.formatNumber(params.value) + '}'
+                        )
+                    },
+                    padding: [0, -100, 25, -130],
+                    rich: {
+                        icon: {
+                            fontSize: 12
+                        },
+                        name: {
+                            fontSize: 14,
+                            padding: [0, 10, 0, 4],
+                            color: '#666666'
+                        },
+                        value: {
+                            fontSize: 16,
+                            fontWeight: 'bold',
+                            color: '#333333'
+                        }
+                    }
+                }
+              },
+              labelLine: {
+                  normal: {
+                      length: 20,
+                      length2: 120,
+                      lineStyle: {
+                          color: '#e6e6e6'
+                      }
+                  }
+              },
+              data: this.value,
+              itemStyle: {
+                emphasis: {
+                  shadowBlur: 10,
+                  shadowOffsetX: 0,
+                  shadowColor: 'rgba(255, 170, 0, 0.5)'
+                },
+                normal: {
+                    borderColor: '#fff',
+                    borderWidth: 2
+                }
+              }
+            }
+          ]
+        }
+        this.dom = echarts.init(this.$refs.dom, 'tdTheme')
+        console.log(option, 'option')
+        this.dom.setOption(option)
+        on(window, 'resize', this.resize)
+      })
+    },
+    // 格式化金额
+    formatNumber (num){
+      let reg = /(?=(\B)(\d{3})+$)/g
+      return num.toString().replace(reg, ',')
+    }
+  },
+  mounted () {
+    this.pageInit()
+  },
+  beforeDestroy () {
+    off(window, 'resize', this.resize)
+  }
+}
+</script>

+ 491 - 0
src/components/Echarts/theme.json

@@ -0,0 +1,491 @@
+
+{
+    "color": [
+        "#2d8cf0",
+        "#19be6b",
+        "#ff9900",
+        "#E46CBB",
+        "#9A66E4",
+        "#ed3f14"
+    ],
+    "backgroundColor": "rgba(0,0,0,0)",
+    "textStyle": {},
+    "title": {
+        "textStyle": {
+            "color": "#516b91"
+        },
+        "subtextStyle": {
+            "color": "#93b7e3"
+        }
+    },
+    "line": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": "2"
+            }
+        },
+        "lineStyle": {
+            "normal": {
+                "width": "2"
+            }
+        },
+        "symbolSize": "6",
+        "symbol": "emptyCircle",
+        "smooth": true
+    },
+    "radar": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": "2"
+            }
+        },
+        "lineStyle": {
+            "normal": {
+                "width": "2"
+            }
+        },
+        "symbolSize": "6",
+        "symbol": "emptyCircle",
+        "smooth": true
+    },
+    "bar": {
+        "itemStyle": {
+            "normal": {
+                "barBorderWidth": 0,
+                "barBorderColor": "#ccc"
+            },
+            "emphasis": {
+                "barBorderWidth": 0,
+                "barBorderColor": "#ccc"
+            }
+        }
+    },
+    "pie": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "scatter": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "boxplot": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "parallel": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "sankey": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "funnel": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "gauge": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            },
+            "emphasis": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        }
+    },
+    "candlestick": {
+        "itemStyle": {
+            "normal": {
+                "color": "#edafda",
+                "color0": "transparent",
+                "borderColor": "#d680bc",
+                "borderColor0": "#8fd3e8",
+                "borderWidth": "2"
+            }
+        }
+    },
+    "graph": {
+        "itemStyle": {
+            "normal": {
+                "borderWidth": 0,
+                "borderColor": "#ccc"
+            }
+        },
+        "lineStyle": {
+            "normal": {
+                "width": 1,
+                "color": "#aaa"
+            }
+        },
+        "symbolSize": "6",
+        "symbol": "emptyCircle",
+        "smooth": true,
+        "color": [
+            "#2d8cf0",
+            "#19be6b",
+            "#f5ae4a",
+            "#9189d5",
+            "#56cae2",
+            "#cbb0e3"
+        ],
+        "label": {
+            "normal": {
+                "textStyle": {
+                    "color": "#eee"
+                }
+            }
+        }
+    },
+    "map": {
+        "itemStyle": {
+            "normal": {
+                "areaColor": "#f3f3f3",
+                "borderColor": "#516b91",
+                "borderWidth": 0.5
+            },
+            "emphasis": {
+                "areaColor": "rgba(165,231,240,1)",
+                "borderColor": "#516b91",
+                "borderWidth": 1
+            }
+        },
+        "label": {
+            "normal": {
+                "textStyle": {
+                    "color": "#000"
+                }
+            },
+            "emphasis": {
+                "textStyle": {
+                    "color": "rgb(81,107,145)"
+                }
+            }
+        }
+    },
+    "geo": {
+        "itemStyle": {
+            "normal": {
+                "areaColor": "#f3f3f3",
+                "borderColor": "#516b91",
+                "borderWidth": 0.5
+            },
+            "emphasis": {
+                "areaColor": "rgba(165,231,240,1)",
+                "borderColor": "#516b91",
+                "borderWidth": 1
+            }
+        },
+        "label": {
+            "normal": {
+                "textStyle": {
+                    "color": "#000"
+                }
+            },
+            "emphasis": {
+                "textStyle": {
+                    "color": "rgb(81,107,145)"
+                }
+            }
+        }
+    },
+    "categoryAxis": {
+        "axisLine": {
+            "show": true,
+            "lineStyle": {
+                "color": "#cccccc"
+            }
+        },
+        "axisTick": {
+            "show": false,
+            "lineStyle": {
+                "color": "#333"
+            }
+        },
+        "axisLabel": {
+            "show": true,
+            "textStyle": {
+                "color": "#999999"
+            }
+        },
+        "splitLine": {
+            "show": true,
+            "lineStyle": {
+                "color": [
+                    "#eeeeee"
+                ]
+            }
+        },
+        "splitArea": {
+            "show": false,
+            "areaStyle": {
+                "color": [
+                    "rgba(250,250,250,0.05)",
+                    "rgba(200,200,200,0.02)"
+                ]
+            }
+        }
+    },
+    "valueAxis": {
+        "axisLine": {
+            "show": true,
+            "lineStyle": {
+                "color": "#cccccc"
+            }
+        },
+        "axisTick": {
+            "show": false,
+            "lineStyle": {
+                "color": "#333"
+            }
+        },
+        "axisLabel": {
+            "show": true,
+            "textStyle": {
+                "color": "#999999"
+            }
+        },
+        "splitLine": {
+            "show": true,
+            "lineStyle": {
+                "color": [
+                    "#eeeeee"
+                ]
+            }
+        },
+        "splitArea": {
+            "show": false,
+            "areaStyle": {
+                "color": [
+                    "rgba(250,250,250,0.05)",
+                    "rgba(200,200,200,0.02)"
+                ]
+            }
+        }
+    },
+    "logAxis": {
+        "axisLine": {
+            "show": true,
+            "lineStyle": {
+                "color": "#cccccc"
+            }
+        },
+        "axisTick": {
+            "show": false,
+            "lineStyle": {
+                "color": "#333"
+            }
+        },
+        "axisLabel": {
+            "show": true,
+            "textStyle": {
+                "color": "#999999"
+            }
+        },
+        "splitLine": {
+            "show": true,
+            "lineStyle": {
+                "color": [
+                    "#eeeeee"
+                ]
+            }
+        },
+        "splitArea": {
+            "show": false,
+            "areaStyle": {
+                "color": [
+                    "rgba(250,250,250,0.05)",
+                    "rgba(200,200,200,0.02)"
+                ]
+            }
+        }
+    },
+    "timeAxis": {
+        "axisLine": {
+            "show": true,
+            "lineStyle": {
+                "color": "#cccccc"
+            }
+        },
+        "axisTick": {
+            "show": false,
+            "lineStyle": {
+                "color": "#333"
+            }
+        },
+        "axisLabel": {
+            "show": true,
+            "textStyle": {
+                "color": "#999999"
+            }
+        },
+        "splitLine": {
+            "show": true,
+            "lineStyle": {
+                "color": [
+                    "#eeeeee"
+                ]
+            }
+        },
+        "splitArea": {
+            "show": false,
+            "areaStyle": {
+                "color": [
+                    "rgba(250,250,250,0.05)",
+                    "rgba(200,200,200,0.02)"
+                ]
+            }
+        }
+    },
+    "toolbox": {
+        "iconStyle": {
+            "normal": {
+                "borderColor": "#999"
+            },
+            "emphasis": {
+                "borderColor": "#666"
+            }
+        }
+    },
+    "legend": {
+        "textStyle": {
+            "color": "#999999"
+        }
+    },
+    "tooltip": {
+        "axisPointer": {
+            "lineStyle": {
+                "color": "#ccc",
+                "width": 1
+            },
+            "crossStyle": {
+                "color": "#ccc",
+                "width": 1
+            }
+        }
+    },
+    "timeline": {
+        "lineStyle": {
+            "color": "#8fd3e8",
+            "width": 1
+        },
+        "itemStyle": {
+            "normal": {
+                "color": "#8fd3e8",
+                "borderWidth": 1
+            },
+            "emphasis": {
+                "color": "#8fd3e8"
+            }
+        },
+        "controlStyle": {
+            "normal": {
+                "color": "#8fd3e8",
+                "borderColor": "#8fd3e8",
+                "borderWidth": 0.5
+            },
+            "emphasis": {
+                "color": "#8fd3e8",
+                "borderColor": "#8fd3e8",
+                "borderWidth": 0.5
+            }
+        },
+        "checkpointStyle": {
+            "color": "#8fd3e8",
+            "borderColor": "rgba(138,124,168,0.37)"
+        },
+        "label": {
+            "normal": {
+                "textStyle": {
+                    "color": "#8fd3e8"
+                }
+            },
+            "emphasis": {
+                "textStyle": {
+                    "color": "#8fd3e8"
+                }
+            }
+        }
+    },
+    "visualMap": {
+        "color": [
+            "#516b91",
+            "#59c4e6",
+            "#a5e7f0"
+        ]
+    },
+    "dataZoom": {
+        "backgroundColor": "rgba(0,0,0,0)",
+        "dataBackgroundColor": "rgba(255,255,255,0.3)",
+        "fillerColor": "rgba(167,183,204,0.4)",
+        "handleColor": "#a7b7cc",
+        "handleSize": "100%",
+        "textStyle": {
+            "color": "#333"
+        }
+    },
+    "markPoint": {
+        "label": {
+            "normal": {
+                "textStyle": {
+                    "color": "#eee"
+                }
+            },
+            "emphasis": {
+                "textStyle": {
+                    "color": "#eee"
+                }
+            }
+        }
+    }
+}

+ 419 - 0
src/libs/tools.js

@@ -0,0 +1,419 @@
+export const forEach = (arr, fn) => {
+  if (!arr.length || !fn) return
+  let i = -1
+  let len = arr.length
+  while (++i < len) {
+    let item = arr[i]
+    fn(item, i, arr)
+  }
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
+ */
+export const getIntersection = (arr1, arr2) => {
+  let len = Math.min(arr1.length, arr2.length)
+  let i = -1
+  let res = []
+  while (++i < len) {
+    const item = arr2[i]
+    if (arr1.indexOf(item) > -1) res.push(item)
+  }
+  return res
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的并集, 两个数组的元素为数值或字符串
+ */
+export const getUnion = (arr1, arr2) => {
+  return Array.from(new Set([...arr1, ...arr2]))
+}
+
+/**
+ * @param {Array} target 目标数组
+ * @param {Array} arr 需要查询的数组
+ * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
+ */
+export const hasOneOf = (targetarr, arr) => {
+  if (!targetarr) return true
+  if (!arr) return true
+  return targetarr.some(_ => arr.indexOf(_) > -1)
+}
+
+/**
+ * @param {String|Number} value 要验证的字符串或数值
+ * @param {*} validList 用来验证的列表
+ */
+export function oneOf (value, validList) {
+  for (let i = 0; i < validList.length; i++) {
+    if (value === validList[i]) {
+      return true
+    }
+  }
+  return false
+}
+
+/**
+ * @param {Number} timeStamp 判断时间戳格式是否是毫秒
+ * @returns {Boolean}
+ */
+const isMillisecond = timeStamp => {
+  const timeStr = String(timeStamp)
+  return timeStr.length > 10
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} currentTime 当前时间时间戳
+ * @returns {Boolean} 传入的时间戳是否早于当前时间戳
+ */
+const isEarly = (timeStamp, currentTime) => {
+  return timeStamp < currentTime
+}
+
+/**
+ * @param {Number} num 数值
+ * @returns {String} 处理后的字符串
+ * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0
+ */
+const getHandledValue = num => {
+  return num < 10 ? '0' + num : num
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间
+ */
+const getDate = (timeStamp, startType) => {
+  const d = new Date(timeStamp * 1000)
+  const year = d.getFullYear()
+  const month = getHandledValue(d.getMonth() + 1)
+  const date = getHandledValue(d.getDate())
+  const hours = getHandledValue(d.getHours())
+  const minutes = getHandledValue(d.getMinutes())
+  const second = getHandledValue(d.getSeconds())
+  let resStr = ''
+  if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
+  else resStr = month + '-' + date + ' ' + hours + ':' + minutes
+  return resStr
+}
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} fmt 格式化字符串
+ */
+export const formtDate = (timeStamp, fmt) => {
+  const d = new Date(timeStamp)
+  var o = {
+        'M+': d.getMonth() + 1, // 月份
+        'd+': d.getDate(), // 日
+        'h+': d.getHours(), // 小时
+        'm+': d.getMinutes(), // 分
+        's+': d.getSeconds(), // 秒
+        'q+': Math.floor((d.getMonth() + 3) / 3), // 季度
+        'S': d.getMilliseconds() // 毫秒
+    }
+    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length))
+    for (var k in o) { if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) }
+    return fmt
+}
+/**
+ * @param {String|Number} timeStamp 时间戳
+ * @returns {String} 相对时间字符串
+ */
+export const getRelativeTime = timeStamp => {
+  // 判断当前传入的时间戳是秒格式还是毫秒
+  const IS_MILLISECOND = isMillisecond(timeStamp)
+  // 如果是毫秒格式则转为秒格式
+  if (IS_MILLISECOND) Math.floor(timeStamp /= 1000)
+  // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
+  timeStamp = Number(timeStamp)
+  // 获取当前时间时间戳
+  const currentTime = Math.floor(Date.parse(new Date()) / 1000)
+  // 判断传入时间戳是否早于当前时间戳
+  const IS_EARLY = isEarly(timeStamp, currentTime)
+  // 获取两个时间戳差值
+  let diff = currentTime - timeStamp
+  // 如果IS_EARLY为false则差值取反
+  if (!IS_EARLY) diff = -diff
+  let resStr = ''
+  const dirStr = IS_EARLY ? '前' : '后'
+  // 少于等于59秒
+  if (diff <= 59) resStr = diff + '秒' + dirStr
+  // 多于59秒,少于等于59分钟59秒
+  else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr
+  // 多于59分钟59秒,少于等于23小时59分钟59秒
+  else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr
+  // 多于23小时59分钟59秒,少于等于29天59分钟59秒
+  else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr
+  // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
+  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp)
+  else resStr = getDate(timeStamp, 'year')
+  return resStr
+}
+
+// 日期格式化
+export const formatSubmitDate = (val, type) => {
+  if (val == null || val == '' || val == undefined) {
+    return ''
+  } else {
+    let _date = new Date(val)
+    let _year = _date.getFullYear()
+    let _montn = (_date.getMonth() + 1) < 10 ? '0' + (_date.getMonth() + 1) : (_date.getMonth() + 1)
+    let _day = _date.getDate() < 10 ? '0' + _date.getDate() : _date.getDate()
+    let _hour = _date.getHours() < 10 ? '0' + _date.getHours() : _date.getHours()
+    let _minutes = _date.getMinutes() < 10 ? '0' + _date.getMinutes() : _date.getMinutes()
+    let _seconds = _date.getSeconds() < 10 ? '0' + _date.getSeconds() : _date.getSeconds()
+
+    if (type == 'minutes') return _year + '-' + _montn + '-' + _day + ' ' + _hour + ':' + _minutes
+    else if (type == 'seconds') return _year + '-' + _montn + '-' + _day + ' ' + _hour + ':' + _minutes + ':' + _seconds
+    else return _year + '-' + _montn + '-' + _day
+  }
+}
+// 正则验证车牌,验证通过返回true,不通过返回false
+export const isLicensePlate = function (str) {
+    return /^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(str)
+}
+// 车牌可输入字符
+export const isCarNumber = function (str) {
+  let _value = str + ''
+  _value = _value.replace(/[^\w\.挂学警港澳使领]/ig, '')
+
+  return _value
+}
+// 小数点后两位
+export const numberToFixed = function (val, num) {
+  let _value = val + ''
+  _value = _value.replace(/[^\d.]/g, '')// 清楚数字和.以外的字数
+  _value = _value.replace(/^\./g, '')
+  _value = _value.replace(/\.{2,}/g, '')// 保留第一个,清楚多余的
+
+  if (num == 1)_value = _value.replace(/^(\-)*(\d+)\.(\d).*$/, '$1$2.$3')
+  else if (num == 3)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d).*$/, '$1$2.$3')
+  else if (num == 4)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d\d).*$/, '$1$2.$3')
+  else if (num == 5)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d\d\d).*$/, '$1$2.$3')
+  else if (num == 0)_value = _value.replace(/^(\-)*(\d+)\.*$/, '$1$2')
+  else _value = _value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
+
+  return _value
+}// 只能输入数字
+export const justNumber = function (val) {
+  let _value = val + ''
+  _value = _value.replace(/\D/g, '')
+
+  return _value
+}
+
+/**
+ * @returns {String} 当前浏览器名称
+ */
+export const getExplorer = () => {
+  const ua = window.navigator.userAgent
+  const isExplorer = (exp) => {
+    return ua.indexOf(exp) > -1
+  }
+  if (isExplorer('MSIE')) return 'IE'
+  else if (isExplorer('Firefox')) return 'Firefox'
+  else if (isExplorer('Chrome')) return 'Chrome'
+  else if (isExplorer('Opera')) return 'Opera'
+  else if (isExplorer('Safari')) return 'Safari'
+}
+
+/**
+ * @description 绑定事件 on(element, event, handler)
+ */
+export const on = (function () {
+  if (document.addEventListener) {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.addEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.attachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * @description 解绑事件 off(element, event, handler)
+ */
+export const off = (function () {
+  if (document.removeEventListener) {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.removeEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.detachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
+ * 如果没有传入key这个参数,则判断obj对象是否有键值对
+ */
+export const hasKey = (obj, key) => {
+  if (key) return key in obj
+  else {
+    let keysArr = Object.keys(obj)
+    return keysArr.length
+  }
+}
+
+/**
+ * @param {*} obj1 对象
+ * @param {*} obj2 对象
+ * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
+ */
+export const objEqual = (obj1, obj2) => {
+  const keysArr1 = Object.keys(obj1)
+  const keysArr2 = Object.keys(obj2)
+  if (keysArr1.length !== keysArr2.length) return false
+  else if (keysArr1.length === 0 && keysArr2.length === 0) return true
+  /* eslint-disable-next-line */
+  else return !keysArr1.some(key => obj1[key] != obj2[key])
+}
+
+/*
+ * @param {*} id 数字
+ * @param {*} list 数组
+ * @description 根据id从数组列表中删除某一项
+*/
+export const removeListById = (id, list) => {
+  list.splice(list.findIndex(item => item.id === id), 1)
+}
+
+/**
+ * @param {*} obj1 对象
+ * @param {*} obj2 对象
+ * @description 遍历赋值
+*/
+export const objExtend = (obj1, obj2) => {
+  for (var a in obj1){
+    obj2[a] = obj1[a]
+  }
+  return obj2
+}
+/**
+ * @param {*} obj 对象
+ * @description 浅拷贝
+*/
+export const cloneObj = (obj) => {
+  let ret = {}
+  for (var a in obj){
+    ret[a] = obj[a]
+  }
+  return ret
+}
+
+/**
+ * 校验身份证号合法性
+ */
+export const checkIdNumberValid = (tex) => {
+    // var tip = '输入的身份证号有误,请检查后重新输入!'
+    let num = tex
+    num = num.toUpperCase()
+
+    let len = num.length
+    let re
+    if (len == 0) return true
+
+    // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X。
+    if (!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(num))){
+        return false
+    }
+
+    // 验证前两位地区是否有效
+    let aCity = {11: '北京',
+12: '天津',
+13: '河北',
+14: '山西',
+15: '内蒙古',
+21: '辽宁',
+22: '吉林',
+23: '黑龙江',
+31: '上海',
+32: '江苏',
+33: '浙江',
+34: '安徽',
+35: '福建',
+36: '江西',
+37: '山东',
+41: '河南',
+42: '湖北',
+43: '湖南',
+44: '广东',
+45: '广西',
+46: '海南',
+50: '重庆',
+51: '四川',
+52: '贵州',
+53: '云南',
+54: '西藏',
+61: '陕西',
+62: '甘肃',
+63: '青海',
+64: '宁夏',
+65: '新疆',
+71: '台湾',
+81: '香港',
+82: '澳门',
+91: '国外'}
+
+    if (aCity[parseInt(num.substr(0, 2))] == null){
+        return false
+    }
+
+    // 当身份证为15位时的验证出生日期。
+    if (len == 15){
+        re = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/)
+        let arrSplit = num.match(re)
+
+        // 检查生日日期是否正确
+        let dtmBirth = new Date('19' + arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+        let bGoodDay = (dtmBirth.getYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4]))
+        if (!bGoodDay){
+            return false
+        }
+    }
+
+    // 当身份证号为18位时,校验出生日期和校验位。
+    if (len == 18){
+        re = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/)
+        let arrSplit = num.match(re)
+        // 检查生日日期是否正确
+        let dtmBirth = new Date(arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+        let bGoodDay = (dtmBirth.getFullYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4]))
+        if (!bGoodDay){
+            return false
+        } else {
+            // 检验18位身份证的校验码是否正确。
+            // 校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。
+            let valnum
+            let arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+            let arrCh = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
+            let nTemp = 0
+            let i
+            for (i = 0; i < 17; i++){
+                nTemp += num.substr(i, 1) * arrInt[i]
+            }
+            valnum = arrCh[nTemp % 11]
+            if (valnum != num.substr(17, 1)){
+                return false
+            }
+        }
+    }
+    return true
+}

+ 543 - 8
src/views/Home.vue

@@ -1,26 +1,561 @@
 <template>
   <div class="home">
     <a-alert message="欢迎登录洗车机运营管理平台系统" type="info" showIcon />
+    <div class="report-page">
+      <!-- 查询条件 -->
+      <a-row>
+        <a-card class="search-panel">
+          <div class="toolsBar">
+            <a-form ref="searchForm" :model="searchForm" :rules="ruleInline" layout="inline">
+              <a-form-item prop="queryWord">
+                统计区间:
+                <a-range-picker
+                  style="width: 220px;"
+                  @change="dateChange"
+                  v-model.trim="searchForm.queryWord"
+                  :disabledDate="disabledDate"
+                />
+              </a-form-item>
+              <a-form-item>
+                <a-button type="primary" class="search-btn" @click="handleSearch()">查询</a-button>
+              </a-form-item>
+              <a-form-item>
+                快捷筛选:
+                <span
+                  :class="['searchItem',curentType==item.type?'active':'']"
+                  v-for="(item,index) in tabList"
+                  :key="index"
+                  @click="getCurentSearchTime(item)">{{ item.name }}</span>
+              </a-form-item>
+            </a-form>
+          </div>
+        </a-card>
+      </a-row>
+      <!-- 营业数据 -->
+      <a-card class="panel ">
+        <a-row :gutter="20">
+          <a-col span="24" class="panel-title">洗车量趋势</a-col>
+          <a-divider />
+        </a-row>
+        <a-row :gutter="20">
+          <a-col :md="24" :lg="24">
+            <a-card shadow>
+              <!-- 洗车量趋势图表展示 -->
+              <chart-line
+                style="height: 300px;"
+                color="#6C6FBE"
+                :yMax="yAmountMax"
+                :xAxisRotate="resize"
+                :xAxisData="XdaysData"
+                :seriesData="amountChartData"
+                text="" />
+              <span v-if="isNoData" class="noData">暂无数据</span>
+            </a-card>
+
+          </a-col>
+        </a-row>
+      </a-card>
+      <!-- 各洗车类型数据 -->
+      <a-card class="panel ">
+        <a-row :gutter="24">
+          <a-col span="24" class="panel-title">各洗车类型数据</a-col>
+          <a-divider />
+          <a-col :md="4" :lg="4">
+            <a-card class="border-radius background-skyblue">
+              <div class="module-list ">
+                <span>{{ carData.dayTotalCar }}</span>
+                <p>快速洗车</p>
+              </div>
+            </a-card>
+          </a-col>
+          <a-col :md="4" :lg="4">
+            <a-card class="border-radius background-greenBlue">
+              <div class="module-list ">
+                <span>{{ carData.dayWashCar }}</span>
+                <p>精致洗车</p>
+              </div>
+            </a-card>
+          </a-col>
+          <a-col :md="4" :lg="4">
+            <a-card class="border-radius background-blackGreen">
+              <div class="module-list ">
+                <span>{{ carData.dayCheckCar }}</span>
+                <p>打蜡精洗</p>
+              </div>
+
+            </a-card>
+          </a-col>
+        </a-row>
+        <!-- 洗车类型数据图表展示 -->
+        <a-row :gutter="20">
+          <a-col class="tab-card">
+            <span :class="['tab-card-item',currentTabChart=='tab1'?'checked-item':'']" @click="currentTabChart='tab1'">详情数据</span>
+            <span :class="['tab-card-item',currentTabChart=='tab2'?'checked-item':'']" @click="currentTabChart='tab2'">变化趋势</span>
+          </a-col>
+          <!-- 详情数据 -->
+          <a-col :md="24" :lg="24" v-if="currentTabChart=='tab1'">
+            <chart-bar
+              ref="ChartBar"
+              style="height: 300px;"
+              color="#6C6FBE"
+              :xAxisRotate="resize"
+              :yMax="yCheckMax"
+              :xAxisData="XdaysData"
+              :seriesData="checkDetailData"
+              text="" />
+            <span v-if="isNoData" class="noData">暂无数据</span>
+          </a-col>
+          <!-- 变化趋势 -->
+          <a-col :md="24" :lg="24" v-if="currentTabChart=='tab2'">
+            <chart-line
+              ref="ChartLine"
+              style="height: 300px;"
+              color="#6C6FBE"
+              yUnit="%"
+              :xAxisRotate="resize"
+              :xAxisData="XdaysData"
+              :seriesData="checkChangeData"
+              text="" />
+            <span v-if="isNoData" class="noData">暂无数据</span>
+          </a-col>
+        </a-row>
+      </a-card>
+      <a-card class="panel ">
+        <a-row :gutter="20">
+          <a-col span="24" class="panel-title">各类型占比</a-col>
+          <a-divider />
+          <a-col span="12">
+            <a-card shadow>
+              <chart-pie
+                ref="chartPie"
+                class="chartPie-box"
+                :searchData="XdaysData"
+                :value="payChartData"
+                :title="payTitle"
+                :total="payCost"
+                :color="payColor"
+                :xAxisRotate="resize"
+                text="" />
+            </a-card>
+          </a-col>
+          <a-col span="12">
+            <a-card shadow>
+              <!-- 服务数据展示 -->
+              <chart-pie
+                ref="serverChartPie"
+                class="chartPie-box"
+                :searchData="XdaysData"
+                :value="serverChartData"
+                title="洗车总量(台)"
+                :total="serverTotal"
+                :color="serverColor"
+                :xAxisRotate="resize"
+                text="" />
+            </a-card>
+          </a-col>
+        </a-row>
+      </a-card>
+    </div>
   </div>
 </template>
 
 <script>
+import {
+  ChartPie,
+  ChartBar,
+  ChartLine
+} from '@/components/Echarts'
+import _ from 'lodash'
+import getDate from '@/libs/getDate'
 export default {
   name: 'Home',
-  components: {},
+  components: {
+    ChartPie,
+    ChartBar,
+    ChartLine
+  },
+  watch: {
+    screenWidth (val) {
+      // 为了避免频繁触发resize函数,使用定时器
+      if (!this.timer) {
+        this.screenWidth = val
+        this.timer = true
+        const that = this
+        setTimeout(function () {
+          // that.screenWidth = that.$store.state.canvasWidth
+          console.log(that.screenWidth)
+          that.timer = false
+        }, 400)
+      }
+    }
+  },
+  computed: {
+    resize () {
+      console.log(this.screenWidth, 'this.screenWidth')
+      if (this.screenWidth < 1640) {
+        return 40
+      } else {
+        return 0
+      }
+    }
+  },
+  // 将 reisze 事件在 vue mounted 的时候 去挂载一下它的方法
+  mounted () {
+    const that = this
+    window.onresize = () => {
+      return (() => {
+        window.screenWidth = document.body.clientWidth
+        that.screenWidth = window.screenWidth
+      })()
+    }
+  },
   data () {
-    return {}
+    return {
+      screenWidth: document.body.clientWidth, // 这里是给到了一个默认值
+      timer: false,
+      isNoData: false, // 是否有每天营业额及进店量数据
+      searchForm: {
+        queryWord: '' // 时间查询条件,默认今天
+      },
+      ruleInline: {},
+      // 快速删选tab
+      tabList: [{
+        name: '今天',
+        type: 'today'
+      },
+      {
+        name: '本周',
+        type: 'thisWeek'
+      },
+      {
+        name: '上周',
+        type: 'lastWeek'
+      },
+      {
+        name: '本月',
+        type: 'thisMonth'
+      }
+      ],
+      curentType: 'today', // 当前所选时间查询类型
+      beginDate: null, // 开始时间
+      endDate: null, // 结束时间
+      yAmountMax: 5000, // 营业额y轴最大值
+      // 营业数据
+      amountData: {
+        daySaleMoney: 0, // 营业额
+        dayIncome: 0, // 营业流水
+        dayCost: 0, // 支出
+        customPrice: 0 // 客单价 = 营业额/进店量
+      },
+
+      // 洗车量图表数据
+      amountChartData: [{
+        name: '全部洗车量',
+        type: 'line',
+        data: []
+      },
+      {
+        name: '用券洗车量',
+        type: 'line',
+        data: []
+      }
+      ],
+      // 洗车用券量占比
+      serverChartData: [{
+        'name': '未用券洗车量',
+        'value': 0
+      }, {
+        'name': '用券洗车量',
+        'value': 0
+      }],
+      serverTotal: 0, // 服务工时费总计
+      serverColor: ['#d48265', '#ff55ff'],
+      // 进店数据
+      currentTabChart: 'tab1', // 进店量详情数据与变化趋势的tab 默认展示详情数据
+      carData: {
+        dayTotalCar: 0, // 进店量
+        dayWashCar: 0, // 洗车量
+        dayCheckCar: 0, // 检测量
+        checkRate: 0 // 检测率  检测量/进店量
+      },
+      XdaysData: [], // 图表X轴时间集合
+      // 进店量,检测量 数据
+      yCheckMax: 80, // 进店量y轴最大值
+      checkDetailData: [{
+        name: '进店量',
+        type: 'bar',
+        data: []
+      },
+      {
+        name: '检测量',
+        type: 'bar',
+        data: []
+      }
+      ],
+      checkChangeData: [{
+        name: '检测率',
+        type: 'line',
+        data: []
+      }],
+      // 各类型占比
+      payColor: ['#ffaa00', '#00ff00', '#00aaff', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
+      payTitle: '洗车总量(台)',
+      payCost: 0, // 支出总额
+      payChartData: [{
+        'name': '快速洗车',
+        'value': 0
+      }, {
+        'name': '精致洗车',
+        'value': 0
+      }, {
+        'name': '打蜡精洗',
+        'value': 0
+      }],
+      dayBundleSaleIncome: 0, // 套餐销售额
+      databack: {
+        serverChartData: [],
+        payChartData: [],
+        amountData: {},
+        carData: {}
+      }
+    }
   },
   created () {
   },
-  methods: { 
+  methods: {
+    // 时间改变
+    dateChange () {
+      this.curentType = ''
+    },
+    // 不可选日期
+    disabledDate (date) {
+      return (date && date.valueOf() > Date.now()) || (date.valueOf() < Date.now() - (90 * 24 * 60 * 60 * 1000))
+    },
+    // 页面初始化
+    pageInit () {
+      this.$refs.searchForm.resetFields()
+      this.databack = {
+        serverChartData: _.cloneDeep(this.serverChartData),
+        payChartData: _.cloneDeep(this.payChartData),
+        amountData: _.cloneDeep(this.amountData),
+        carData: _.cloneDeep(this.carData)
+      }
+      this.dayBundleSaleIncome = 0
+      this.serverTotal = 0
+      this.payCost = 0
+    },
+    // 查询
+    handleSearch () {
+      this.showChart = false
+      if (this.searchForm.queryWord != ',' && this.searchForm.queryWord) {
+        const searchTime = this.searchForm.queryWord.toString().split(',')
+        this.beginDate = this.exitTime(searchTime[0])
+        this.endDate = this.exitTime(searchTime[1])
+        this.getPageData()
+      } else {
+        this.$Message.error('请选择查询条件')
+      }
+    },
+    // 格式化时间
+    exitTime (time) {
+      const D = new Date(time)
+      const RES_DATE = D.getFullYear() + '-' + this.p((D.getMonth() + 1)) + '-' + this.p(D.getDate())
+      return RES_DATE
+    },
+    // p为不够10添加0的函数
+    p (s) {
+      return s < 10 ? '0' + s : s
+    },
+    // 获取快速查询时间 并 赋值到时间选择框中
+    getCurentSearchTime (item) {
+      this.curentType = item ? item.type : this.curentType
+      const quickType = {
+        lastMonth: [getDate.getLastMonthDays().starttime, getDate.getLastMonthDays().endtime],
+        thisMonth: [getDate.getCurrMonthDays().starttime, getDate.getCurrMonthDays().endtime],
+        lastWeek: [getDate.getLastWeekDays().starttime, getDate.getLastWeekDays().endtime],
+        thisWeek: [getDate.getCurrWeekDays().starttime, getDate.getCurrWeekDays().endtime],
+        yesterday: [getDate.getYesterday().starttime, getDate.getYesterday().endtime],
+        today: [getDate.getToday().starttime, getDate.getToday().endtime]
+      }
+      this.searchForm.queryWord = quickType[this.curentType]
+      this.beginDate = quickType[this.curentType][0]
+      this.endDate = quickType[this.curentType][1]
+      this.getPageData()
+    },
+    // 根据查询条件获取所有数据
+    getPageData () {
+      this.XdaysData = [] // x轴置空
+      const _date = {
+        beginDate: this.beginDate,
+        endDate: this.endDate
+      }
+      // this.getAmount(_date)
+      // this.getServerData(_date)
+      // this.getBundelData(_date)
+      // this.getChartData(_date)
+      // this.getPayData(_date)
+    }
   }
 }
 </script>
-<style scoped>
-  .home {
-    /* width: 900px;
-    margin: 0 auto;
-    padding: 25px 0; */
+<style lang="less" scoped>
+  .report-page {
+    position: relative;
+    margin-top: 10px;
+    height: calc(100vh - 285px);
+    overflow-y: scroll;
+    .search-panel {
+      .ivu-card-body>div {
+        display: inline-block;
+        margin-right: 20px;
+      }
+
+      .desc {
+        color: #999;
+      }
+
+      .searchItem {
+        padding: 8px 20px;
+        border: 1px dashed #EEEEEE;
+        margin: 0 5px;
+        border-radius: 5px;
+      }
+
+      .active {
+        background-color: #ff9900;
+        color: #fff;
+        border: none;
+      }
+    }
+
+    .panel {
+      margin-top: 10px;
+      background-color: #FFFFFF;
+
+      .noData {
+        background-color: rgba(50, 50, 50, 0.7);
+        color: #fff;
+        padding: 15px 30px;
+        position: absolute;
+        top: 40%;
+        left: 40%;
+      }
+      .chartPie-box{
+        height: 350px;
+      }
+      .panel-title {
+        font-size: 18px;
+        color: #333;
+        padding-bottom: 5px;
+      }
+
+      .tab-card {
+        float: right;
+
+        .tab-card-item {
+          padding: 5px 10px;
+          border: 1px solid #eee;
+          cursor: pointer;
+        }
+
+        .checked-item {
+          border-color: #2d8cf0;
+          color: #2d8cf0;
+        }
+      }
+
+      .border-radius {
+        border-radius: 15px;
+      }
+
+      .background-blue {
+        background-color: #157edf;
+      }
+
+      .background-green {
+        background-color: #29aa4f;
+      }
+
+      .background-yellow {
+        background-color: #dbb211;
+      }
+
+      .background-pop {
+        background-color: #8544e0;
+      }
+
+      .background-black {
+        background-color: #1f2c65;
+      }
+
+      .background-pink {
+        background-color: #d23e57;
+      }
+
+      .background-skyblue {
+        background-color: #00aaff;
+      }
+
+      .background-greenBlue {
+        background-color: #1b9eaa;
+      }
+
+      .background-blackGreen {
+        background-color: #357c09;
+      }
+
+      .background-orange {
+        background-color: #d2701b;
+      }
+    }
+
+    .count-div {
+      display: flex;
+      justify-content: space-between;
+    }
+
+    .count-card {
+      width: 49%;
+      flex-shrink: 0;
+      flex-grow: 0;
+    }
+
+    .home-card-title {
+      text-align: center;
+    }
+
+    .home-list {
+      margin: 0;
+    }
+
+    .home-list-li {
+      list-style: none;
+      display: flex;
+      justify-content: space-between;
+      padding: 5px 0;
+
+      i {
+        color: #B2B2B2;
+      }
+    }
+
+    .module-list {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      flex-direction: column;
+      color: #FFFFFF;
+
+      span {
+        font-weight: 600;
+        font-size: 20px;
+      }
+    }
+
+    .module-icon {
+      width: 52px;
+      height: 54px;
+
+    }
   }
 </style>

+ 2 - 2
vue.config.js

@@ -109,8 +109,8 @@ const vueConfig = {
     proxy: {
       '/api': {
         // target: 'http://carwash.test.zyucgj.com/cw-admin/',
-        // target: 'http://192.168.16.102:8101/cw-admin/',
-        target: 'https://carwash.test.zyucgj.com/cw-admin/',
+        target: 'http://192.168.16.102:8101/cw-admin/',
+        // target: 'https://carwash.test.zyucgj.com/cw-admin/',
         ws: false,
         changeOrigin: true,
         pathRewrite: {