Bladeren bron

Merge branch 'develop_hgyh_v2.0' of chelingzhu-web/qhsxKx-admin-html into master

增加统计报表功能
lilei 4 jaren geleden
bovenliggende
commit
ad917b0bd6

+ 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",

+ 10 - 0
src/api/order.js

@@ -55,3 +55,13 @@ export const orderTotal = params => {
     method: 'post'
   })
 }
+// 导出
+export const exportOrder = params => {
+  const url = 'order/export'
+  return axios({
+    url: url,
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}

+ 9 - 0
src/api/report-charts.js

@@ -0,0 +1,9 @@
+import { axios } from '@/utils/request'
+// 获取列表数据
+export const getOrderstatistics = params => {
+  return axios({
+    url: 'order/statistics',
+    data: params,
+    method: 'POST'
+  })
+}

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

@@ -0,0 +1,183 @@
+<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, String],
+      default: 100
+    },
+    // y轴坐标单位
+    yUnit: {
+      type: String,
+      default: ''
+    },
+    // 当有多列柱状图展示时的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
+          } else {
+            this.isShowZoom = false
+          }
+          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 () {
+      const legend = this.seriesData.map(_ => _.name)
+      this.$nextTick(() => {
+        const xAxisData = this.value ? Object.keys(this.value) : this.xAxisData
+        const seriesData = this.value ? Object.values(this.value) : this.seriesData
+        const 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,
+            axisLabel: {
+              formatter: `{value} ${this.yUnit}`
+            },
+            // 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)
+        this.dom.resize() // 解决首次加载宽度超出容器bug
+        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轴长度大于25时,显示滚动条并且滚动条结束位置在80%
+          if (nVal.length > 25) {
+            this.isShowZoom = true
+            this.xZoomEnd = 80
+          } else {
+            this.isShowZoom = false
+          }
+          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, // echarts实例对象
+      isShowZoom: false, // 是否展示滚动条
+      xZoomEnd: 100 // x轴滚动条结束位置
+    }
+  },
+  methods: {
+    resize () {
+      this.dom.resize()
+    },
+    pageInit () {
+      const legend = this.seriesData.map(_ => _.name)
+      this.$nextTick(() => {
+        const xAxisData = this.value ? Object.keys(this.value) : this.xAxisData
+        const seriesData = this.value ? Object.values(this.value) : this.seriesData
+        const 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)
+        this.dom.resize()
+        on(window, 'resize', this.resize)
+      })
+    }
+  },
+  mounted () {
+    this.pageInit()
+  },
+  beforeDestroy () {
+    off(window, 'resize', this.resize)
+  }
+}
+</script>

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

@@ -0,0 +1,191 @@
+<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, String],
+      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 // echarts实例
+    }
+  },
+  methods: {
+    resize () {
+      this.dom.resize()
+    },
+    pageInit () {
+      this.$nextTick(() => {
+        const legend = this.value.map(_ => _.name)
+        const 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: 24,
+                  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)
+        this.dom.resize()
+        on(window, 'resize', this.resize)
+      })
+    },
+    // 格式化金额
+    formatNumber (num) {
+      const 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"
+                }
+            }
+        }
+    }
+}

+ 61 - 61
src/libs/getDate.js

@@ -1,65 +1,65 @@
 // 引入 moment 时间插件
-import moment from "moment";
-//获取今日/昨日/本周/上周/本月/上月 时间
+import moment from 'moment'
+// 获取今日/昨日/本周/上周/本月/上月 时间
 export default {
-    // 获取今日的开始结束时间
-    getToday() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().startOf("day").valueOf()).format("YYYY-MM-DD");
-        obj.endtime = moment(moment().valueOf()).format("YYYY-MM-DD");
-        return obj
-    },
-    // 获取昨日的开始结束时间
-    getYesterday() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().add(-1, 'days').startOf("day").valueOf()).format("YYYY-MM-DD");
-        obj.endtime = moment(moment().add(-1, 'days').endOf('day').valueOf()).format('YYYY-MM-DD');
-        return obj
-    },
-    // 获取当前周的开始结束时间
-    getCurrWeekDays() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().week(moment().week()).startOf('week').add(1, 'days').valueOf()).format('YYYY-MM-DD')
-        obj.endtime = moment(moment().week(moment().week()).endOf('week').add(1, 'days').valueOf()).format('YYYY-MM-DD');
-        return obj
-    },
-    // 获取上一周的开始结束时间
-    getLastWeekDays() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().week(moment().week() - 1).startOf('week').add(1, 'days').valueOf()).format('YYYY-MM-DD')
-        obj.endtime = moment(moment().week(moment().week() - 1).endOf('week').add(1, 'days').valueOf()).format('YYYY-MM-DD');
-        return obj
-    },
-    // 获取当前月的开始结束时间
-    getCurrMonthDays() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().month(moment().month()).startOf('month').valueOf()).format('YYYY-MM-DD');
-        obj.endtime = moment(moment().month(moment().month()).endOf('month').valueOf()).format('YYYY-MM-DD');
-        return obj
-    },
-    // 获取上一个月的开始结束时间
-    getLastMonthDays() {
-        let obj = {
-            starttime: '',
-            endtime: ''
-        }
-        obj.starttime = moment(moment().month(moment().month() - 1).startOf('month').valueOf()).format('YYYY-MM-DD');
-        obj.endtime = moment(moment().month(moment().month() - 1).endOf('month').valueOf()).format('YYYY-MM-DD');
-        return obj
+  // 获取今日的开始结束时间
+  getToday () {
+    const obj = {
+      starttime: '',
+      endtime: ''
     }
+    obj.starttime = moment(moment().startOf('day').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取昨日的开始结束时间
+  getYesterday () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().add(-1, 'days').startOf('day').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().add(-1, 'days').endOf('day').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取当前周的开始到当天结束时间
+  getCurrWeekDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().week(moment().week()).startOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取上一周的开始结束时间
+  getLastWeekDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().week(moment().week() - 1).startOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().week(moment().week() - 1).endOf('week').add('days').valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取当前月的开始到当天结束时间
+  getCurrMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month()).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().valueOf()).format('YYYY-MM-DD')
+    return obj
+  },
+  // 获取上一个月的开始结束时间
+  getLastMonthDays () {
+    const obj = {
+      starttime: '',
+      endtime: ''
+    }
+    obj.starttime = moment(moment().month(moment().month() - 1).startOf('month').valueOf()).format('YYYY-MM-DD')
+    obj.endtime = moment(moment().month(moment().month() - 1).endOf('month').valueOf()).format('YYYY-MM-DD')
+    return obj
+  }
 }

+ 428 - 0
src/libs/tools.js

@@ -0,0 +1,428 @@
+export const forEach = (arr, fn) => {
+  if (!arr.length || !fn) return
+  let i = -1
+  const len = arr.length
+  while (++i < len) {
+    const item = arr[i]
+    fn(item, i, arr)
+  }
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
+ */
+export const getIntersection = (arr1, arr2) => {
+  const len = Math.min(arr1.length, arr2.length)
+  let i = -1
+  const 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
+}
+//  获取三个月后的时间戳
+export const getThreeMonthsAfter = (dtstr) => {
+  var s = dtstr.split('-')
+  var yy = parseInt(s[0])
+  var mm = parseInt(s[1])
+  var dd = parseInt(s[2])
+  var dt = new Date(yy, mm + 2, dd)
+  return dt.valueOf()
+}
+/**
+ * @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 {
+    const _date = new Date(val)
+    const _year = _date.getFullYear()
+    const _montn = (_date.getMonth() + 1) < 10 ? '0' + (_date.getMonth() + 1) : (_date.getMonth() + 1)
+    const _day = _date.getDate() < 10 ? '0' + _date.getDate() : _date.getDate()
+    const _hour = _date.getHours() < 10 ? '0' + _date.getHours() : _date.getHours()
+    const _minutes = _date.getMinutes() < 10 ? '0' + _date.getMinutes() : _date.getMinutes()
+    const _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 {
+    const 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) => {
+  const ret = {}
+  for (var a in obj) {
+    ret[a] = obj[a]
+  }
+  return ret
+}
+
+/**
+ * 校验身份证号合法性
+ */
+export const checkIdNumberValid = (tex) => {
+  // var tip = '输入的身份证号有误,请检查后重新输入!'
+  let num = tex
+  num = num.toUpperCase()
+
+  const 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
+  }
+
+  // 验证前两位地区是否有效
+  const 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})$/)
+    const arrSplit = num.match(re)
+
+    // 检查生日日期是否正确
+    const dtmBirth = new Date('19' + arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+    const 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)$/)
+    const arrSplit = num.match(re)
+    // 检查生日日期是否正确
+    const dtmBirth = new Date(arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+    const 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
+      const arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+      const 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
+}

+ 600 - 8
src/views/Home.vue

@@ -1,26 +1,618 @@
 <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" layout="inline">
+              <a-form-item props="queryWord" label="统计区间">
+                <a-range-picker
+                  style="width: 220px;"
+                  @change="dateChange"
+                  v-model.trim="searchForm.queryWord"
+                  :disabledDate="disabledDate"
+                />
+              </a-form-item>
+              <a-form-item props="storeIds" label="洗车网点">
+                <a-select
+                  allowClear
+                  mode="multiple"
+                  placeholder="选择网点"
+                  v-model="searchForm.storeIdList"
+                  style="width: 200px"
+                >
+                  <a-select-option v-for="item in storesList" :value="item.id" :key="item.id">
+                    {{ item.name }}
+                  </a-select-option>
+                </a-select>
+              </a-form-item>
+              <a-form-item>
+                <a-button type="primary" class="search-btn" @click="handleSearch()">查询</a-button>
+                <a-button type="" style="margin-left: 10px;" @click="handleReset()">重置</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" title="洗车量趋势">
+        <!-- 洗车量趋势图表展示 -->
+        <chart-line
+          style="height: 300px;"
+          color="#6C6FBE"
+          :yMax="1000"
+          yUnit="台"
+          :xAxisRotate="resize"
+          :xAxisData="XdaysData"
+          :seriesData="washCarsChartData"
+          text="" />
+        <span v-if="isNoData" class="noData">暂无数据</span>
+      </a-card>
+      <!-- 各洗车类型数据 -->
+      <a-card class="panel " title="各洗车类型数据">
+        <a-row :gutter="24">
+          <a-col :md="4" :lg="4">
+            <a-card class="border-radius background-skyblue">
+              <div class="module-list ">
+                <span>{{ chartData.KX }}</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>{{ chartData.JX }}</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>{{ chartData.DLX }}</span>
+                <p>打蜡精洗</p>
+              </div>
+
+            </a-card>
+          </a-col>
+        </a-row>
+        <!-- 洗车类型数据图表展示 -->
+        <a-row :gutter="24">
+          <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 span="24" v-if="currentTabChart=='tab1'">
+            <chart-bar
+              ref="ChartBar"
+              style="height: 300px;"
+              color="#6C6FBE"
+              :xAxisRotate="resize"
+              :yMax="1000"
+              yUnit="台"
+              :xAxisData="XdaysData"
+              :seriesData="washTypeBarData"
+              text="" />
+            <span v-if="isNoData" class="noData">暂无数据</span>
+          </a-col>
+          <!-- 变化趋势 -->
+          <a-col span="24" v-if="currentTabChart=='tab2'">
+            <chart-line
+              ref="ChartLine"
+              style="height: 300px;"
+              color="#6C6FBE"
+              yUnit="台"
+              :yMax="1000"
+              :xAxisRotate="resize"
+              :xAxisData="XdaysData"
+              :seriesData="washTypeLineData"
+              text="" />
+            <span v-if="isNoData" class="noData">暂无数据</span>
+          </a-col>
+        </a-row>
+      </a-card>
+      <a-card class="panel chart-pie" title="各类型占比">
+        <a-row :gutter="24">
+          <a-col span="12">
+            <chart-pie
+              ref="chartPie"
+              class="chartPie-box"
+              :searchData="XdaysData"
+              :value="washTypePieData"
+              title="洗车总量(台)"
+              :total="chartData.allOrderNum"
+              :color="washTypePieColor"
+              :xAxisRotate="resize"
+              text="" />
+          </a-col>
+          <a-col span="12">
+            <!-- 用券与未用券百分比展示 -->
+            <chart-pie
+              ref="serverChartPie"
+              class="chartPie-box"
+              :searchData="XdaysData"
+              :value="couponChartData"
+              title="洗车总量(台)"
+              :total="chartData.allOrderNum"
+              :color="couponColor"
+              :xAxisRotate="resize"
+              text="" />
+          </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'
+import moment from 'moment'
+import { getOrderstatistics } from '@/api/report-charts.js'
+import {
+  storeList
+} from '@/api/order.js'
 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
+      }
+    },
+    // 各类型占比总计数据
+    washTypePieData () {
+      const data = [{
+        'name': '快速洗车',
+        'value': this.chartData.KX
+      }, {
+        'name': '精致洗车',
+        'value': this.chartData.JX
+      }, {
+        'name': '打蜡精洗',
+        'value': this.chartData.DLX
+      }]
+      return data
+    },
+    // 洗车用券量占比数据
+    couponChartData () {
+      const data = [{
+        'name': '未用券洗车量',
+        'value': this.chartData.notUseCouponOrderNum
+      }, {
+        'name': '用券洗车量',
+        'value': this.chartData.useCouponOrderNum
+      }]
+      return data
+    }
+  },
+
   data () {
-    return {}
+    return {
+      screenWidth: document.body.clientWidth, // 这里是给到了一个默认值
+      timer: false,
+      form: this.$form.createForm(this, { name: 'searchForm' }),
+      isNoData: false, // 是否有每天营业额及进店量数据
+      storesList: [], // 洗车网点列表
+      searchForm: {
+        queryWord: null, // 时间查询条件,默认本周
+        storeIdList: [] // 已选洗车网点
+      },
+      chartData: {
+        KX: 0, // 快洗
+        JX: 0, // 精洗
+        DLX: 0, // 打蜡洗
+        allOrderNum: 0, // 洗车总量
+        useCouponOrderNum: 0, // 用券洗车量
+        notUseCouponOrderNum: 0 // 未用券洗车量
+      },
+      // 快速删选tab
+      tabList: [{
+        name: '今天',
+        type: 'today'
+      },
+      {
+        name: '本周',
+        type: 'thisWeek'
+      },
+      {
+        name: '上周',
+        type: 'lastWeek'
+      },
+      {
+        name: '本月',
+        type: 'thisMonth'
+      }
+      ],
+      curentType: 'thisWeek', // 当前所选时间查询类型
+      beginDate: null, // 开始时间
+      endDate: null, // 结束时间
+      // 洗车量图表数据
+      washCarsChartData: [{
+        name: '全部洗车量',
+        type: 'line',
+        data: []
+      },
+      {
+        name: '用券洗车量',
+        type: 'line',
+        data: []
+      }
+      ],
+      couponColor: ['#d48265', '#ff55ff'],
+      // 洗车类型数据
+      currentTabChart: 'tab1', // 洗车类型详情数据与变化趋势的tab 默认展示详情数据
+      XdaysData: [], // 图表X轴时间集合
+      // 洗车类型柱状图表数据
+      washTypeBarData: [{
+        name: '快速洗车',
+        type: 'bar',
+        data: []
+      },
+      {
+        name: '精致洗车',
+        type: 'bar',
+        data: []
+      },
+      {
+        name: '打蜡精洗',
+        type: 'bar',
+        data: []
+      }
+      ],
+      // 洗车类型折线图表数据
+      washTypeLineData: [{
+        name: '快速洗车',
+        type: 'line',
+        data: []
+      },
+      {
+        name: '精致洗车',
+        type: 'line',
+        data: []
+      },
+      {
+        name: '打蜡精洗',
+        type: 'line',
+        data: []
+      }
+      ],
+      // 各类型占比
+      washTypePieColor: ['#ffaa00', '#00ff00', '#00aaff', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
+      databack: {
+        couponChartData: [],
+        washTypePieData: [],
+        chartData: {}
+      }
+    }
   },
   created () {
   },
-  methods: { 
+  methods: {
+    moment,
+    // 时间改变
+    dateChange (dates) {
+      console.log(dates, this.searchForm.queryWord, 'dddddd')
+      this.curentType = ''
+    },
+    // 不可选日期
+    disabledDate (date) {
+      return (date && date.valueOf() > Date.now()) || (date.valueOf() < Date.now() - (90 * 24 * 60 * 60 * 1000))
+    },
+    // 查询洗车网点
+    getListStore () {
+      storeList({ pageNo: 1, pageSize: 1000 }).then(res => {
+        console.log(res)
+        this.storesList = res.data.list ? res.data.list : []
+      })
+    },
+    // 页面初始化
+    pageInit () {
+      this.form.resetFields()
+      this.databack = {
+        couponChartData: _.cloneDeep(this.couponChartData),
+        washTypePieData: _.cloneDeep(this.washTypePieData),
+        chartData: _.cloneDeep(this.chartData)
+      }
+    },
+    // 查询
+    handleSearch () {
+      this.showChart = false
+      if (this.searchForm.queryWord != ',' && this.searchForm.queryWord.length) {
+        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('请选择统计区间')
+      }
+    },
+    // 重置
+    handleReset () {
+      this.searchForm.storeIdList = []
+      this.curentType = 'thisWeek'
+      this.getCurentSearchTime()
+    },
+    // 格式化时间
+    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: [moment(getDate.getLastMonthDays().starttime), moment(getDate.getLastMonthDays().endtime)],
+        thisMonth: [moment(getDate.getCurrMonthDays().starttime), moment(getDate.getCurrMonthDays().endtime)],
+        lastWeek: [moment(getDate.getLastWeekDays().starttime), moment(getDate.getLastWeekDays().endtime)],
+        thisWeek: [moment(getDate.getCurrWeekDays().starttime), moment(getDate.getCurrWeekDays().endtime)],
+        yesterday: [moment(getDate.getYesterday().starttime), moment(getDate.getYesterday().endtime)],
+        today: [moment(getDate.getToday().starttime), moment(getDate.getToday().endtime)]
+      }
+      console.log(quickType[this.curentType])
+      this.searchForm.queryWord = quickType[this.curentType]
+      this.beginDate = quickType[this.curentType][0].format('YYYY-MM-DD')
+      this.endDate = quickType[this.curentType][1].format('YYYY-MM-DD')
+      console.log(this.searchForm.queryWord, '1111111')
+      this.getPageData()
+    },
+    // 根据查询条件获取所有数据
+    getPageData () {
+      this.XdaysData = [] // x轴置空
+      const _date = {
+        beginDate: this.beginDate,
+        endDate: this.endDate,
+        storeIdList: this.searchForm.storeIdList
+      }
+      console.log(_date, '_date')
+      getOrderstatistics(_date).then(res => {
+        console.log(res, 'rrrrrrr')
+        if (res.status == 200) {
+          this.chartData = res.data
+          if (res.data.dateList && res.data.dateList.length) {
+            this.isNoData = false
+            this.filterData(res.data.dateList)
+          } else {
+            this.isNoData = true
+          }
+        } else {
+          this.isNoData = true
+        }
+      })
+    },
+    // 整合图表每天数据
+    filterData (data) {
+      const days = [] // 日期合集
+      const kxData = [] // 快速洗车数据
+      const jxData = [] // 精致洗车数据
+      const dlxData = [] // 打蜡洗车数据
+      const totalData = [] // 全部洗车量数据
+      const useCouponData = [] // 用券洗车量数据
+      data.map(item => {
+        const itemDay = item.date.split('-')
+        const month = Number(itemDay[1])
+        const day = Number(itemDay[2])
+        const time = month + '月' + day + '日'
+        days.push(time)
+        kxData.push(item.KX)
+        jxData.push(item.JX)
+        dlxData.push(item.DLX)
+        totalData.push(item.allOrderNum)
+        useCouponData.push(item.useCouponOrderNum)
+      })
+      this.XdaysData = days
+      const washTypeData = [kxData, jxData, dlxData]
+      console.log(washTypeData, 'washTypeData')
+      // 洗车类型柱状图表数据
+      this.washTypeBarData.map((item, index) => {
+        item.data = washTypeData[index]
+      })
+      // 洗车类型折线图表数据
+      this.washTypeLineData.map((item, index) => {
+        item.data = washTypeData[index]
+      })
+      // 洗车量图表数据赋值
+      this.washCarsChartData[0].data = totalData
+      this.washCarsChartData[1].data = useCouponData
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.pageInit()
+      vm.getListStore() // 获取网点列表
+      vm.curentType = 'thisWeek'
+      vm.getCurentSearchTime()
+    })
+  },
+  // 将 reisze 事件在 vue mounted 的时候 去挂载一下它的方法
+  mounted () {
+    const that = this
+    window.onresize = () => {
+      return (() => {
+        window.screenWidth = document.body.clientWidth
+        that.screenWidth = window.screenWidth
+      })()
+    }
   }
 }
 </script>
-<style scoped>
-  .home {
-    /* width: 900px;
-    margin: 0 auto;
-    padding: 25px 0; */
+<style lang="less" scoped>
+  .home{
+    width: 100%;
+  }
+  .report-page {
+    margin-top: 10px;
+    height: calc(100vh - 285px);
+    overflow-y: scroll;
+    overflow-x: hidden;
+    .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: 300px;
+        width: 100%;
+      }
+      .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;
+      }
+    }
+
+    .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>

+ 75 - 20
src/views/Order/OrderCenter.vue

@@ -4,46 +4,47 @@
     <div class="orderCenter-searchForm">
       <a-form ref="searchForm">
         <a-row :gutter="48">
-          <a-col :span="8">
-            <a-form-item label="下单时间" :label-col="{ span:4 }" :wrapper-col="{ span:12}">
-              <a-range-picker v-model="time" :format="dateFormat" :placeholder="['开始时间','结束时间']" @change="onChange" />
+          <a-col :span="6">
+            <a-form-item label="下单时间" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+              <a-range-picker v-model="time" :disabledDate="disabledDate" :format="dateFormat" :placeholder="['开始时间','结束时间']" @change="onChange" />
             </a-form-item>
           </a-col>
           <a-col :span="6">
-            <a-form-item label="网点名称" :label-col="{ span:6 }" :wrapper-col="{ span:12}">
+            <a-form-item label="网点名称" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
               <a-select placeholder="请选择" allowClear v-model="storeId" mode="multiple">
                 <a-select-option v-for="item in storeOptionData" :key="item.id" :value="item.id">{{ item.name }}</a-select-option>
               </a-select>
             </a-form-item>
           </a-col>
           <a-col :span="6">
-            <a-form-item label="单号" :label-col="{ span:6 }" :wrapper-col="{ span:12}">
+            <a-form-item label="单号" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
               <a-input v-model.trim="searchForm.orderNo" placeholder="请输入" allowClear />
             </a-form-item>
           </a-col>
-          <a-col :span="4">
-            <a-button style="margin-right: 10px;" type="primary" @click="handleSearch">查询</a-button>
-            <a-button type="" @click="handleRest">重置</a-button>
-          </a-col>
-        </a-row>
-        <a-row :gutter="48">
-          <a-col :span="8">
-            <a-form-item label="服务项目" :label-col="{ span:4 }" :wrapper-col="{ span:12}">
+          <a-col :span="6">
+            <a-form-item label="服务项目" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
               <a-select placeholder="请选择" allowClear v-model="searchForm.itemId">
                 <a-select-option v-for="item in itemOptionData" :key="item.id" :value="item.id">{{ item.itemName }}</a-select-option>
               </a-select>
             </a-form-item>
           </a-col>
+        </a-row>
+        <a-row :gutter="48">
           <a-col :span="6">
-            <a-form-item label="手机号码" :label-col="{ span:6 }" :wrapper-col="{ span:12}">
+            <a-form-item label="手机号码" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
               <a-input v-model.trim="searchForm.customerMobile" placeholder="请输入" allowClear :maxLength="11" />
             </a-form-item>
           </a-col>
           <a-col :span="6">
-            <a-form-item label="状态" :label-col="{ span:6 }" :wrapper-col="{ span:12}">
+            <a-form-item label="状态" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
               <v-select v-model="searchForm.orderStatus" ref="orderStatus" code="ORDER_STATUS" placeholder="请选择" allowClear></v-select>
             </a-form-item>
           </a-col>
+          <a-col :span="12">
+            <a-button style="margin-right: 10px;" type="primary" @click="handleSearch">查询</a-button>
+            <a-button type="" @click="handleRest">重置</a-button>
+            <a-button type="" :loading="loading" class="export-btn" @click="handleExport">导出</a-button>
+          </a-col>
         </a-row>
       </a-form>
     </div>
@@ -92,13 +93,15 @@ import {
   STable,
   VSelect
 } from '@/components'
-import getDate from '../../libs/getDate.js'
+import getDate from '@/libs/getDate.js'
+import { getThreeMonthsAfter } from '@/libs/tools.js'
 import {
   orderCenterList,
   itemList,
   storeList,
-  orderTotal
-} from '../../api/order.js'
+  orderTotal,
+  exportOrder
+} from '@/api/order.js'
 import OrderDetailModal from './OrderDetailModal.vue'
 import moment from 'moment'
 export default {
@@ -121,6 +124,7 @@ export default {
         customerMobile: '', // 手机号码
         orderStatus: '' // 状态
       },
+      loading: false, // 导出loading
       orderTotal: '', // 合计开单数量
       paymentAmountTotal: '', // 合计实收金额
       itemOptionData: [], // 服务项目数据
@@ -225,6 +229,10 @@ export default {
   },
   methods: {
     moment,
+    // 不可选日期
+    disabledDate (date) {
+      return date && date.valueOf() > Date.now()
+    },
     // 创建时间change
     onChange (dates, dateStrings) {
       if ((dates, dateStrings)) {
@@ -304,7 +312,7 @@ export default {
     // },
     // 查询
     handleSearch () {
-      this.$refs.table.refresh()
+      this.$refs.table.refresh(true)
       this.getTotal()
     },
     // 重置
@@ -323,6 +331,48 @@ export default {
       // this.$refs.searchForm.resetFields()
       this.$refs.table.refresh()
       this.getTotal()
+    },
+    // 导出
+    handleExport () {
+      const params = {
+        beginDate: this.searchForm.beginDate == null ? getDate.getToday().starttime : this.searchForm.beginDate,
+        endDate: this.searchForm.endDate == null ? getDate.getToday().endtime : this.searchForm.endDate,
+        storeIdList: this.storeId,
+        orderNo: this.searchForm.orderNo,
+        itemId: this.searchForm.itemId,
+        customerMobile: this.searchForm.customerMobile,
+        orderStatus: this.searchForm.orderStatus
+      }
+      // 获取距开始日期3个月后的时间戳
+      const tVal = getThreeMonthsAfter(params.beginDate)
+      // 获取结束时间时间戳
+      const endVal = new Date(params.endDate.replace(/-/g, '/'))
+      if (endVal.valueOf() > tVal - 86400000) {
+        return this.$message.warning('单次最多只能导出3个月的数据,请缩小查询区间后再进行导出!')
+      }
+      this.loading = true
+      exportOrder(params).then(res => {
+        console.log(res)
+        this.loading = false
+        this.download(res)
+      })
+    },
+    download (data) {
+      if (!data) {
+        return
+      }
+      const url = window.URL.createObjectURL(new Blob([data]))
+      const link = document.createElement('a')
+
+      link.style.display = 'none'
+      link.href = url
+      const a = moment().format('YYYYMMDDhhmmss')
+      const fname = '订单记录-' + a
+      console.log(fname, '111111')
+      link.setAttribute('download', fname + '.xlsx')
+
+      document.body.appendChild(link)
+      link.click()
     }
   },
   beforeRouteEnter (to, from, next) {
@@ -342,7 +392,12 @@ export default {
 			margin-bottom: 10px;
 		}
 	}
-
+  .export-btn{
+    background-color: #ff9900;
+    border-color: #ff9900;
+    color: #fff;
+    margin-left: 10px;
+  }
 	// 合计
 	.total {
 		margin: 15px 0 25px;

+ 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: {