lilei 3 anos atrás
pai
commit
0901dcd009

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

@@ -0,0 +1,229 @@
+<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: {
+    // 单列柱状图,列name为x轴,value为y轴
+    value: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    text: String,
+    subtext: String,
+    color: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 当有多列柱状图展示时的X轴坐标数据
+    xAxisData: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 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
+            this.xZoomEnd = 100
+          }
+          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
+    },
+    value: {
+      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 that = this
+      const legend = this.seriesData.map(_ => _.name)
+      this.$nextTick(() => {
+        const keys = this.value.map(item => (item = item.name))
+        const values = this.value.map(item => (item = item.value))
+        const xAxisData = this.value.length ? keys : this.xAxisData
+        const seriesData = this.value.length ? values : this.seriesData
+        const yMax = this.getYMax(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: yMax,
+            axisLabel: {
+              formatter: `{value} ${this.yUnit}`
+            },
+            // interval: 2,
+            position: 'left'
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          }
+        }
+        // 单柱图
+        if (this.value.length) {
+          option.series = [
+            {
+              data: seriesData,
+              type: 'bar',
+              barWidth: 35,
+              label: {
+                normal: {
+                  show: true,
+                  position: 'top'
+                }
+              }
+            }
+          ]
+        } else {
+          option.series = this.seriesData
+        }
+        // 已创建的先销毁
+        if (this.dom) {
+          this.dom.dispose()
+        }
+        this.dom = echarts.init(this.$refs.dom, 'tdTheme')
+        this.dom.setOption(option)
+        // 更新y轴最大值
+        this.dom.on('legendselectchanged', function (params) {
+          // console.log(params, that.seriesData, 'legendselectchanged')
+          const data = that.seriesData.filter(item => params.selected[item.name])
+          option.yAxis.max = that.getYMax(data)
+          that.dom.setOption(option)
+        })
+        // 解决首次加载宽度超出容器bug
+        this.dom.resize()
+        on(window, 'resize', this.resize)
+      })
+    },
+    // 计算y轴最大值
+    getYMax (data) {
+      let max = []
+      data.map(item => {
+        max = max.concat(item.data)
+      })
+      return Math.max.apply(null, max)
+    }
+  },
+  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 }

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

@@ -0,0 +1,210 @@
+<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: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 当有多列折线图展示时的X轴坐标数据
+    xAxisData: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // 当有多列折线图展示时的y轴坐标数据
+    seriesData: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // y轴坐标单位
+    yUnit: {
+      type: String,
+      default: ''
+    },
+    // x轴标签旋转角度
+    xAxisRotate: {
+      type: Number,
+      default: 0
+    }
+  },
+  watch: {
+    xAxisData: {
+      handler (nVal, oVal) {
+        if (nVal) {
+          // 根据x轴时间长度控制滚动条位置及显示状态 x轴长度大于25时,显示滚动条并且滚动条结束位置在80%
+          if (nVal.length > 25) {
+            this.isShowZoom = true
+            this.xZoomEnd = 80
+          } else {
+            this.isShowZoom = false
+            this.xZoomEnd = 100
+          }
+          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轴滚动条结束位置
+      yMax: 1000 // y轴最大值
+    }
+  },
+  methods: {
+    resize () {
+      this.dom.resize()
+    },
+    pageInit () {
+      const that = this
+      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 yMax = this.getYMax(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: 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 {
+          option.series = this.seriesData
+        }
+        // 已创建的先销毁
+        if (this.dom) {
+          this.dom.dispose()
+        }
+        this.dom = echarts.init(this.$refs.dom, 'tdTheme')
+        this.dom.setOption(option)
+        // 更新y轴最大值
+        this.dom.on('legendselectchanged', function (params) {
+          // console.log(params, that.seriesData, 'legendselectchanged')
+          const data = that.seriesData.filter(item => params.selected[item.name])
+          option.yAxis.max = that.getYMax(data)
+          that.dom.setOption(option)
+        })
+        // 重置图标大小
+        this.dom.resize()
+        on(window, 'resize', this.resize)
+      })
+    },
+    // 计算y轴最大值
+    getYMax (data) {
+      let max = []
+      data.map(item => {
+        max = max.concat(item.data)
+      })
+      return Math.max.apply(null, max)
+    }
+  },
+  mounted () {
+    this.pageInit()
+  },
+  beforeDestroy () {
+    off(window, 'resize', this.resize)
+  }
+}
+</script>

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

@@ -0,0 +1,200 @@
+<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: ''
+    },
+    // 圆环各类型颜色
+    color: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    // x轴标签旋转角度
+    xAxisRotate: {
+      type: Number,
+      default: 0
+    },
+    // 单位
+    unit: {
+      type: String,
+      default: ''
+    }
+  },
+  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}${this.unit} ${this.unit == '%' ? '' : '({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) + this.unit + '}'
+                    )
+                  },
+                  padding: [0, -140, 25, -140],
+                  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
+                }
+              }
+            }
+          ]
+        }
+        // 已创建的先销毁
+        if (this.dom) {
+          this.dom.dispose()
+        }
+        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"
+                }
+            }
+        }
+    }
+}

+ 428 - 0
src/libs/tools.js

@@ -3,3 +3,431 @@ export const getOperationalPrecision = (num1, num2) => {
   const val = ((num1 * 10000) * (num2 * 10000) / 100000000).toFixed(2) || 0
   return val != 0 ? val : 0
 }
+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
+}

+ 269 - 2
src/views/reportData/stockExpenditureReport/list.vue

@@ -1,8 +1,275 @@
 <template>
+  <a-card size="small" :bordered="false" class="noticeList-wrap">
+    <!-- 搜索条件 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="handelSearch(1)">
+        <a-row :gutter="15">
+          <a-col :md="6" :sm="24">
+            <a-form-item label="出库完成时间">
+              <rangeDate ref="rangeDate" @change="dateChange" />
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="24">
+            <a-button type="primary" @click="handelSearch(1)" id="noticeList-refresh">查询</a-button>
+            <a-button style="margin-left: 8px" @click="resetSearchForm" id="noticeList-reset">重置</a-button>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+    <!-- 列表 -->
+    <a-row :gutter="15">
+      <a-col :md="12" :sm="24">
+        <div class="chart-box">
+          <div class="chart-hj">库存总出数量:372,总成本:¥4315.50</div>
+          <div id="con1" class="chart"></div>
+        </div>
+      </a-col>
+      <a-col :md="12" :sm="24">
+        <a-table
+          class="sTable"
+          ref="table"
+          size="default"
+          :rowKey="(record) => record.id"
+          :columns="columns"
+          :dataSource="loadData"
+          :loading="listLoading"
+          :pagination="false"
+          bordered>
+        </a-table>
+      </a-col>
+    </a-row>
+  </a-card>
 </template>
 
 <script>
-</script>
+import rangeDate from '@/views/common/rangeDate.vue'
+import echarts from 'echarts'
+export default {
+  components: { rangeDate },
+  data () {
+    return {
+      queryParam: { //  查询条件
+        beginDate: '',
+        endDate: ''
+      },
+      disabled: false, //  查询、重置按钮是否可操作
+      columns: [
+        { title: '明细项', dataIndex: 'itemName', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '数量(件)', dataIndex: 'qty', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '成本(元)', dataIndex: 'cost', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '售价(元)', dataIndex: 'price', align: 'center', customRender: function (text) { return text || '--' } }
+      ],
+      // 统计图
+      pieData: [],
+      pieColor: ['#00aaff', '#ffaa00', '#00aa00', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
+      loadData: [],
+      listLoading: false,
+      chart1: null
+    }
+  },
 
-<style>
+  methods: {
+    // 初始化饼图 title: 标题 data:数据 bit:单位
+    initPie (chart, title, data, bit) {
+      const legend = []
+      if (bit) {
+        data.map(item => {
+    			 const p = `${item.name}: ${item.value}${bit}`
+    			 legend.push(p)
+        })
+      }
+      const option = {
+        color: this.pieColor,
+        title: {
+          text: title,
+          top: 20,
+          left: 20,
+          textStyle: {
+            color: '#abd7ff'
+          },
+          textAlingn: 'left'
+        },
+        tooltip: {
+          trigger: 'item',
+          formatter: `{b} : {d}%`
+        },
+        legend: {
+    		  orient: 'horizontal',
+    		  left: 'center',
+    		  bottom: '5',
+    		  padding: 5,
+    		  textStyle: {
+    		    color: '#abd7ff',
+            fontSize: 10
+    		  },
+    		  show: !!bit,
+    		  formatter: function (name) {
+    			  if (bit) {
+              const p = data.find(item => item.name == name)
+              return name + (p ? (':' + p.value + bit) : '')
+    			  }
+    		  }
+        },
+        series: [{
+          type: 'pie',
+          radius: ['30%', '50%'],
+          startAngle: 75,
+          top: 50,
+          avoidLabelOverlap: true, // 在标签拥挤重叠的情况下会挪动各个标签的位置,防止标签间的重叠
+          label: {
+            normal: {
+              formatter: params => {
+                return (
+                  '{name|' + params.name + '}{value|' +
+    										params.percent + '%' + '}'
+                )
+              },
+              padding: [0, -70, 25, -70],
+              rich: {
+                name: {
+                  fontSize: 12,
+                  padding: [0, 4, 0, 4],
+                  color: '#999'
+                },
+                value: {
+                  fontSize: 12,
+                  color: '#999'
+                }
+              }
+            }
+          },
+          labelLine: {
+            normal: {
+              length: 20,
+              length2: 70,
+              lineStyle: {
+                color: '#e6e6e6'
+              }
+            }
+          },
+          data: data,
+          insideLabel: {
+            show: true
+          }
+        }]
+      }
+      return option
+    },
+    //  时间  change
+    dateChange (date) {
+      this.queryParam.beginDate = date[0] ? date[0] + ' 00:00:00' : ''
+      this.queryParam.endDate = date[1] ? date[1] + ' 23:59:59' : ''
+    },
+    // 查询列表
+    handelSearch () {
+      const params = {
+        beginDate: this.queryParam.beginDate,
+        endDate: this.queryParam.endDate
+      }
+      // 开始查询
+      this.listLoading = true
+      setTimeout(() => {
+        this.listLoading = false
+        this.loadData = [
+          {
+            itemName: '采购退货',
+            qty: '14',
+            cost: '84.50',
+            price: ''
+          },
+          {
+            itemName: '散件退货',
+            qty: '3',
+            cost: '28.00',
+            price: ''
+          },
+          {
+            itemName: '销售(含急件)',
+            qty: '77',
+            cost: '837.50',
+            price: '2672.55'
+          },
+          {
+            itemName: '急件冲减',
+            qty: '9',
+            cost: '109.50',
+            price: ''
+          },
+          {
+            itemName: '店内调出',
+            qty: '6',
+            cost: '55.00',
+            price: ''
+          },
+          {
+            itemName: '盘亏出库',
+            qty: '190',
+            cost: '2725.00',
+            price: ''
+          },
+          {
+            itemName: '连锁调出',
+            qty: '73',
+            cost: '476.00',
+            price: ''
+          },
+          {
+            itemName: '总出',
+            qty: '372',
+            cost: '4315.50',
+            price: '2672.55'
+          }
+        ]
+        this.chartInit()
+      }, 600)
+    },
+    chartInit () {
+      this.loadData.map((item, index) => {
+        if (index != this.loadData.length - 1) {
+          this.pieData.push({
+            name: item.itemName + '总成本',
+            value: item.cost
+          })
+        }
+      })
+      const con5 = document.getElementById('con1')
+      const chart1 = echarts.init(con1)
+      const option = this.initPie(chart1, '', this.pieData)
+      chart1.setOption(option)
+      this.chart1 = chart1
+      this.chart1.resize()
+    },
+    //  重置
+    resetSearchForm () {
+      this.$refs.rangeDate.resetDate()
+      this.handelSearch()
+    }
+  },
+  mounted () {
+    window.onresize = () => {
+      return (() => {
+        this.chart1.resize()
+      })()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.handelSearch()
+    })
+  }
+}
+</script>
+<style lang="less">
+  .chart-box{
+    height: 400px;
+    .chart-hj{
+      font-size: 22px;
+      text-align: center;
+    }
+    .chart{
+      width: 100%;
+      height: 450px;
+      overflow: auto;
+    }
+  }
 </style>

+ 263 - 2
src/views/reportData/stockIncomeReport/list.vue

@@ -1,8 +1,269 @@
 <template>
+  <a-card size="small" :bordered="false" class="noticeList-wrap">
+    <!-- 搜索条件 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="handelSearch(1)">
+        <a-row :gutter="15">
+          <a-col :md="6" :sm="24">
+            <a-form-item label="入库完成时间">
+              <rangeDate ref="rangeDate" @change="dateChange" />
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="24">
+            <a-button type="primary" @click="handelSearch(1)" id="noticeList-refresh">查询</a-button>
+            <a-button style="margin-left: 8px" @click="resetSearchForm" id="noticeList-reset">重置</a-button>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+    <!-- 列表 -->
+    <a-row :gutter="15">
+      <a-col :md="12" :sm="24">
+        <div class="chart-box">
+          <div class="chart-hj">库存总入数量:28,总成本:¥189.50</div>
+          <div id="con1" class="chart"></div>
+        </div>
+      </a-col>
+      <a-col :md="12" :sm="24">
+        <a-table
+          class="sTable"
+          ref="table"
+          size="default"
+          :rowKey="(record) => record.id"
+          :columns="columns"
+          :dataSource="loadData"
+          :loading="listLoading"
+          :pagination="false"
+          bordered>
+        </a-table>
+      </a-col>
+    </a-row>
+  </a-card>
 </template>
 
 <script>
-</script>
+import rangeDate from '@/views/common/rangeDate.vue'
+import echarts from 'echarts'
+export default {
+  components: { rangeDate },
+  data () {
+    return {
+      queryParam: { //  查询条件
+        beginDate: '',
+        endDate: ''
+      },
+      disabled: false, //  查询、重置按钮是否可操作
+      columns: [
+        { title: '明细项', dataIndex: 'itemName', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '数量(件)', dataIndex: 'qty', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '成本(元)', dataIndex: 'cost', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '售价(元)', dataIndex: 'price', align: 'center', customRender: function (text) { return text || '--' } }
+      ],
+      // 统计图
+      pieData: [],
+      pieColor: ['#00aaff', '#ffaa00', '#00aa00', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
+      loadData: [],
+      listLoading: false,
+      chart1: null
+    }
+  },
 
-<style>
+  methods: {
+    // 初始化饼图 title: 标题 data:数据 bit:单位
+    initPie (chart, title, data, bit) {
+      const legend = []
+      if (bit) {
+        data.map(item => {
+    			 const p = `${item.name}: ${item.value}${bit}`
+    			 legend.push(p)
+        })
+      }
+      const option = {
+        color: this.pieColor,
+        title: {
+          text: title,
+          top: 20,
+          left: 20,
+          textStyle: {
+            color: '#abd7ff'
+          },
+          textAlingn: 'left'
+        },
+        tooltip: {
+          trigger: 'item',
+          formatter: `{b} : {d}%`
+        },
+        legend: {
+    		  orient: 'horizontal',
+    		  left: 'center',
+    		  bottom: '5',
+    		  padding: 5,
+    		  textStyle: {
+    		    color: '#abd7ff',
+            fontSize: 10
+    		  },
+    		  show: !!bit,
+    		  formatter: function (name) {
+    			  if (bit) {
+              const p = data.find(item => item.name == name)
+              return name + (p ? (':' + p.value + bit) : '')
+    			  }
+    		  }
+        },
+        series: [{
+          type: 'pie',
+          radius: ['30%', '50%'],
+          startAngle: 75,
+          top: 50,
+          avoidLabelOverlap: true, // 在标签拥挤重叠的情况下会挪动各个标签的位置,防止标签间的重叠
+          label: {
+            normal: {
+              formatter: params => {
+                return (
+                  '{name|' + params.name + '}{value|' +
+    										params.percent + '%' + '}'
+                )
+              },
+              padding: [0, -70, 25, -70],
+              rich: {
+                name: {
+                  fontSize: 12,
+                  padding: [0, 4, 0, 4],
+                  color: '#999'
+                },
+                value: {
+                  fontSize: 12,
+                  color: '#999'
+                }
+              }
+            }
+          },
+          labelLine: {
+            normal: {
+              length: 20,
+              length2: 70,
+              lineStyle: {
+                color: '#e6e6e6'
+              }
+            }
+          },
+          data: data,
+          insideLabel: {
+            show: true
+          }
+        }]
+      }
+      return option
+    },
+    //  时间  change
+    dateChange (date) {
+      this.queryParam.beginDate = date[0] ? date[0] + ' 00:00:00' : ''
+      this.queryParam.endDate = date[1] ? date[1] + ' 23:59:59' : ''
+    },
+    // 查询列表
+    handelSearch () {
+      const params = {
+        beginDate: this.queryParam.beginDate,
+        endDate: this.queryParam.endDate
+      }
+      // 开始查询
+      this.listLoading = true
+      setTimeout(() => {
+        this.listLoading = false
+        this.loadData = [
+          {
+            itemName: '采购入库',
+            qty: '8',
+            cost: '52.00',
+            price: ''
+          },
+          {
+            itemName: '散件入库',
+            qty: '17',
+            cost: '115.00',
+            price: ''
+          },
+          {
+            itemName: '销售退货',
+            qty: '3',
+            cost: '22.50',
+            price: '30.00'
+          },
+          {
+            itemName: '盘盈入库',
+            qty: '0',
+            cost: '00.00',
+            price: ''
+          },
+          {
+            itemName: '连锁调入',
+            qty: '0',
+            cost: '00.00',
+            price: ''
+          },
+          {
+            itemName: '库存导入',
+            qty: '0',
+            cost: '00.00',
+            price: ''
+          },
+          {
+            itemName: '总入',
+            qty: '28',
+            cost: '189.50',
+            price: '30.00'
+          }
+        ]
+        this.chartInit()
+      }, 600)
+    },
+    chartInit () {
+      this.loadData.map((item, index) => {
+        if (index != this.loadData.length - 1) {
+          this.pieData.push({
+            name: item.itemName + '总成本',
+            value: item.cost
+          })
+        }
+      })
+      const con5 = document.getElementById('con1')
+      const chart1 = echarts.init(con1)
+      const option = this.initPie(chart1, '', this.pieData)
+      chart1.setOption(option)
+      this.chart1 = chart1
+      this.chart1.resize()
+    },
+    //  重置
+    resetSearchForm () {
+      this.$refs.rangeDate.resetDate()
+      this.handelSearch()
+    }
+  },
+  mounted () {
+    window.onresize = () => {
+      return (() => {
+        this.chart1.resize()
+      })()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.handelSearch()
+    })
+  }
+}
+</script>
+<style lang="less">
+  .chart-box{
+    height: 400px;
+    .chart-hj{
+      font-size: 22px;
+      text-align: center;
+    }
+    .chart{
+      width: 100%;
+      height: 450px;
+      overflow: auto;
+    }
+  }
 </style>