Explorar o código

Merge branch 'deploy_baobiao' of chelingzhu-web/zxyj-admin-html into master

liwenwen %!s(int64=4) %!d(string=hai) anos
pai
achega
8a6decdccd

+ 26 - 0
package-lock.json

@@ -4195,6 +4195,11 @@
         }
       }
     },
+    "comutils": {
+      "version": "1.1.19",
+      "resolved": "https://registry.npmjs.org/comutils/-/comutils-1.1.19.tgz",
+      "integrity": "sha512-JxXB67juILiwhdLwOsYyjUqwWEhHdObI0EClOPk+JDtEuTbac59s0pxGpfCBnNNQ5JommifmcMGneW/4Cg7YWw=="
+    },
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -5698,6 +5703,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",
@@ -16772,6 +16785,14 @@
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.3.tgz",
       "integrity": "sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ=="
     },
+    "vue-seamless-scroll": {
+      "version": "1.1.23",
+      "resolved": "https://registry.npmjs.org/vue-seamless-scroll/-/vue-seamless-scroll-1.1.23.tgz",
+      "integrity": "sha512-HBjUub8WwsKJzbFCrwKPDrZn4e+SSbkKgwWtjKtfLwesiFGwSsVxP44/Z6d3kpXy94qIFOiflJH6l0/9pj7SGA==",
+      "requires": {
+        "comutils": "^1.1.9"
+      }
+    },
     "vue-style-loader": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
@@ -17562,6 +17583,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=="
     }
   }
 }

+ 2 - 0
package.json

@@ -19,6 +19,7 @@
     "axios": "^0.19.0",
     "china-division": "^2.3.1",
     "core-js": "2.6.9",
+    "echarts": "^4.9.0",
     "enquire.js": "^2.1.6",
     "lodash.get": "^4.4.2",
     "lodash.pick": "^4.4.0",
@@ -36,6 +37,7 @@
     "vue-ls": "^3.2.1",
     "vue-quill-editor": "^3.0.6",
     "vue-router": "^3.1.2",
+    "vue-seamless-scroll": "^1.1.23",
     "vue-svg-component-runtime": "^1.0.1",
     "vuex": "^3.1.1",
     "wangeditor": "^3.1.1"

+ 1 - 0
public/index.html

@@ -28,5 +28,6 @@
     <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
     <% } %>
     <!-- built files will be auto injected -->
+    <script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&key=EZHBZ-FWHWJ-MF4FO-K6DJF-4YNGQ-PJBYF"></script>
   </body>
 </html>

BIN=BIN
public/mark.png


+ 3 - 2
src/App.vue

@@ -5,7 +5,6 @@
     </div>
   </a-config-provider>
 </template>
-
 <script>
 import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
 import { AppDeviceEnquire } from '@/utils/mixin'
@@ -20,7 +19,9 @@ export default {
   created () {
     // 在页面加载时读取sessionStorage
     if (sessionStorage.getItem('store')) {
-      this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
+      const data = JSON.parse(sessionStorage.getItem('store'))
+      sessionStorage.setItem('Access-Token', data.user.token)
+      this.$store.replaceState(Object.assign({}, this.$store.state, data))
     }
     // 在页面刷新时将store保存到sessionStorage里
     window.addEventListener('beforeunload', () => {

+ 10 - 0
src/api/luckyDraw.js

@@ -69,3 +69,13 @@ export const getLuckyDrawWinDetail = (params) => {
     method: 'get'
   })
 }
+
+// 中奖纪录导出
+export const exportLuckyDrawWin = (params) => {
+  return axios.request({
+    url: 'luckyDrawWin/exportLuckyDrawWin',
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}

+ 65 - 0
src/api/nodeMap.js

@@ -0,0 +1,65 @@
+import { axios } from '@/utils/request'
+
+// 获取用户总数
+export const getUserTotal = params => {
+  const url = `/customer/getCustomerTotal`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取设备总数
+export const getDeviceTotal = params => {
+  const url = `/device/getDeviceTotal`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取兑换亭总数
+export const getOfficialPartnerTotal = params => {
+  const url = `/seller/getOfficialPartnerTotal`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取兑换机总数
+export const getExchangeMachineTotal = params => {
+  const url = `/exchangeMachine/getExchangeMachineTotal`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取最新投递轮播
+export const getDeliveryCarousel = params => {
+  const url = `/deliveryLog/getDeliveryCarousel`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取最新用户轮播
+export const getCustomerCarousel = params => {
+  const url = `/customer/getCustomerCarousel`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}
+// 获取地图网点(含经纬度)
+export const getMapStation = params => {
+  const url = `/station/queryList`
+  return axios({
+    url: url,
+    data: params,
+    method: 'POST'
+  })
+}

+ 64 - 0
src/api/reportForm.js

@@ -0,0 +1,64 @@
+import { axios } from '@/utils/request'
+
+// 垃圾投递分时查询表
+// 列表查询
+export const getPutInTimeList = (params) => {
+  const url = `putInTimeShareing/query/${params.pageNo}/${params.pageSize}`
+  delete params.pageNo
+  delete params.pageSize
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+// 导出
+export const exportTnTimeForm = (params) => {
+  return axios.request({
+    url: 'putInTimeShareing/exportPutInTimeShareing',
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}
+// 合计
+export const getDetailTotal = (params) => {
+  const url = `putInTimeShareing/sumAllOfDetail`
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+/**
+ * 垃圾投递汇总表
+ */
+// 列表查询
+export const getSumList = (params) => {
+  const url = `putInTimeShareing/querySum/${params.pageNo}/${params.pageSize}`
+  delete params.pageNo
+  delete params.pageSize
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+// 导出
+export const exportSumForm = (params) => {
+  return axios.request({
+    url: 'putInTimeShareing/exportPutInTimeShareingSum',
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}
+// 合计
+export const getSumTotal = (params) => {
+  const url = `putInTimeShareing/sumAllOfSum`
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}

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

@@ -0,0 +1,207 @@
+<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: 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
+            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 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 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'
+          },
+		  grid: {
+			  left: '3%',
+			  right: '4%',
+			  bottom: '3%',
+			  containLabel: true
+		  }
+        }
+        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 }

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

@@ -0,0 +1,191 @@
+<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
+            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轴滚动条结束位置
+    }
+  },
+  methods: {
+    resize () {
+      this.dom.resize()
+    },
+    pageInit () {
+      const legend = this.seriesData.map(_ => _.name)
+      this.$nextTick(() => {
+        const xAxisData = this.value ? Object.keys(this.value) : this.xAxisData
+        console.log(xAxisData, '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>

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

@@ -0,0 +1,196 @@
+<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
+                }
+              }
+            }
+          ]
+        }
+        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"
+                }
+            }
+        }
+    }
+}

+ 304 - 242
src/config/router.config.js

@@ -46,6 +46,40 @@ export const asyncRouterMap = [{
       icon: 'home'
     }
   },
+  {
+    path: '/map',
+    name: 'map',
+    redirect: '/map',
+    component: PageView,
+    meta: {
+      title: '网点地图',
+      icon: 'global',
+      permission: 'M_map'
+    },
+    hideChildrenInMenu: true,
+    children: [{
+      path: '/map',
+      name: 'map',
+      redirect: '/map/NodeMap',
+      component: RouteView,
+      meta: {
+        title: '网点地图',
+        icon: 'global',
+        permission: 'M_map'
+      },
+      hideChildrenInMenu: true,
+      children: [{
+        path: '/map/NodeMap',
+        name: 'NodeMap',
+        component: () => import(/* webpackChunkName: "map" */ '@/views/map/NodeMap.vue'),
+        meta: {
+          title: '网点地图',
+          icon: 'global',
+          hidden: true
+        }
+      }]
+    }]
+  },
   {
     path: '/shop',
     redirect: '/shop/goods',
@@ -55,73 +89,72 @@ export const asyncRouterMap = [{
       icon: 'shop',
       permission: 'M_shop'
     },
-    children: [
-      {
-        path: '/shop/goods',
-        redirect: '/shop/goods/list',
-        name: 'goodsList',
-        component: RouteView,
+    children: [{
+      path: '/shop/goods',
+      redirect: '/shop/goods/list',
+      name: 'goodsList',
+      component: RouteView,
+      meta: {
+        title: '商品管理',
+        icon: 'shopping',
+        permission: 'M_goodsManage_list'
+      },
+      hideChildrenInMenu: true,
+      children: [{
+        path: '/shop/goods/list',
+        name: 'goodsListList',
+        component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsList.vue'),
         meta: {
-          title: '商品管理',
+          title: '商品列表',
           icon: 'shopping',
+          hidden: true,
           permission: 'M_goodsManage_list'
-        },
-        hideChildrenInMenu: true,
-        children: [{
-          path: '/shop/goods/list',
-          name: 'goodsListList',
-          component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsList.vue'),
-          meta: {
-            title: '商品列表',
-            icon: 'shopping',
-            hidden: true,
-            permission: 'M_goodsManage_list'
-          }
-        },
-        {
-          path: '/shop/goods/add',
-          name: 'goodsListAdd',
-          component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsEdit.vue'),
-          meta: {
-            title: '新增商品',
-            icon: 'shopping',
-            hidden: true,
-            permission: 'B_goodsManage_add'
-          }
-        },
-        {
-          path: '/shop/goods/edit/:id',
-          name: 'goodsListEdit',
-          component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsEdit.vue'),
-          meta: {
-            title: '编辑商品',
-            icon: 'shopping',
-            hidden: true,
-            permission: 'B_goodsManage_edit'
-          }
         }
-        ]
       },
       {
-        path: '/shop/goodsShelves',
-        name: 'goodsShelves',
-        component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsShelves.vue'),
+        path: '/shop/goods/add',
+        name: 'goodsListAdd',
+        component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsEdit.vue'),
         meta: {
-          title: '商品排序',
-          icon: 'flag',
-          permission: 'M_goodsShelves_0'
+          title: '新增商品',
+          icon: 'shopping',
+          hidden: true,
+          permission: 'B_goodsManage_add'
         }
       },
       {
-        path: '/shopSetting/goodsClass',
-        name: 'goodsClass',
-        component: () => import(/* webpackChunkName: "shop" */ '@/views/shopSetting/goodsClass.vue'),
+        path: '/shop/goods/edit/:id',
+        name: 'goodsListEdit',
+        component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsEdit.vue'),
         meta: {
-          title: '商品分类管理',
-          icon: 'folder',
-          permission: 'M_goodsClass_list'
+          title: '编辑商品',
+          icon: 'shopping',
+          hidden: true,
+          permission: 'B_goodsManage_edit'
         }
       }
+      ]
+    },
+    {
+      path: '/shop/goodsShelves',
+      name: 'goodsShelves',
+      component: () => import(/* webpackChunkName: "shop" */ '@/views/shop/goodsShelves.vue'),
+      meta: {
+        title: '商品排序',
+        icon: 'flag',
+        permission: 'M_goodsShelves_0'
+      }
+    },
+    {
+      path: '/shopSetting/goodsClass',
+      name: 'goodsClass',
+      component: () => import(/* webpackChunkName: "shop" */ '@/views/shopSetting/goodsClass.vue'),
+      meta: {
+        title: '商品分类管理',
+        icon: 'folder',
+        permission: 'M_goodsClass_list'
+      }
+    }
     ]
   },
   {
@@ -133,44 +166,41 @@ export const asyncRouterMap = [{
       icon: 'profile',
       permission: 'M_orderManage_list'
     },
-    children: [
+    children: [{
+      path: '/order/orderManage',
+      redirect: '/order/orderManage/list',
+      name: 'orderManage',
+      component: RouteView,
+      meta: {
+        title: '订单管理',
+        icon: 'profile',
+        permission: 'M_orderManage_list'
+      },
+      hideChildrenInMenu: true,
+      children: [{
+        path: '/order/orderManage/list',
+        name: 'orderManageList',
+        component: () => import(/* webpackChunkName: "order" */ '@/views/shop/shopOrder.vue'),
+        meta: {
+          title: '订单列表',
+          icon: 'shopping',
+          hidden: true,
+          permission: 'M_orderManage_list'
+        }
+      },
       {
-        path: '/order/orderManage',
-        redirect: '/order/orderManage/list',
-        name: 'orderManage',
-        component: RouteView,
+        path: '/order/orderManage/detail/:id',
+        name: 'orderManageDetail',
+        component: () => import(/* webpackChunkName: "order" */ '@/views/shop/orderDetail.vue'),
         meta: {
-          title: '订单管理',
+          title: '订单详情',
           icon: 'profile',
-          permission: 'M_orderManage_list'
-        },
-        hideChildrenInMenu: true,
-        children: [
-          {
-            path: '/order/orderManage/list',
-            name: 'orderManageList',
-            component: () => import(/* webpackChunkName: "order" */ '@/views/shop/shopOrder.vue'),
-            meta: {
-              title: '订单列表',
-              icon: 'shopping',
-              hidden: true,
-              permission: 'M_orderManage_list'
-            }
-          },
-          {
-            path: '/order/orderManage/detail/:id',
-            name: 'orderManageDetail',
-            component: () => import(/* webpackChunkName: "order" */ '@/views/shop/orderDetail.vue'),
-            meta: {
-              title: '订单详情',
-              icon: 'profile',
-              hidden: true,
-              permission: 'B_orderManage_detail'
-            }
-          }
-        ]
+          hidden: true,
+          permission: 'B_orderManage_detail'
+        }
       }
-    ]
+      ]
+    }]
   },
   // 用户
   {
@@ -182,63 +212,95 @@ export const asyncRouterMap = [{
       icon: 'user',
       permission: 'M_user'
     },
-    children: [
+    children: [{
+      path: '/userInfo/userManage',
+      redirect: '/userInfo/userManageList/list',
+      name: 'userManageS',
+      component: RouteView,
+      meta: {
+        title: '用户管理',
+        icon: 'profile',
+        permission: 'M_user_userManage_list'
+      },
+      hideChildrenInMenu: true,
+      children: [{
+        path: '/userInfo/userManageList/list',
+        name: 'userManageList',
+        component: () => import(/* webpackChunkName: "userManageList" */ '@/views/userInfo/userManageList.vue'),
+        meta: {
+          title: '用户列表',
+          icon: 'team',
+          hidden: true,
+          permission: 'M_user_userManage_list'
+        }
+      },
       {
-        path: '/userInfo/userManage',
-        redirect: '/userInfo/userManageList/list',
-        name: 'userManageS',
-        component: RouteView,
+        path: '/userInfo/userManageList_user/detail/:id',
+        name: 'userManageDetail',
+        component: () => import(/* webpackChunkName: "userManageDetail" */ '@/views/userInfo/userDetails.vue'),
         meta: {
-          title: '用户管理',
+          title: '用户详情',
           icon: 'profile',
-          permission: 'M_user_userManage_list'
-        },
-        hideChildrenInMenu: true,
-        children: [{
-          path: '/userInfo/userManageList/list',
-          name: 'userManageList',
-          component: () => import(/* webpackChunkName: "userManageList" */ '@/views/userInfo/userManageList.vue'),
-          meta: {
-            title: '用户列表',
-            icon: 'team',
-            hidden: true,
-            permission: 'M_user_userManage_list'
-          }
-        },
-        {
-          path: '/userInfo/userManageList_user/detail/:id',
-          name: 'userManageDetail',
-          component: () => import(/* webpackChunkName: "userManageDetail" */ '@/views/userInfo/userDetails.vue'),
-          meta: {
-            title: '用户详情',
-            icon: 'profile',
-            hidden: true,
-            permission: 'B_user_userManage_userDetails'
-          }
-        },
-        {
-          path: '/userInfo/userManageList_Ld/detail',
-          name: 'ledouDetail',
-          component: () => import(/* webpackChunkName: "LdDetails" */ '@/views/userInfo/LdDetails.vue'),
-          meta: {
-            title: '乐豆明细',
-            icon: 'profile',
-            hidden: true,
-            permission: 'B_user_userManage_LdDetails'
-          }
+          hidden: true,
+          permission: 'B_user_userManage_userDetails'
         }
-        ]
       },
       {
-        path: '/userInfo/releaseRecord/list',
-        name: 'releaseRecordList',
-        component: () => import(/* webpackChunkName: "releaseRecord" */ '@/views/releaseRecord/releaseRecordList.vue'),
+        path: '/userInfo/userManageList_Ld/detail',
+        name: 'ledouDetail',
+        component: () => import(/* webpackChunkName: "LdDetails" */ '@/views/userInfo/LdDetails.vue'),
         meta: {
-          title: '投放记录',
-          icon: 'container',
-          permission: 'M_user_releaseRecord'
+          title: '乐豆明细',
+          icon: 'profile',
+          hidden: true,
+          permission: 'B_user_userManage_LdDetails'
         }
       }
+      ]
+    },
+    {
+      path: '/userInfo/releaseRecord/list',
+      name: 'releaseRecordList',
+      component: () => import(/* webpackChunkName: "releaseRecord" */ '@/views/releaseRecord/releaseRecordList.vue'),
+      meta: {
+        title: '投放记录',
+        icon: 'container',
+        permission: 'M_user_releaseRecord'
+      }
+    }
+    ]
+  },
+  // 报表
+  {
+    path: '/reportForm',
+    redirect: '/reportForm/rubbishDeliverySearchTime',
+    component: PageView,
+    meta: {
+      title: '报表',
+      icon: 'line-chart',
+      permission: 'M_reportForm'
+    },
+    children: [{
+      path: '/reportForm/rubbishDeliverySearchTime',
+      name: 'rubbishDeliverySearchTime',
+      component: () => import(/* webpackChunkName: "reportForm" */
+        '@/views/reportForm/rubbishDeliverySearchTime.vue'),
+      meta: {
+        title: '垃圾投递分时查询表',
+        icon: 'file-done',
+        permission: 'M_rubbishSearchTime_list'
+      }
+    },
+    {
+      path: '/reportForm/rubbishDeliveryTotal',
+      name: 'rubbishDeliveryTotal',
+      component: () => import(/* webpackChunkName: "reportForm" */ '@/views/reportForm/rubbishDeliveryTotal.vue'),
+      meta: {
+        title: '垃圾投递汇总表',
+        icon: 'snippets',
+        permission: 'M_rubbishTotal_list'
+      }
+    }
     ]
   },
   {
@@ -250,27 +312,26 @@ export const asyncRouterMap = [{
       icon: 'gift',
       permission: 'M_market'
     },
-    children: [
-      {
-        path: '/market/winingRecord',
-        name: 'winingRecord',
-        component: () => import(/* webpackChunkName: "market" */ '@/views/market/winingRecord.vue'),
-        meta: {
-          title: '中奖记录',
-          icon: 'file-done',
-          permission: 'M_winingRecord'
-        }
-      },
-      {
-        path: '/market/lotterySettings',
-        name: 'lotterySettings',
-        component: () => import(/* webpackChunkName: "market" */ '@/views/market/lotterySettings.vue'),
-        meta: {
-          title: '大转盘设置',
-          icon: 'edit',
-          permission: 'M_lottery_list'
-        }
+    children: [{
+      path: '/market/winingRecord',
+      name: 'winingRecord',
+      component: () => import(/* webpackChunkName: "market" */ '@/views/market/winingRecord.vue'),
+      meta: {
+        title: '中奖记录',
+        icon: 'file-done',
+        permission: 'M_winingRecord'
       }
+    },
+    {
+      path: '/market/lotterySettings',
+      name: 'lotterySettings',
+      component: () => import(/* webpackChunkName: "market" */ '@/views/market/lotterySettings.vue'),
+      meta: {
+        title: '大转盘设置',
+        icon: 'edit',
+        permission: 'M_lottery_list'
+      }
+    }
     ]
   },
   {
@@ -282,18 +343,16 @@ export const asyncRouterMap = [{
       icon: 'appstore',
       permission: 'M_AdBanner_list'
     },
-    children: [
-      {
-        path: '/shopSetting/bannerSetting',
-        name: 'bannerSetting',
-        component: () => import(/* webpackChunkName: "shopSetting" */ '@/views/shop/bannerSetting.vue'),
-        meta: {
-          title: '推广位设置',
-          icon: 'picture',
-          permission: 'M_AdBanner_list'
-        }
+    children: [{
+      path: '/shopSetting/bannerSetting',
+      name: 'bannerSetting',
+      component: () => import(/* webpackChunkName: "shopSetting" */ '@/views/shop/bannerSetting.vue'),
+      meta: {
+        title: '推广位设置',
+        icon: 'picture',
+        permission: 'M_AdBanner_list'
       }
-    ]
+    }]
   },
   {
     path: '/equipmentManage',
@@ -307,7 +366,8 @@ export const asyncRouterMap = [{
     children: [{
       path: '/equipmentManage/network',
       name: 'network',
-      component: () => import(/* webpackChunkName: "equipmentManage" */ '@/views/equipmentManage/network/network.vue'),
+      component: () => import(/* webpackChunkName: "equipmentManage" */
+        '@/views/equipmentManage/network/network.vue'),
       meta: {
         title: '网点管理',
         icon: 'cluster',
@@ -317,7 +377,8 @@ export const asyncRouterMap = [{
     {
       path: '/equipmentManage/equipment',
       name: 'equipment',
-      component: () => import(/* webpackChunkName: "equipmentManage" */ '@/views/equipmentManage/equipment/equipment.vue'),
+      component: () => import(/* webpackChunkName: "equipmentManage" */
+        '@/views/equipmentManage/equipment/equipment.vue'),
       meta: {
         title: '设备管理',
         icon: 'usb',
@@ -327,7 +388,8 @@ export const asyncRouterMap = [{
     {
       path: '/equipmentManage/boxSetting',
       name: 'boxSetting',
-      component: () => import(/* webpackChunkName: "equipmentManage" */ '@/views/equipmentManage/boxSetting/boxSetting.vue'),
+      component: () => import(/* webpackChunkName: "equipmentManage" */
+        '@/views/equipmentManage/boxSetting/boxSetting.vue'),
       meta: {
         title: '箱体类型设置',
         icon: 'block',
@@ -348,7 +410,8 @@ export const asyncRouterMap = [{
     {
       path: '/equipmentManage/openTimeSetting',
       name: 'openTimeSetting',
-      component: () => import(/* webpackChunkName: "equipmentManage" */ '@/views/equipmentManage/openTimeSetting/OpenTimeSetting.vue'),
+      component: () => import(/* webpackChunkName: "equipmentManage" */
+        '@/views/equipmentManage/openTimeSetting/OpenTimeSetting.vue'),
       meta: {
         title: '投放时间设置',
         icon: 'dashboard',
@@ -366,37 +429,39 @@ export const asyncRouterMap = [{
       icon: 'bank',
       permission: 'M_businessManage'
     },
-    children: [
-      {
-        path: '/businessManage/partnerManage',
-        name: 'partnerManage',
-        component: () => import(/* webpackChunkName: "businessManage" */ '@/views/businessManage/partnerManage/partnerManage.vue'),
-        meta: {
-          title: '商户管理',
-          icon: 'solution',
-          permission: 'M_partnerManage_list'
-        }
-      },
-      {
-        path: '/businessManage/userManage',
-        name: 'userManage',
-        component: () => import(/* webpackChunkName: "businessManage" */ '@/views/businessManage/userManage/userManage.vue'),
-        meta: {
-          title: '用户管理',
-          icon: 'folder',
-          permission: 'M_userManage_list'
-        }
-      },
-      {
-        path: '/businessManage/leduQuery',
-        name: 'leduQuery',
-        component: () => import(/* webpackChunkName: "businessManage" */ '@/views/businessManage/leduQuery/leduQuery.vue'),
-        meta: {
-          title: '商户乐豆统计',
-          icon: 'search',
-          permission: 'M_ledouTjList'
-        }
+    children: [{
+      path: '/businessManage/partnerManage',
+      name: 'partnerManage',
+      component: () => import(/* webpackChunkName: "businessManage" */
+        '@/views/businessManage/partnerManage/partnerManage.vue'),
+      meta: {
+        title: '商户管理',
+        icon: 'solution',
+        permission: 'M_partnerManage_list'
+      }
+    },
+    {
+      path: '/businessManage/userManage',
+      name: 'userManage',
+      component: () => import(/* webpackChunkName: "businessManage" */
+        '@/views/businessManage/userManage/userManage.vue'),
+      meta: {
+        title: '用户管理',
+        icon: 'folder',
+        permission: 'M_userManage_list'
+      }
+    },
+    {
+      path: '/businessManage/leduQuery',
+      name: 'leduQuery',
+      component: () => import(/* webpackChunkName: "businessManage" */
+        '@/views/businessManage/leduQuery/leduQuery.vue'),
+      meta: {
+        title: '商户乐豆统计',
+        icon: 'search',
+        permission: 'M_ledouTjList'
       }
+    }
     ]
   },
   {
@@ -408,18 +473,16 @@ export const asyncRouterMap = [{
       icon: 'user-add',
       permission: 'M_supplier_list'
     },
-    children: [
-      {
-        path: '/supplier/list',
-        name: 'supplier',
-        component: () => import(/* webpackChunkName: "supplier" */ '@/views/shopSetting/supplier.vue'),
-        meta: {
-          title: '供货商管理',
-          icon: 'solution',
-          permission: 'M_supplier_list'
-        }
+    children: [{
+      path: '/supplier/list',
+      name: 'supplier',
+      component: () => import(/* webpackChunkName: "supplier" */ '@/views/shopSetting/supplier.vue'),
+      meta: {
+        title: '供货商管理',
+        icon: 'solution',
+        permission: 'M_supplier_list'
       }
-    ]
+    }]
   },
   // auth
   {
@@ -431,37 +494,36 @@ export const asyncRouterMap = [{
       icon: 'lock',
       permission: 'M_auth_0'
     },
-    children: [
-      {
-        path: '/auth/userList',
-        name: 'powerUserList',
-        component: () => import(/* webpackChunkName: "auth" */ '@/views/power/user/userList.vue'),
-        meta: {
-          title: '用户管理',
-          icon: 'user',
-          permission: 'M_power_user_list'
-        }
-      },
-      {
-        path: '/auth/roleList',
-        name: 'powerRoleList',
-        component: () => import(/* webpackChunkName: "auth" */ '@/views/power/role/roleList.vue'),
-        meta: {
-          title: '角色管理',
-          icon: 'solution',
-          permission: 'M_power_role_list'
-        }
-      },
-      {
-        path: '/menusAuth/menu',
-        name: 'powerMenu',
-        component: () => import(/* webpackChunkName: "auth" */ '@/views/power/menu/menu.vue'),
-        meta: {
-          title: '菜单管理',
-          icon: 'profile',
-          permission: 'M_power_menu_list'
-        }
+    children: [{
+      path: '/auth/userList',
+      name: 'powerUserList',
+      component: () => import(/* webpackChunkName: "auth" */ '@/views/power/user/userList.vue'),
+      meta: {
+        title: '用户管理',
+        icon: 'user',
+        permission: 'M_power_user_list'
       }
+    },
+    {
+      path: '/auth/roleList',
+      name: 'powerRoleList',
+      component: () => import(/* webpackChunkName: "auth" */ '@/views/power/role/roleList.vue'),
+      meta: {
+        title: '角色管理',
+        icon: 'solution',
+        permission: 'M_power_role_list'
+      }
+    },
+    {
+      path: '/menusAuth/menu',
+      name: 'powerMenu',
+      component: () => import(/* webpackChunkName: "auth" */ '@/views/power/menu/menu.vue'),
+      meta: {
+        title: '菜单管理',
+        icon: 'profile',
+        permission: 'M_power_menu_list'
+      }
+    }
     ]
   },
   {

+ 1 - 1
src/core/directives/hasPermission.js

@@ -16,7 +16,7 @@ import store from '@/store'
  */
 const hasPermission = Vue.directive('hasPermission', {
   inserted: function (el, binding, vnode) {
-    console.log(binding.value)
+    // console.log(binding.value)
     const roles = store.getters.roles
     const permissionList = roles.permissionList
     if (!permissionList.includes(binding.value)) {

+ 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
+}

+ 3 - 1
src/main.js

@@ -18,7 +18,9 @@ import './permission' // permission control
 import './utils/filter'
 
 import VueCookies from 'vue-cookies' // global filter
-
+// 无缝滚动
+import scroll from 'vue-seamless-scroll'
+Vue.use(scroll)
 Vue.config.productionTip = false
 
 // mount axios Vue.$http and this.$http

+ 2 - 2
src/permission.js

@@ -17,8 +17,8 @@ router.beforeEach((to, from, next) => {
   NProgress.start() // start progress bar
   to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
   let token = store.state.user.token || sessionStorage.getItem(ACCESS_TOKEN)
-  token = token != 'undefined' ? token : ''
-  // console.log(token)
+  token = token != 'null' && token != 'undefined' && token ? token : ''
+  // console.log(token,'token')
   // 已登录
   if (token) {
     /* has token */

+ 566 - 0
src/views/map/NodeMap.vue

@@ -0,0 +1,566 @@
+<template>
+  <div class="nodeMap-contenter">
+    <!-- 地图 -->
+    <div class="map-cont">
+      <a-row class="map-title" :getter="24">
+        <span>智享亿家生活垃圾分类网点地图</span>
+        <a-icon @click="isFullscreenMap" :style="{fontSize:'30px',padding: '7px',float: 'right', cursor: 'pointer'}" :type="isMapFull ?'fullscreen-exit':'fullscreen'" />
+      </a-row>
+      <!-- 地图容器 -->
+      <div id="container" :style="{height: mapHeight}">
+      </div>
+      <div class="map-footer">
+        <a-row :getter="[24,24]">
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <div class="map-data">
+              <a-icon type="team" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
+              <span>&nbsp;&nbsp;用户总数 {{ userTotal }} 个</span>
+            </div>
+          </a-col>
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <div class="map-data">
+              <a-icon type="rest" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
+              <span>&nbsp;&nbsp;智能收集箱 {{ deviceTotal }} 台</span>
+            </div>
+          </a-col>
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <div class="map-data">
+              <a-icon type="shop" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
+              <span>&nbsp;&nbsp;智能兑换亭 {{ sellerTotal }} 台</span>
+            </div>
+          </a-col>
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <div class="map-data">
+              <a-icon type="hdd" :style="{color:'#83ace1',fontSize:'20px',padding:'5px',backgroundColor:'#FFF',borderRadius:'8px'}" />
+              <span>&nbsp;&nbsp;智能兑换机 {{ machineTotal }} 台</span>
+            </div>
+          </a-col>
+        </a-row>
+      </div>
+    </div>
+
+    <div v-show="!isMapFull" class="data-cont">
+      <!-- 实时轮播数据 -->
+      <a-row :gutter="[24,24]">
+        <a-col
+          :xs="24"
+          :sm="24"
+          :md="24"
+          :lg="24"
+          :xl="24"
+          :xxl="16">
+          <div class="switper-cont">
+            <div class="data-title">实时投递数据</div>
+            <div class="chart-box data-form">
+              <div class="swipter-title">
+                <a-row :gutter="0">
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="6">投递时间</a-col>
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="4">投递用户</a-col>
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="6">投递网点</a-col>
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="5">投递分类</a-col>
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="3">投递重量(g)</a-col>
+                </a-row>
+              </div>
+              <vue-seamless-scroll :data="deliveryLogData" :class-option="classOption" class="seamless-warp">
+                <a-spin class="loading" :spinning="deliverySpinning" tip="Loading..." />
+                <div class="swipter-data" v-for="item in deliveryLogData" :key="item.id">
+                  <a-row :gutter="0">
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="6">{{ item.deliveryTime }}</a-col>
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="4">{{ item.customerMobile }}</a-col>
+                    <a-col
+                      :title="item.stationName"
+                      style="padding: 0px 2px;text-align: center;white-space: nowrap;
+				  				  					text-overflow: ellipsis;
+				  				  					overflow: hidden;
+				  				  					word-break: break-all;"
+                      :span="6">
+                      {{ item.stationName }}
+                    </a-col>
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="5">{{ item.rubbishTypeDictValue }}</a-col>
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="3">{{ item.rubbishWeight }}</a-col>
+                  </a-row>
+                </div>
+              </vue-seamless-scroll>
+            </div>
+          </div>
+        </a-col>
+        <a-col
+          :xs="24"
+          :sm="24"
+          :md="24"
+          :lg="24"
+          :xl="24"
+          :xxl="8">
+          <div class="switper-cont">
+            <div class="data-title">实时新增用户</div>
+            <div class="chart-box data-form">
+              <div class="swipter-title">
+                <a-row :gutter="0">
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="16">注册时间</a-col>
+                  <a-col style="padding: 0px 2px;text-align: center;" :span="8">用户手机</a-col>
+                </a-row>
+              </div>
+              <vue-seamless-scroll :data="userLogData" :class-option="classOption" class="seamless-warp">
+                <a-spin class="loading" :spinning="userLogSpinning" tip="Loading..." />
+                <div class="swipter-data" v-for="item in userLogData" :key="item.id">
+                  <a-row :gutter="0">
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="16">{{ item.registerTime }}</a-col>
+                    <a-col style="padding: 0px 2px;text-align: center;" :span="8">{{ item.mobile }}</a-col>
+                  </a-row>
+                </div>
+              </vue-seamless-scroll>
+            </div>
+            </vue-seamless-scroll>
+          </div>
+        </a-col>
+      </a-row>
+      <!-- 图表数据 -->
+      <a-row :gutter="[24,24]" type="flex" justify="space-between">
+        <a-col
+          :xs="24"
+          :sm="20"
+          :md="12"
+          :lg="12"
+          :xl="12"
+          :xxl="8">
+          <div class="chart-cont">
+            <div class="data-title">设备分布统计图</div>
+            <chart-pie
+              ref="chartPie"
+              class="chart-box"
+              :value="equipmentPieData"
+              title=""
+              unit="%"
+              :color="equipmentPieColor"
+              :xAxisRotate="resize"
+              text="" />
+          </div>
+        </a-col>
+        <a-col
+          xs="24"
+          :sm="20"
+          :md="12"
+          :lg="12"
+          :xl="12"
+          :xxl="8">
+          <div class="chart-cont">
+            <div class="data-title">设备数量统计对比图</div>
+            <chart-bar
+              ref="ChartBar"
+              class="chart-box"
+              color="#0000fe"
+              :yMax="1000"
+              yUnit="台"
+              :value="equipmentBarData"
+              :seriesData="equipmentBarSeriesData"
+              text="" />
+          </div>
+        </a-col>
+        <a-col
+          xs="24"
+          :sm="20"
+          :md="16"
+          :lg="12"
+          :xl="12"
+          :xxl="8">
+          <div class="chart-cont">
+            <div class="data-title">项目介绍</div>
+            <div class="chart-box textCount">
+              全市共有党政机关、企事业单位827个,职工人数16655人。其中,市级单位334个,秦都区297个,渭城区196个;居民小区1010个(两区居民小区994个,保障房小区16个),
+              物业管理小区770个(两区754个,保障房小区16个)。其中,市本级保障房小区12个,物业管理小区12个。秦都区654个(含保障房小区3个),物业管理小区584个。渭城区
+              344个(含保障房小区1个),物业管理174个(含保障房小区1个);学校(中小学、幼儿园)200所,其中,秦都区139所(中小学53所、幼儿园86所)。渭城区61所(中小
+              学27所、幼儿园34所)。截止目前,城区已建成试点单位67个, 设置智能分类箱
+              <span class="blue">{{ deviceTotal }}</span>组, 积分兑换亭<span class="blue">{{ sellerTotal }}</span>个, 无人兑换机<span
+                class="blue">{{ machineTotal }}</span>个, 覆盖28处办公场所的177个机关事业单位,居民小区29个,企业2个,学校8个。 软件注册人数<span class="blue">{{ userTotal }}</span>人,
+              周回收有害垃圾5.646公斤、 可回收物9.26吨, 日处理厨余垃圾106吨。
+            </div>
+          </div>
+        </a-col>
+      </a-row>
+    </div>
+
+  </div>
+  </div>
+</template>
+<script>
+import {
+  ChartPie,
+  ChartBar
+} from '@/components/Echarts'
+import vueSeamlessScroll from 'vue-seamless-scroll'
+import {
+  getUserTotal,
+  getDeviceTotal,
+  getOfficialPartnerTotal,
+  getDeliveryCarousel,
+  getCustomerCarousel,
+  getExchangeMachineTotal,
+  getMapStation
+} from '@/api/nodeMap.js'
+export default {
+  name: 'NodeMap',
+  components: {
+    ChartPie,
+    ChartBar,
+    vueSeamlessScroll
+  },
+  data () {
+    return {
+      map: null, // 地图对象
+      markerLayer: null, // 地图点标记对象
+      infoWindow: null, // 地图信息窗口对象
+      isMapFull: false, // 地图是否全屏
+      userTotal: 0, // 用户总数
+      deviceTotal: 0, // 设备总数
+      sellerTotal: 0, // 兑换亭总数
+      machineTotal: 0, // 兑换机总数
+      deliverySpinning: true, // 实时投递数据loading
+      userLogSpinning: true, // 实时用户数据loading
+      deliveryLogData: [], // 实时投递数据
+      userLogData: [], // 实时用户数据
+      screenWidth: document.body.clientWidth, // 这里是给到了一个默认值
+      timer: false,
+      // 设备分布统计图
+      equipmentPieData: [{
+        name: '学校',
+        value: '14'
+      },
+      {
+        name: '居民小区',
+        value: '49'
+      },
+      {
+        name: '公共机构',
+        value: '32'
+      },
+      {
+        name: '企业',
+        value: '5'
+      }
+      ],
+      equipmentPieColor: ['#ffaa00', '#00ff00', '#00aaff', '#ff55ff', '#1dc5d4', '#8465c7', '#00ffff'],
+      // 设备数量统计对比图数据
+      equipmentBarData: [{
+        name: '智能分类箱',
+        value: 0
+      },
+      {
+        name: '积分兑换亭',
+        value: 0
+      },
+      {
+        name: '无人兑换机',
+        value: 0
+      }
+      ],
+      equipmentBarSeriesData: ['0', '0', '0']
+    }
+  },
+  computed: {
+    resize () {
+      console.log(this.screenWidth, 'this.screenWidth')
+      if (this.screenWidth < 1640) {
+        return 40
+      } else {
+        return 0
+      }
+    },
+    mapHeight () {
+      return this.isMapFull ? '80vh' : '30vh'
+    },
+    classOption () {
+      return {
+        step: 0.4, // 数值越大速度滚动越快
+        limitMoveNum: 2, // 开始无缝滚动的数据量 this.dataList.length
+        hoverStop: true, // 是否开启鼠标悬停stop
+        direction: 1, // 0向下 1向上 2向左 3向右
+        openWatch: true, // 开启数据实时监控刷新dom
+        singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
+        singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
+        waitTime: 1000 // 单步运动停止的时间(默认值1000ms)
+      }
+    }
+  },
+  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)
+      }
+    }
+  },
+  methods: {
+    // 初始化地图
+    initMap () {
+      // 定义地图中心点坐标 默认定位在陕西省咸阳市
+      var center = new TMap.LatLng(34.329828, 108.708344)
+      // 定义map变量,调用 TMap.Map() 构造函数创建地图
+      this.map = new TMap.Map(document.getElementById('container'), {
+        center: center, // 设置地图中心点坐标
+        zoom: 17.2, // 设置地图缩放级别
+        pitch: 43.5, // 设置俯仰角
+        rotation: 45 // 设置地图旋转角度
+      })
+      // 创建并初始化MultiMarker 点标记
+      this.markerLayer = new TMap.MultiMarker({
+        map: this.map, // 指定地图容器
+        // 样式定义
+        styles: {
+          // 创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
+          'myStyle': new TMap.MarkerStyle({
+            'width': 25, // 点标记样式宽度(像素)
+            'height': 25, // 点标记样式高度(像素)
+            'src': '../mark.png', // 图片路径
+            // 焦点在图片中的像素位置,一般大头针类似形式的图片以针尖位置做为焦点,圆形点以圆心位置为焦点
+            'anchor': {
+              x: 16,
+              y: 32
+            }
+          })
+        },
+        // 点标记数据数组
+        geometries: []
+      })
+      // 初始化infoWindow
+      this.infoWindow = new TMap.InfoWindow({
+        map: this.map,
+        position: new TMap.LatLng(39.984104, 116.307503),
+        offset: {
+          x: 0,
+          y: -32
+        } // 设置信息窗相对position偏移像素
+      })
+      this.infoWindow.close() // 初始关闭信息窗关闭
+      this.markerLayer.on('mousemove', this.showMarkTitle)
+      // 监听地图加载完成
+      this.map.on('tilesloaded', this.getMapMark)
+    },
+    // 获取地图点坐标
+    getMapMark () {
+      getMapStation({}).then(res => {
+        const list = res.data || []
+        const data = []
+        list.map((item, index) => {
+          if (item.lat <= 85.051128 && item.lat >= -85.051128) {
+            const p = {
+              'id': item.id, // 点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
+              'styleId': 'myStyle', // 指定样式id
+              'position': new TMap.LatLng(Number(item.lat), Number(item.lng)), // 点标记坐标位置
+              'properties': { // 自定义属性
+                'title': item.name
+              }
+            }
+            data.push(p)
+          }
+        })
+        // 增加点标记
+        this.markerLayer.add(data)
+      })
+    },
+    // 显示地图标记点名称
+    showMarkTitle (evt) {
+      this.infoWindow.open() // 打开信息窗
+      this.infoWindow.setPosition(evt.geometry.position) // 设置信息窗位置
+      this.infoWindow.setContent(evt.geometry.properties.title) // 设置信息窗内容
+    },
+    // 获取用户总数
+    getUser () {
+      getUserTotal({}).then(res => {
+        this.userTotal = res.data || 0
+      })
+    },
+    // 获取设备总数
+    getDevice () {
+      getDeviceTotal({}).then(res => {
+        this.deviceTotal = res.data || 0
+        this.equipmentBarData[0].value = this.deviceTotal
+        console.log(this.equipmentBarData, 'this.equipmentBarData')
+      })
+    },
+    // 获取兑换亭总数
+    getSeller () {
+      getOfficialPartnerTotal({}).then(res => {
+        this.sellerTotal = res.data || 0
+        this.equipmentBarData[1].value = this.sellerTotal
+      })
+    },
+    // 获取兑换机总数
+    getMachine () {
+      getExchangeMachineTotal({}).then(res => {
+        this.machineTotal = res.data || 0
+        this.equipmentBarData[2].value = this.machineTotal
+      })
+    },
+    // 获取实时投递数据
+    getDeliveryLog () {
+      getDeliveryCarousel({}).then(res => {
+        this.deliveryLogData = res.data.list || []
+      })
+    },
+    // 获取实时用户数据
+    getUserLog () {
+      getCustomerCarousel({}).then(res => {
+        this.userLogData = res.data.list || []
+      })
+    },
+    // 地图是否全屏展示
+    isFullscreenMap () {
+      this.isMapFull = !this.isMapFull
+    }
+  },
+  mounted () {
+    this.initMap()
+    const that = this
+    window.onresize = () => {
+      return (() => {
+        window.screenWidth = document.body.clientWidth
+        that.screenWidth = window.screenWidth
+      })()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.deliverySpinning = true
+      vm.userLogSpinning = true
+      setTimeout(() => {
+        vm.deliverySpinning = false
+        vm.userLogSpinning = false
+      }, 2000)
+      vm.getUser()
+      vm.getDevice()
+      vm.getSeller()
+      vm.getMachine()
+      vm.getDeliveryLog()
+      vm.getUserLog()
+      // 5分钟轮播
+      setInterval(() => vm.getDeliveryLog(), 1000 * 60 * 5)
+      setInterval(() => vm.getUserLog(), 1000 * 60 * 5)
+    })
+  },
+  beforeRouterLeave (to, from, next) {
+	  next(vm => {
+		  // 销毁地图
+		  vm.map.destroy()
+	  })
+  }
+}
+</script>
+
+<style lang="less" scoped="scoped">
+	.nodeMap-contenter {
+		width: 100%;
+
+		.map-cont {
+			width: 100%;
+			margin-bottom: 20px;
+
+			.map-title {
+				background-color: #5c9eff;
+				height: 45px;
+				line-height: 45px;
+				text-align: center;
+				color: #fff;
+				font-size: 20px;
+			}
+
+			.map-footer {
+				background-color: #5c9eff;
+				padding: 5px;
+				color: #fff;
+				font-size: 16px;
+
+				.map-data {
+					padding: 3px;
+				}
+			}
+		}
+
+		.data-cont {
+			width: 100%;
+		}
+
+		.data-form {
+			text-align: center;
+			width: 100%;
+			border: 1px solid #d6d6d6;
+			border-radius: 10px;
+			padding: 10px;
+			overflow: hidden;
+			display: flex;
+			flex-direction: column;
+
+			.seamless-warp {
+				flex: 1;
+				overflow: hidden;
+			}
+
+			.swipter-data {
+				height: 40px;
+				line-height: 40px;
+			}
+
+			.loading {
+				margin-top: 50px;
+			}
+
+			.network-name {
+				white-space: nowrap;
+				text-overflow: ellipsis;
+				overflow: hidden;
+				word-break: break-all;
+			}
+		}
+
+		.switper-cont {
+			height: 300px;
+			padding: 20px;
+			background-color: #fff;
+			border-radius: 10px;
+			box-shadow: 5px 5px 8px #888888;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.data-title {
+			font-size: 16px;
+		}
+
+		.data-title:before {
+			content: "";
+			border: 2px solid #5c9eff;
+			margin-right: 20px;
+		}
+
+		.chart-box {
+			flex: 1;
+			width: 100%;
+			margin-top: 10px;
+		}
+
+		.chart-cont {
+			padding: 20px;
+			height: 400px;
+			background-color: #fff;
+			border-radius: 20px;
+			box-shadow: 5px 5px 8px #888888;
+			display: flex;
+			flex-direction: column;
+
+			.textCount {
+				text-indent: 2em;
+
+				.blue {
+					color: #5c9eff;
+					padding: 0px 2px;
+				}
+			}
+		}
+	}
+</style>

+ 198 - 46
src/views/market/winingRecord.vue

@@ -3,7 +3,7 @@
     <div class="winRecord-searchForm">
       <a-form layout="inline" v-bind="formItemLayout" @keyup.enter.native="$refs.table.refresh(true)">
         <a-row :gutter="20">
-          <a-col :span="6">
+          <a-col :span="5">
             <a-form-item label="中奖时间" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
               <a-range-picker
                 id="winRecord-time"
@@ -37,7 +37,26 @@
           </a-col>
           <a-col :span="4">
             <a-button class="handle-btn serach-btn" id="winRecord-btn-serach" type="primary" @click="$refs.table.refresh(true)">查询</a-button>
-            <a-button class="handle-btn" type="" id="winRecord-btn-reset" @click="handleReset">重置</a-button>
+            <a-button class="handle-btn serach-btn" type="" id="winRecord-btn-reset" @click="handleReset">重置</a-button>
+            <a-button class="export-btn" id="winRecord-btn--export" :loading="loading" v-hasPermission="'B_winingRecord_export'" @click="handleExport">导出</a-button>
+          </a-col>
+        </a-row>
+        <a-row :gutter="20">
+          <a-col :span="5">
+            <a-form-item label="活动名称" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
+              <a-input id="winRecord-activeName" allowClear v-model="queryParam.activeName" placeholder="请输入活动名称" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="5">
+            <a-form-item label="奖品类型" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
+              <v-select
+                v-model="queryParam.prizeType"
+                ref="orderState"
+                id="winRecord-prizeType"
+                code="PRIZE_TYPE"
+                placeholder="请选择奖品类型"
+                allowClear></v-select>
+            </a-form-item>
           </a-col>
         </a-row>
       </a-form>
@@ -80,22 +99,38 @@
 </template>
 
 <script>
-import { STable, VSelect } from '@/components'
+import {
+  STable,
+  VSelect
+} from '@/components'
 import GoodsLogistics from './GoodsLogistics.vue'
 import GoodsLogisticsDetail from './GoodsLogisticsDetail.vue'
 import getDate from '@/libs/getDate.js'
 import moment from 'moment'
-import { getLuckyWinList, sendExpress } from '@/api/luckyDraw.js'
+import {
+  getLuckyWinList,
+  sendExpress,
+  exportLuckyDrawWin
+} from '@/api/luckyDraw.js'
 export default {
   name: 'WinRecord',
-  components: { STable, VSelect, GoodsLogistics, GoodsLogisticsDetail },
+  components: {
+    STable,
+    VSelect,
+    GoodsLogistics,
+    GoodsLogisticsDetail
+  },
   data () {
     return {
       openModal: false, //  是否填写物流信息
       openDetailModal: false, //  是否查看物流信息
       formItemLayout: {
-        labelCol: { span: 6 },
-        wrapperCol: { span: 18 }
+        labelCol: {
+          span: 6
+        },
+        wrapperCol: {
+          span: 18
+        }
       },
       // 查询参数
       queryParam: {
@@ -103,20 +138,67 @@ export default {
         endDate: null, // 结束时间
         prizeDesc: '', //  奖品
         customerMobile: '', // 用户
-        state: '' // 状态
+        state: '', // 状态
+        activeName: '', // 活动名称
+        prizeType: '' // 奖品类型
       },
-	  itemId: '', // 发货列id
-	  luckyDrawWinNo: '', // 中奖奖品编码
+      itemId: '', // 发货列id
+      luckyDrawWinNo: '', // 中奖奖品编码
       loading: false, // 导出loading
       // 表头
-      columns: [
-        { title: '序号', dataIndex: 'no', width: 80, align: 'center' },
-        { title: '中奖时间', dataIndex: 'winTime', width: 200, align: 'center' },
-        { title: '活动名称', dataIndex: 'activeName', width: 200, align: 'center' },
-        { title: '奖品', dataIndex: 'prizeDesc', scopedSlots: { customRender: 'prizeDesc' }, width: 200, align: 'center' },
-        { title: '中奖用户', dataIndex: 'customerMobile', width: 200, align: 'center' },
-        { title: '状态', dataIndex: 'stateDictValue', width: 200, align: 'center' },
-        { title: '操作', scopedSlots: { customRender: 'action' }, width: 150, align: 'center' }
+      columns: [{
+        title: '序号',
+        dataIndex: 'no',
+        width: 80,
+        align: 'center'
+      },
+      {
+        title: '中奖时间',
+        dataIndex: 'winTime',
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '活动名称',
+        dataIndex: 'activeName',
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '奖品类型',
+        dataIndex: 'prizeTypeDictValue',
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '奖品',
+        dataIndex: 'prizeDesc',
+        scopedSlots: {
+          customRender: 'prizeDesc'
+        },
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '中奖用户',
+        dataIndex: 'customerMobile',
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '状态',
+        dataIndex: 'stateDictValue',
+        width: 200,
+        align: 'center'
+      },
+      {
+        title: '操作',
+        scopedSlots: {
+          customRender: 'action'
+        },
+        width: 150,
+        align: 'center'
+      }
       ],
       // 加载数据方法 必须为 Promise 对象
       loadData: parameter => {
@@ -170,10 +252,65 @@ export default {
         moment(getDate.getToday().endtime, this.dateFormat)
       ]
       this.queryParam.customerMobile = ''
+      this.queryParam.activeName = ''
+      this.queryParam.prizeType = ''
       this.queryParam.prizeDesc = ''
       this.queryParam.state = ''
       this.$refs.table.refresh(true)
     },
+    // 导出
+    handleExport () {
+      const params = this.queryParam
+      if (this.time && this.time.length) {
+        params.beginDate = moment(this.time[0]).format('YYYY-MM-DD 00:00:00')
+        params.endDate = moment(this.time[1]).format('YYYY-MM-DD 23:59:59')
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
+      }
+      if (!params.beginDate && !params.endDate) {
+        this.$message.error('请先选择需要导出的中奖时间区间再进行导出!')
+        return
+      }
+      // 判断两个时间段是否相差m个月  第二个参数指相差单位,第三个参数指是否返回浮点形式(小数)
+      if (moment(params.endDate).diff(moment(params.beginDate), 'months', true) > 3) {
+        this.$message.error('单次最多只能导出3个月的数据,请缩小查询区间后再进行导出!')
+        return
+      }
+      this.$confirm({
+        title: '提示',
+        content: '导出过程可能需要一段时间,且导出期间不可进行其他操作,确认要导出吗?',
+        onOk: () => {
+          this.loading = true
+          exportLuckyDrawWin(params).then(res => {
+            this.loading = false
+            this.download(res)
+          })
+        },
+        onCancel () {}
+      })
+      // this.loading = true
+      // exportLuckyDrawWin(params).then(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('YYYY-MM-DD')
+      const a = moment(this.queryParam.beginDate).format('LL') + '-' + moment(this.queryParam.endDate).format('LL')
+      console.log(a)
+      const fname = a + '中奖记录表'
+      link.setAttribute('download', fname + '.xlsx')
+      document.body.appendChild(link)
+      link.click()
+    },
     //  查看物流详情
     handleView (row) {
       this.itemId = row.id
@@ -182,42 +319,57 @@ export default {
     // 发货
     sendGood (row) {
       this.itemId = row.id
-	  this.luckyDrawWinNo = row.luckyDrawWinNo
+      this.luckyDrawWinNo = row.luckyDrawWinNo
       this.openModal = true
     },
     // 提交发货
     handleSubmit (data) {
       console.log(data, 'ppppppp')
-	  const params = data
-	  params.luckyDrawWinNo = this.luckyDrawWinNo
-	  sendExpress(params).then(res => {
-		  if (res.status == 200) {
-			  this.openModal = false
-			  this.$message.success(res.message)
-			  this.$refs.table.refresh(true)
-		  }
-	  })
+      const params = data
+      params.luckyDrawWinNo = this.luckyDrawWinNo
+      sendExpress(params).then(res => {
+        if (res.status == 200) {
+          this.openModal = false
+          this.$message.success(res.message)
+          this.$refs.table.refresh(true)
+        }
+      })
     }
   }
 }
 </script>
 <style lang="less" scoped>
-  .winRecord-table-page-search-wrapper{
-    .winRecord-searchForm{
-      .ant-form-inline .ant-form-item{
-        width: 100%;
-      }
-      // 搜索框的下间距
-      .ant-form-item {
-      	margin-bottom: 10px;
-      }
-      .handle-btn{
-        margin-top: 4px;
-        margin-bottom: 20px;
-      }
-      .serach-btn{
-        margin-right: 10px;
-      }
-    }
-  }
+	.winRecord-table-page-search-wrapper {
+		.winRecord-searchForm {
+			.ant-form-inline .ant-form-item {
+				width: 100%;
+			}
+
+			// 搜索框的下间距
+			.ant-form-item {
+				margin-bottom: 10px;
+			}
+
+			.handle-btn {
+				margin-top: 4px;
+				margin-bottom: 20px;
+			}
+
+			.serach-btn {
+				margin-right: 10px;
+			}
+		}
+
+		.export-btn {
+			background-color: #ff9900;
+			border-color: #ff9900;
+			color: #fff;
+		}
+
+		.export-btn.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),
+		.export-btn.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger) {
+			color: #fff;
+			border-color: #ff9900;
+		}
+	}
 </style>

+ 5 - 3
src/views/releaseRecord/releaseRecordList.vue

@@ -34,7 +34,8 @@
           </a-col>
           <a-col :span="4">
             <a-button class="handle-btn serach-btn" id="releaseRecordList-serach" type="primary" @click="refresh">查询</a-button>
-            <a-button class="handle-btn" type="" id="releaseRecordList-reset" @click="handleReset">重置</a-button>
+            <a-button class="handle-btn serach-btn" type="" id="releaseRecordList-reset" @click="handleReset">重置</a-button>
+            <a-button class="export-btn" id="releaseRecordList-export" v-hasPermission="'B_exportDeliveryLog'" :loading="loading" @click="handleExport">导出</a-button>
           </a-col>
         </a-row>
         <a-row :gutter="20">
@@ -71,9 +72,9 @@
               </div>
             </a-form-item>
           </a-col>
-          <a-col :span="4">
+          <!-- <a-col :span="4">
             <a-button class="export-btn" id="releaseRecordList-export" v-hasPermission="'B_exportDeliveryLog'" :loading="loading" @click="handleExport">导出</a-button>
-          </a-col>
+          </a-col> -->
         </a-row>
       </a-form>
     </div>
@@ -368,6 +369,7 @@ export default {
       }
       .handle-btn{
         margin-bottom: 15px;
+		margin-top: 4px;
       }
       .serach-btn{
         margin-right: 10px;

+ 307 - 0
src/views/reportForm/rubbishDeliverySearchTime.vue

@@ -0,0 +1,307 @@
+<template>
+  <a-card :bordered="false" class="shopOrder-table-page-search-wrapper">
+    <div class="shopOrder-searchForm">
+      <a-form layout="inline" @keyup.enter.native="refresh">
+        <a-row :gutter="20">
+          <a-col :span="8">
+            <a-form-item label="投递时间:" :label-col="{ span: 5 }" :wrapper-col="{ span: 19}">
+              <a-range-picker
+                id="rubbishSearchTime-time"
+                v-model="time"
+                style="width: 100%;"
+                :format="dateFormat"
+                :showTime="{ format: 'HH'}"
+                :placeholder="['开始时间','结束时间']"
+                :disabledDate="disabledDate"
+                @change="onChange" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="6">
+            <a-form-item label="网点名称" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+              <a-select
+                id="rubbishSearchTime-stationNo"
+                placeholder="请选择网点名称"
+                allowClear
+                v-model="queryParam.stationNo"
+                :showSearch="true"
+                option-filter-prop="children"
+                :filter-option="filterOption"
+              >
+                <a-select-option v-for="item in optionData" :key="item.stationNo" :value="item.stationNo">{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <a-col :span="6">
+            <a-button
+              class="serach-btn"
+              type="primary"
+              id="rubbishSearchTime-refresh"
+              @click="refresh">查询</a-button>
+            <a-button class="serach-btn" id="rubbishSearchTime-handleReset" @click="handleReset">重置</a-button>
+            <a-button class="export-btn" id="rubbishSearchTime-export" v-hasPermission="'B_rubbishSearchTime_export'" :loading="loading" @click="handleExport">导出</a-button>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+    <!-- 合计 -->
+    <a-alert type="info" showIcon style="margin-bottom:15px">
+      <div class="ftext" slot="message">总计<span> {{ totalNum }} </span>条,可回收垃圾总计<span> {{ recyclTotal }} </span>千克,有害垃圾总计<span> {{ harmTotal }} </span>千克</div>
+    </a-alert>
+    <s-table
+      ref="table"
+      size="default"
+      :rowKey="(record) => record.id"
+      :columns="columns"
+      :data="loadData"
+      bordered>
+    </s-table>
+  </a-card>
+
+</template>
+
+<script>
+import { STable, VSelect } from '@/components'
+import {
+  stationList
+} from '@/api/releaseRecord.js'
+import { getPutInTimeList, exportTnTimeForm, getDetailTotal } from '@/api/reportForm.js'
+import moment from 'moment'
+import getDate from '@/libs/getDate.js'
+export default {
+  components: { STable, VSelect },
+  data () {
+    return {
+      loading: false,
+      form: this.$form.createForm(this),
+      formItemLayout: {
+        labelCol: { span: 4 },
+        wrapperCol: { span: 16 }
+      },
+      // 将默认近七天时间日期回显在时间选择框中
+      time: [
+        moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+        moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+      ],
+      dateFormat: 'YYYY-MM-DD HH',
+      // 查询参数
+      queryParam: {
+        beginDate: null, // 开始时间
+        endDate: null, // 结束时间
+        stationNo: undefined // 网点名称
+      },
+      optionData: [],
+      totalNum: 0,	// 总条数
+      recyclTotal: 0,	// 可回收总计
+	  harmTotal: 0, // 有害垃圾总计
+      // 表头
+      columns: [
+        { title: '序号', dataIndex: 'no', width: 60, align: 'center' },
+        { title: '投递时间', dataIndex: 'putInDate', width: 100, align: 'center' },
+        { title: '可回收垃圾投递量(kg)',
+          children: [
+            { title: '废旧衣物', width: 100, align: 'center', dataIndex: 'clothesWeight', customRender: (text) => { return text || 0 } },
+            { title: '废旧纸张', width: 100, align: 'center', dataIndex: 'paperWeight', customRender: (text) => { return text || 0 } },
+            { title: '废旧塑料', width: 100, align: 'center', dataIndex: 'plasticWeight', customRender: (text) => { return text || 0 } },
+            { title: '废旧金属', width: 100, align: 'center', dataIndex: 'metalWeight', customRender: (text) => { return text || 0 } },
+            { title: '废旧金属/塑料', width: 100, align: 'center', dataIndex: 'plasticOrMetalWeight', customRender: (text) => { return text || 0 } },
+            { title: '废旧玻璃', width: 100, align: 'center', dataIndex: 'glassWeight', customRender: (text) => { return text || 0 } },
+            { title: '可回收物', width: 100, align: 'center', dataIndex: 'recyclingWeight', customRender: (text) => { return text || 0 } }
+          ]
+        },
+        { title: '可回收垃圾小计(kg)', dataIndex: 'recyclTotalWeight', width: 100, align: 'center' },
+        { title: '有害垃圾(kg)', dataIndex: 'harmWeight', width: 100, align: 'center' }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        const searchData = Object.assign(parameter, this.queryParam)
+        if (this.time && this.time.length) {
+          searchData.beginDate = moment(this.time[0])
+          searchData.endDate = moment(this.time[1])
+        } else {
+          searchData.beginDate = ''
+          searchData.endDate = ''
+        }
+        return getPutInTimeList(searchData).then(res => {
+          if (res.status == 200) {
+            const no = (res.data.pageNo - 1) * res.data.pageSize
+            for (let i = 0; i < res.data.list.length; i++) {
+              const _item = res.data.list[i]
+              _item.no = no + i + 1
+            }
+            this.totalNum = res.data.count
+            this.getListTotal()
+            return res.data
+          } else {
+			  this.totalNum = 0
+		  }
+        })
+      }
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.time = [
+		  moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+		  moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+	  ]
+      vm.queryParam.stationNo = undefined
+      vm.getStationList()
+    })
+  },
+  methods: {
+    // 不可选日期
+    disabledDate (date, dateStrings) {
+      return date && date.valueOf() > Date.now()
+    },
+    // 创建时间change
+    onChange (dates, dateStrings) {
+      if ((dates, dateStrings)) {
+        this.queryParam.beginDate = dateStrings[0]
+        this.queryParam.endDate = dateStrings[1]
+      }
+    },
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    // 导出
+    handleExport () {
+      const params = this.queryParam
+      if (this.time && this.time.length) {
+        params.beginDate = moment(this.time[0])
+        params.endDate = moment(this.time[1])
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
+      }
+      if (!params.beginDate && !params.endDate) {
+        this.$message.error('请先选择需要导出的投放时间区间再进行导出!')
+        return
+      }
+      // 判断两个时间段是否相差m个月  第二个参数指相差单位,第三个参数指是否返回浮点形式(小数)
+      if (moment(params.endDate).diff(moment(params.beginDate), 'months', true) > 3) {
+        this.$message.error('单次最多只能导出3个月的数据,请缩小查询区间后再进行导出!')
+        return
+      }
+      this.$confirm({
+        title: '提示',
+        content: '导出过程可能需要一些时间,且导出期间不能进行其它操作,确定要导出吗?',
+        onOk: () => {
+          this.loading = true
+          exportTnTimeForm(params).then(res => {
+			  this.loading = false
+			  this.download(res)
+          })
+        },
+        onCancel () {
+          console.log('Cancel')
+        }
+      })
+    },
+    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(this.queryParam.beginDate).format('LL') + '-' + moment(this.queryParam.endDate).format('LL')
+	  console.log(a)
+	  const name = this.queryParam.stationNo ? this.optionData.find(item => item.stationNo == this.queryParam.stationNo).name : ''
+      const fname = a + '分时投递统计表' + (name ? '(' + name + ')' : '')
+      link.setAttribute('download', fname + '.xlsx')
+      document.body.appendChild(link)
+      link.click()
+    },
+    // 查询
+    refresh () {
+      this.$refs.table.refresh(true)
+    },
+    // 获取合作商数据
+    getStationList () {
+      stationList().then(res => {
+        if (res.status == 200) {
+          this.optionData = res.data || []
+        }
+      })
+    },
+    // 合计
+    getListTotal () {
+      const params = {
+        beginDate: this.queryParam.beginDate,
+        endDate: this.queryParam.endDate,
+        stationNo: this.queryParam.stationNo // 网点名称
+      }
+      if (this.time && this.time.length) {
+        params.beginDate = moment(this.time[0])
+        params.endDate = moment(this.time[1])
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
+      }
+      getDetailTotal(params).then(res => {
+        if (res.status == 200) {
+          if (res.data) {
+            this.recyclTotal = res.data.recyclAll || 0
+            this.harmTotal = res.data.harmAll || 0
+          } else {
+            this.recyclTotal = 0
+            this.harmTotal = 0
+          }
+        }
+      })
+    },
+    // 重置
+    handleReset () {
+      this.time = [
+		  moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+		  moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+	  ]
+      this.queryParam.stationNo = undefined
+      this.$refs.table.refresh(true)
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+  .shopOrder-table-page-search-wrapper{
+    .shopOrder-searchForm{
+      .ant-form-inline .ant-form-item{
+        width: 100%;
+      }
+      // 搜索框的下间距
+      .ant-form-item {
+      	margin-bottom: 10px;
+      }
+      // .handle-btn{
+      //   margin-bottom: 15px;
+      // }
+      .serach-btn{
+        margin-right: 10px;
+		margin-top: 4px;
+      }
+    }
+    .export-btn{
+      background-color: #ff9900;
+      border-color: #ff9900;
+      color: #fff;
+    }
+    .export-btn.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger), .export-btn.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger){
+      color: #fff;
+      border-color: #ff9900;
+    }
+    .actionBtn {
+    	font-size: 20px;
+    	padding: 0 10px;
+    }
+    .blue {
+    	color: #1890FF;
+    }
+    .green {
+    	color: #16b50e;
+    }
+    .red {
+    	color: red;
+    }
+  }
+</style>

+ 479 - 0
src/views/reportForm/rubbishDeliveryTotal.vue

@@ -0,0 +1,479 @@
+<template>
+  <a-card :bordered="false" class="shopOrder-table-page-search-wrapper">
+    <div class="network-searchForm">
+      <a-form-model ref="queryForm" :model="queryParam" layout="inline" v-bind="formItemLayout" @keyup.enter.native="$refs.table.refresh(true)">
+        <a-row :gutter="24">
+          <a-col :span="6">
+            <a-form-model-item label="投递时间" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+              <a-range-picker
+                id="rubbishTotal-time"
+                v-model="time"
+                style="width: 100%;"
+                :format="dateFormat"
+                :placeholder="['开始时间','结束时间']"
+                :disabledDate="disabledDate"
+                @change="onChange" />
+            </a-form-model-item>
+          </a-col>
+          <a-col :span="6">
+            <a-form-model-item prop="provinceCode" label="所属区域:" :label-col="{ span:8 }" :wrapper-col="{ span:16}">
+              <a-select id="rubbishTotal-provinceCode" allowClear v-model="queryParam.provinceCode" @change="getCityList" placeholder="请选择省">
+                <a-select-option v-for="item in addrProvinceList" :value="item.id" :key="item.id + 'a'">{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-model-item>
+          </a-col>
+          <a-col :span="5">
+            <a-form-model-item prop="cityCode" label=" " :colon="false" :label-col="{ span:1 }" :wrapper-col="{ span:22}">
+              <a-select allowClear id="rubbishTotal-cityCode" v-model="queryParam.cityCode" @change="getAreaList" placeholder="请选择市">
+                <a-select-option v-for="item in addrCityList" :value="item.id" :key="item.id + 'b'">{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-model-item>
+          </a-col>
+          <a-col :span="5">
+            <a-form-model-item prop="districtCode" label=" " :colon="false" :label-col="{ span:0 }" :wrapper-col="{ span:20}">
+              <a-select allowClear id="rubbishTotal-districtCode" v-model="queryParam.districtCode" placeholder="请选择区/县">
+                <a-select-option v-for="item in addrDistrictList" :value="item.id" :key="item.id + 'c'">{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+        <a-row :gutter="24">
+          <a-col :span="6">
+            <a-form-model-item label="网点名称" prop="stationNo" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+              <a-select
+                id="rubbishTotal-stationNo"
+                placeholder="请选择网点名称"
+                allowClear
+                v-model="queryParam.stationNo"
+                :showSearch="true"
+                option-filter-prop="children"
+                :filter-option="filterOption">
+                <a-select-option v-for="item in optionData" :key="item.stationNo" :value="item.stationNo">{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-model-item>
+          </a-col>
+          <a-col :span="10">
+            <a-button class="handle-btn serach-btn" id="rubbishTotal-btn-serach" type="primary" @click="$refs.table.refresh(true)">查询</a-button>
+            <a-button class="handle-btn serach-btn" type="" id="rubbishTotal-btn-reset" @click="handleReset">重置</a-button>
+            <a-button class="export-btn" id="rubbishTotal-export" v-hasPermission="'M_rubbishTotal_export'" :loading="loading" @click="handleExport">导出</a-button>
+          </a-col>
+        </a-row>
+      </a-form-model>
+    </div>
+    <!-- 合计 -->
+    <a-alert type="info" showIcon style="margin-bottom:15px">
+      <div class="ftext" slot="message">总计<span> {{ totalNum }} </span>条,可回收垃圾总计<span> {{ recyclTotal }} </span>千克,有害垃圾总计<span> {{ harmTotal }} </span>千克</div>
+    </a-alert>
+    <s-table
+      ref="table"
+      size="default"
+      :rowKey="(record) => record.id"
+      :columns="columns"
+      :data="loadData"
+      bordered>
+    </s-table>
+  </a-card>
+
+</template>
+
+<script>
+import {
+  STable,
+  VSelect
+} from '@/components'
+import {
+  stationList
+} from '@/api/releaseRecord.js'
+import {
+  getProvince,
+  getCityByPro,
+  getDistrictByCity
+} from '@/api/station'
+import {
+  getSumList,
+  exportSumForm,
+  getSumTotal
+} from '@/api/reportForm.js'
+import moment from 'moment'
+import getDate from '@/libs/getDate.js'
+export default {
+  components: {
+    STable,
+    VSelect
+  },
+  data () {
+    return {
+      loading: false,
+      form: this.$form.createForm(this),
+      formItemLayout: {
+        labelCol: {
+          span: 7
+        },
+        wrapperCol: {
+          span: 17
+        }
+      },
+      // 将默认近七天时间日期回显在时间选择框中
+      time: [
+        moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+        moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+      ],
+      dateFormat: 'YYYY-MM-DD',
+      // 查询参数
+      queryParam: {
+        beginDate: null, // 开始时间
+        endDate: null, // 结束时间
+        stationNo: undefined, // 网点名称
+        provinceCode: undefined, //  省
+        cityCode: undefined, // 市
+        districtCode: undefined // 区
+      },
+      addrProvinceList: [], //  省下拉
+      addrCityList: [], //  市下拉
+      addrDistrictList: [], //  区下拉
+      optionData: [],
+      totalNum: 0,	// 总条数
+      recyclTotal: 0,	// 可回收总计
+      harmTotal: 0, // 有害垃圾总计
+      // 表头
+      columns: [{
+        title: '序号',
+        dataIndex: 'no',
+        width: 60,
+        align: 'center'
+      },
+      {
+        title: '网点名称',
+        dataIndex: 'stationName',
+        width: 100,
+        align: 'center'
+      },
+      {
+        title: '可回收垃圾投递量(kg)',
+        children: [{
+          title: '废旧衣物',
+          width: 100,
+          align: 'center',
+          dataIndex: 'clothesWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '废旧纸张',
+          width: 100,
+          align: 'center',
+          dataIndex: 'paperWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '废旧塑料',
+          width: 100,
+          align: 'center',
+          dataIndex: 'plasticWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '废旧金属',
+          width: 100,
+          align: 'center',
+          dataIndex: 'metalWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '废旧金属/塑料',
+          width: 100,
+          align: 'center',
+          dataIndex: 'plasticOrMetalWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '废旧玻璃',
+          width: 100,
+          align: 'center',
+          dataIndex: 'glassWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        },
+        {
+          title: '可回收物',
+          width: 100,
+          align: 'center',
+          dataIndex: 'recyclingWeight',
+          customRender: (text) => {
+            return text || 0
+          }
+        }
+        ]
+      },
+      {
+        title: '可回收垃圾小计(kg)',
+        dataIndex: 'recyclTotalWeight',
+        width: 100,
+        align: 'center'
+      },
+      {
+        title: '有害垃圾(kg)',
+        dataIndex: 'harmWeight',
+        width: 100,
+        align: 'center'
+      }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        const searchData = Object.assign(parameter, this.queryParam)
+        if (this.time && this.time.length) {
+          searchData.beginDate = moment(this.time[0]).format('YYYY-MM-DD')
+          searchData.endDate = moment(this.time[1]).format('YYYY-MM-DD')
+        } else {
+          searchData.beginDate = ''
+          searchData.endDate = ''
+        }
+        return getSumList(searchData).then(res => {
+          if (res.status == 200) {
+            const no = (res.data.pageNo - 1) * res.data.pageSize
+            for (let i = 0; i < res.data.list.length; i++) {
+              const _item = res.data.list[i]
+              _item.no = no + i + 1
+            }
+            this.totalNum = res.data.count
+            this.getListTotal()
+            return res.data
+          } else {
+			  this.totalNum = 0
+		  }
+        })
+      }
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {
+      vm.time = [
+		  moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+		  moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+      ]
+      vm.$refs.queryForm.resetFields()
+      vm.getStationList()
+      vm.getProvinceList()
+    })
+  },
+  methods: {
+    // 不可选日期
+    disabledDate (date, dateStrings) {
+      return date && date.valueOf() > Date.now()
+    },
+    // 创建时间change
+    onChange (dates, dateStrings) {
+      if ((dates, dateStrings)) {
+        this.queryParam.beginDate = dateStrings[0]
+        this.queryParam.endDate = dateStrings[1]
+      }
+    },
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    // 获取省列表'
+    getProvinceList () {
+      getProvince().then(res => {
+        if (res.status == 200) {
+          this.addrProvinceList = res.data || []
+        } else {
+          this.addrProvinceList = []
+        }
+      })
+    },
+    // 获取城市列表
+    getCityList (val) {
+      this.addrCityList = []
+      this.addrDistrictList = []
+      this.queryParam.cityCode = undefined
+      this.queryParam.districtCode = undefined
+      this.getCityListRequest(val)
+    },
+    getCityListRequest (val) {
+      getCityByPro({
+        id: val
+      }).then(res => {
+        if (res.status == 200) {
+          this.addrCityList = res.data || []
+        } else {
+          this.addrCityList = []
+        }
+      })
+    },
+    // 获取区县列表
+    getAreaList (val) {
+      this.addrDistrictList = []
+      this.queryParam.districtCode = undefined
+      this.getAreaListRequest(val)
+    },
+    getAreaListRequest (val) {
+      getDistrictByCity({
+        id: val
+      }).then(res => {
+        if (res.status == 200) {
+          this.addrDistrictList = res.data || []
+        } else {
+          this.addrDistrictList = []
+        }
+      })
+    },
+    // 导出
+    handleExport () {
+      const params = this.queryParam
+      if (this.time && this.time.length) {
+        params.beginDate = moment(this.time[0]).format('YYYY-MM-DD')
+        params.endDate = moment(this.time[1]).format('YYYY-MM-DD')
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
+      }
+      if (!params.beginDate && !params.endDate) {
+        this.$message.error('请先选择需要导出的投放时间区间再进行导出!')
+        return
+      }
+      // 判断两个时间段是否相差m个月  第二个参数指相差单位,第三个参数指是否返回浮点形式(小数)
+      if (moment(params.endDate).diff(moment(params.beginDate), 'months', true) > 3) {
+        this.$message.error('单次最多只能导出3个月的数据,请缩小查询区间后再进行导出!')
+        return
+      }
+      this.$confirm({
+		  title: '提示',
+		  content: '导出过程可能需要一些时间,且导出期间不能进行其它操作,确定要导出吗?',
+		  onOk: () => {
+			  this.loading = true
+			  exportSumForm(params).then(res => {
+				  this.loading = false
+				  this.download(res)
+			  })
+		  },
+		  onCancel () {
+          console.log('Cancel')
+		  }
+      })
+    },
+    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(this.queryParam.beginDate).format('LL') + '-' + moment(this.queryParam.endDate).format('LL')
+      const address = this.queryParam.provinceCode ? this.addrProvinceList.find(item => item.id == this.queryParam.provinceCode).name +
+	  (this.queryParam.cityCode ? this.addrCityList.find(item => item.id == this.queryParam.cityCode).name : '') +
+	  (this.queryParam.districtCode ? this.addrDistrictList.find(item => item.id == this.queryParam.districtCode).name : '') : ''
+      const fname = a + '垃圾分类投递量统计表' + (address ? '(' + address + ')' : '')
+      link.setAttribute('download', fname + '.xlsx')
+      document.body.appendChild(link)
+      link.click()
+    },
+    // 查询
+    refresh () {
+      this.$refs.table.refresh(true)
+    },
+    // 获取合作商数据
+    getStationList () {
+      stationList().then(res => {
+        if (res.status == 200) {
+          this.optionData = res.data || []
+        }
+      })
+    },
+    // 合计
+    getListTotal () {
+      const params = this.queryParam
+      if (this.time && this.time.length) {
+        params.beginDate = moment(this.time[0]).format('YYYY-MM-DD')
+        params.endDate = moment(this.time[1]).format('YYYY-MM-DD')
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
+      }
+      getSumTotal(params).then(res => {
+        if (res.status == 200) {
+          if (res.data) {
+            this.recyclTotal = res.data.recyclAll || 0
+            this.harmTotal = res.data.harmAll || 0
+          } else {
+            this.recyclTotal = 0
+            this.harmTotal = 0
+          }
+        }
+      })
+    },
+    // 重置
+    handleReset () {
+      this.time = [
+		  moment(getDate.getRecentday().starttime, 'YYYY-MM-DD HH'),
+		  moment(getDate.getRecentday().endtime, 'YYYY-MM-DD HH')
+	  ]
+      this.$refs.queryForm.resetFields()
+      this.$refs.table.refresh(true)
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+	.shopOrder-table-page-search-wrapper {
+		.network-searchForm {
+			.ant-form-inline .ant-form-item {
+				width: 100%;
+			}
+
+			// 搜索框的下间距
+			.ant-form-item {
+				margin-bottom: 10px;
+			}
+
+			.handle-btn {
+				margin-top: 4px;
+			}
+
+			.serach-btn {
+				margin-right: 10px;
+			}
+		}
+
+		.export-btn {
+			background-color: #ff9900;
+			border-color: #ff9900;
+			color: #fff;
+		}
+
+		.export-btn.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),
+		.export-btn.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger) {
+			color: #fff;
+			border-color: #ff9900;
+		}
+
+		.actionBtn {
+			font-size: 20px;
+			padding: 0 10px;
+		}
+
+		.blue {
+			color: #1890FF;
+		}
+
+		.green {
+			color: #16b50e;
+		}
+
+		.red {
+			color: red;
+		}
+	}
+</style>

+ 155 - 49
src/views/userInfo/userManageList.vue

@@ -3,20 +3,44 @@
     <div class="userManageList-searchForm">
       <a-form layout="inline" v-bind="formItemLayout" @keyup.enter.native="refresh">
         <a-row :gutter="20">
-          <a-col :span="5">
+          <a-col :span="6">
+            <a-form-item label="注册时间" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
+              <a-range-picker
+                id="network-time"
+                v-model="registerTime"
+                :format="dateFormat"
+                :placeholder="['开始时间','结束时间']"
+                :disabledDate="disabledDate"
+                @change="onChangeregisterTime" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="6">
+            <a-form-item label="最后投递时间" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
+              <a-range-picker
+                id="network-time"
+                v-model="lastDeliverTime"
+                :format="dateFormat"
+                :placeholder="['开始时间','结束时间']"
+                :disabledDate="disabledDate"
+                @change="onChangelastDeliverTime" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="6">
             <a-form-item label="用户手机" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
               <a-input id="releaseRecordList-mobile" allowClear :maxLength="11" v-model="queryParam.mobile" placeholder="请输入用户手机" />
             </a-form-item>
           </a-col>
-          <a-col :span="5">
+          <a-col :span="6">
             <a-form-item label="乐豆余额" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
               <div class="input-number">
-                <a-input-number id="releaseRecordList-inputNumber" v-model="queryParam.currentGoldMin" :max="999999999" @change="onChange" />-
-                <a-input-number id="inputNumber" v-model="queryParam.currentGoldMax" :max="999999999" @change="onChange" />
+                <a-input-number id="releaseRecordList-inputNumber" v-model="queryParam.currentGoldMin" :max="999999999" />-
+                <a-input-number id="inputNumber" v-model="queryParam.currentGoldMax" :max="999999999" />
               </div>
             </a-form-item>
           </a-col>
-          <a-col :span="5">
+        </a-row>
+        <a-row :gutter="20">
+          <a-col :span="6">
             <a-form-item label="账户状态" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
               <v-select
                 v-model="queryParam.state"
@@ -26,7 +50,7 @@
                 allowClear></v-select>
             </a-form-item>
           </a-col>
-          <a-col :span="5">
+          <a-col :span="6">
             <a-form-item label="用户身份" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
               <v-select
                 v-model="queryParam.customerRole"
@@ -36,7 +60,7 @@
                 allowClear></v-select>
             </a-form-item>
           </a-col>
-          <a-col :span="4">
+          <a-col :span="6">
             <a-button class="handle-btn serach-btn" id="releaseRecordList-btn-serach" type="primary" @click="refresh">查询</a-button>
             <a-button class="handle-btn" type="" id="releaseRecordList-btn-reset" @click="handleReset">重置</a-button>
             <a-button
@@ -47,6 +71,7 @@
               v-hasPermission="'B_user_userManage_export'">导出</a-button>
           </a-col>
         </a-row>
+
       </a-form>
     </div>
     <s-table
@@ -88,6 +113,7 @@ import {
   changeStatus,
   customeExport
 } from '@/api/userInfo.js'
+import moment from 'moment'
 export default {
   name: 'UserManageList',
   components: {
@@ -104,13 +130,20 @@ export default {
           span: 17
         }
       },
+      registerTime: [], // 注册时间
+      lastDeliverTime: [], // 最后投递时间
+      dateFormat: 'YYYY-MM-DD',
       // 查询参数
       queryParam: {
         mobile: '', //  用户手机
         state: '', // 账户状态
         customerRole: '', // 用户身份
         currentGoldMin: '', // 乐豆余额最小值
-        currentGoldMax: '' // 乐豆余额最大值
+        currentGoldMax: '', // 乐豆余额最大值
+        beginDate: null, // 最后投递时间的开始时间
+        endDate: null, // 最后投递时间的结束时间
+        beginRegisterTime: null, //  注册时间最小值
+        endRegisterTime: null //  注册时间最大值
       },
       loading: false, // 导出loading
       // 表头
@@ -126,12 +159,12 @@ export default {
         width: 100,
         align: 'center'
       },
-	  {
-	    title: '用户身份',
-	    dataIndex: 'customerRoleDictValue',
-	    width: 100,
-	    align: 'center'
-	  },
+      {
+        title: '用户身份',
+        dataIndex: 'customerRoleDictValue',
+        width: 100,
+        align: 'center'
+      },
       {
         title: '乐豆余额',
         dataIndex: 'currentGold',
@@ -156,7 +189,7 @@ export default {
         width: 200,
         align: 'center',
         customRender: (text) => {
-		  return text || '--'
+          return text || '--'
         }
       },
       {
@@ -165,7 +198,7 @@ export default {
         width: 200,
         align: 'center',
         customRender: (text) => {
-		  return text || '--'
+          return text || '--'
         }
       },
       {
@@ -174,7 +207,7 @@ export default {
         width: 200,
         align: 'center',
         customRender: (text) => {
-		  return text || '--'
+          return text || '--'
         }
       },
       {
@@ -197,7 +230,22 @@ export default {
       ],
       // 加载数据方法 必须为 Promise 对象
       loadData: parameter => {
-        return customerList(Object.assign(parameter, this.queryParam)).then(res => {
+        const searchData = Object.assign(parameter, this.queryParam)
+        if (this.registerTime && this.registerTime.length) {
+          searchData.beginRegisterTime = moment(this.registerTime[0])
+          searchData.endRegisterTime = moment(this.registerTime[1])
+        } else {
+          searchData.beginRegisterTime = null
+          searchData.endRegisterTime = null
+        }
+        if (this.lastDeliverTime && this.lastDeliverTime.length) {
+          searchData.beginDate = moment(this.lastDeliverTime[0])
+          searchData.endDate = moment(this.lastDeliverTime[1])
+        } else {
+          searchData.beginDate = null
+          searchData.endDate = null
+        }
+        return customerList(searchData).then(res => {
           if (res.status == 200) {
             const no = (res.data.pageNo - 1) * res.data.pageSize
             for (let i = 0; i < res.data.list.length; i++) {
@@ -213,13 +261,30 @@ export default {
   },
   beforeRouteEnter (to, from, next) {
     next(vm => {
-      vm.handleReset()
+      vm.queryParam = {}
+	  vm.registerTime = []
+	  vm.lastDeliverTime = []
     })
   },
   methods: {
-	  onChange () {
-
-	  },
+    // 不可选日期
+    disabledDate (date, dateStrings) {
+      return date && date.valueOf() > Date.now()
+    },
+    // 注册时间change
+    onChangeregisterTime (dates, dateStrings) {
+      if ((dates, dateStrings)) {
+        this.queryParam.beginRegisterTime = dateStrings[0]
+        this.queryParam.endRegisterTime = dateStrings[1]
+      }
+    },
+    // 最后投递时间change
+    onChangelastDeliverTime (dates, dateStrings) {
+      if ((dates, dateStrings)) {
+        this.queryParam.beginDate = dateStrings[0]
+        this.queryParam.endDate = dateStrings[1]
+      }
+    },
     refresh () {
       const isONull = this.queryParam.currentGoldMin === null
       const isOEmpty = this.queryParam.currentGoldMin === ''
@@ -254,18 +319,14 @@ export default {
     },
     // 查看乐豆详情
     handleLd (row) {
-    	this.$router.push({
-    		// name: 'ledouDetail',
-    		// params: {
-    		// 	customerNo: row.customerNo,
-    		// 	mobile: row.mobile,
-    		// 	currentGold: row.currentGold,
-      //     id: row.id
-    		// }
-    		path: '/userInfo/userManageList_Ld/detail', query: { customerNo: row.customerNo, id: row.id }
-    	})
+      this.$router.push({
+        path: '/userInfo/userManageList_Ld/detail',
+        query: {
+          customerNo: row.customerNo,
+          id: row.id
+        }
+      })
     },
-    // 启用禁用
     // 更改启用禁用状态
     changeFlagHandle (text, record) {
       const _this = this
@@ -284,27 +345,72 @@ export default {
     },
     // 重置
     handleReset () {
-      this.queryParam.mobile = ''
-      this.queryParam.currentGoldMin = ''
-      this.queryParam.currentGoldMax = ''
-      this.queryParam.state = ''
-      this.queryParam.customerRole = ''
+      this.queryParam.mobile = '' //  用户手机
+      this.queryParam.currentGoldMin = '' // 乐豆余额最小值
+      this.queryParam.currentGoldMax = '' // 乐豆余额最大值
+      this.queryParam.state = '' // 账户状态
+      this.queryParam.customerRole = '' // 用户身份
+      this.queryParam.beginDate = null // 最后投递时间的开始时间
+      this.queryParam.endDate = null // 最后投递时间的结束时间
+      this.queryParam.beginRegisterTime = null //  注册时间最小值
+      this.queryParam.endRegisterTime = null //  注册时间最大值
+      this.registerTime = []
+      this.lastDeliverTime = []
       this.$refs.table.refresh(true)
     },
     // 导出
     handleExport () {
-      const params = {
-        mobile: this.queryParam.mobile,
-        customerRole: this.queryParam.customerRole,
-        currentGoldMin: this.queryParam.currentGoldMin,
-        currentGoldMax: this.queryParam.currentGoldMax,
-        state: this.queryParam.state = ''
+      const params = this.queryParam
+      if (this.lastDeliverTime && this.lastDeliverTime.length) {
+        params.beginDate = moment(this.lastDeliverTime[0])
+        params.endDate = moment(this.lastDeliverTime[1])
+        if (!params.beginDate && !params.endDate) {
+          this.$message.error('请先选择需要导出的最后投递时间区间再进行导出!')
+          return
+        }
+        // 判断两个时间段是否相差m个月  第二个参数指相差单位,第三个参数指是否返回浮点形式(小数)
+        if (moment(params.endDate).diff(moment(params.beginDate), 'months', true) > 3) {
+          this.$message.error('单次最多只能导出3个月的数据,请缩小最后投递时间查询区间后再进行导出!')
+          return
+        }
+      } else {
+        params.beginDate = ''
+        params.endDate = ''
       }
-      this.loading = true
-      customeExport(params).then(res => {
-        this.loading = false
-        this.download(res)
-      })
+
+      if (this.registerTime && this.registerTime.length) {
+        params.beginRegisterTime = moment(this.registerTime[0])
+        params.endRegisterTime = moment(this.registerTime[1])
+        if (!params.beginRegisterTime && !params.endRegisterTime) {
+          this.$message.error('请先选择需要导出的注册时间区间再进行导出!')
+          return
+        }
+        // 判断两个时间段是否相差m个月  第二个参数指相差单位,第三个参数指是否返回浮点形式(小数)
+        if (moment(params.endRegisterTime).diff(moment(params.beginRegisterTime), 'months', true) > 3) {
+          this.$message.error('单次最多只能导出3个月的数据,请缩小注册时间查询区间后再进行导出!')
+          return
+        }
+      } else {
+        params.beginRegisterTime = ''
+        params.endRegisterTime = ''
+      }
+      if (this.lastDeliverTime.length == 0 && this.registerTime.length == 0) {
+        this.$message.error('请先选择需要导出的注册时间区间或最后投递时间区间再进行导出(3个月以内)!')
+        return
+      }
+	  this.$confirm({
+	    title: '提示',
+	    content: '导出过程可能需要一段时间,且导出期间不可进行其他操作,确认要导出吗?',
+	    onOk: () => {
+	  		this.loading = true
+	  		customeExport(params).then(res => {
+	  		  this.loading = false
+	  		  this.download(res)
+	  		})
+	    },
+	    onCancel () {
+        }
+	  })
     },
     download (data) {
       if (!data) {

+ 1 - 1
vue.config.js

@@ -108,7 +108,7 @@ const vueConfig = {
     // If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
     proxy: {
       '/api': {
-        // target: 'http://192.168.16.105:8301/gc-admin/',
+        // target: 'http://192.168.16.149:8301/gc-admin/',
         target: 'https://lese.test.sxzxyj.net/gc-admin/',
         ws: false,
         changeOrigin: true,