Jelajahi Sumber

Merge remote-tracking branch 'origin/developer' into developerA

zhengjinyi 1 tahun lalu
induk
melakukan
5263aa2010

+ 24 - 0
src/api/mallPortrait/disitrib.js

@@ -0,0 +1,24 @@
+import request from '@/utils/request'
+
+/**
+ * 获取机构统计报表
+ */
+export const getIns_statistics = (query) => request({
+  url: '/home/getClubCount',
+  method: 'get',
+  params: query
+})
+
+// 获取机构订单趋势
+export const getIns_orderTrends = (query) => request({
+  url: '/home/getClubTrend',
+  method: 'get',
+  params: query
+})
+
+// 获取机构访问统计
+export const getIns_accessStatistics = (query) => request({
+  url: '/home/getAccessRecordCount',
+  method: 'get',
+  params: query
+})

+ 1 - 2
src/components/Charts/mixins/resize.js

@@ -49,8 +49,7 @@ export default {
       this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
     },
     resize() {
-      const { chart } = this
-      chart && chart.resize()
+      this.chart && this.chart.resize()
     }
   }
 }

+ 3 - 1
src/router/index.js

@@ -15,6 +15,7 @@ import productRouter from './modules/product'
 import financeRouter from './modules/finance'
 import keywordLibraryRouter from './modules/keywordLibrary'
 import memberRouter from './modules/member'
+import mallProtraitRouter from './modules/mallPortrait'
 // import tableRouter from './modules/table'
 // import nestedRouter from './modules/nested'
 
@@ -101,7 +102,8 @@ export const asyncRoutes = [
   financeRouter,
   wechatRouter,
   memberRouter,
-  keywordLibraryRouter
+  keywordLibraryRouter,
+  mallProtraitRouter
 ]
 
 /**

+ 28 - 0
src/router/modules/mallPortrait.js

@@ -0,0 +1,28 @@
+import Layout from '@/layout'
+
+export default {
+  path: '/mall',
+  component: Layout,
+  meta: { title: '商城数据分析', icon: 'link' },
+  name: 'Mall',
+  redirect: '/mall/portrait',
+  alwaysShow: true,
+  children: [
+    {
+      path: 'portrait',
+      name: 'Portrait',
+      meta: { title: '数据分析', icon: 'component' },
+      redirect: '/mall/portrait/index',
+      component: () => import('@/views/index'),
+      children: [
+        {
+          path: 'index',
+          hidden: true,
+          component: () => import('@/views/mall-portrait/index.vue'),
+          name: 'Index',
+          meta: { title: '数据分析', activeMenu: '/mall/portrait' }
+        }
+      ]
+    }
+  ]
+}

+ 1 - 1
src/views/library/keyword/list.vue

@@ -39,7 +39,7 @@
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="序号" :index="indexMethod" type="index" sortable="custom" align="center" width="80" />
       <el-table-column prop="keyword" label="关键词" align="center" />
-      <el-table-column prop="frequency" label="搜索次数" width="120" align="center" sortable
+      <el-table-column prop="pv" label="搜索次数" width="120" align="center" sortable
                        :sort-orders="['ascending', 'descending']"
       />
       <el-table-column label="最近搜索时间" align="center">

+ 129 - 0
src/views/mall-portrait/components/hot-tips.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="hot-container">
+    <div class="head">
+      <slot name="head"></slot>
+    </div>
+    <div class="content">
+      <div class="title" :style="tabStyle">
+        <span v-for="c, index in columns" :key="index">{{ c.text }}</span>
+      </div>
+      <div v-for="hot, i in reloadData" :key="i" class="cell" :style="tabStyle">
+        <template v-if="isObj(hot)">
+          <div v-for="_, index in columns" :key="index">
+            <div v-if="is_picture(hot[_.label])" class="picture"><el-image v-if="hot[_.label]" :src="hot[_.label]" fit="contain" /></div>
+            <div v-else-if="!is_picture(hot[_.label]) && _.label !== 'top'" class="text">{{ hot[_.label] || '' }}</div>
+            <div v-else>{{ i + 1 }}</div>
+          </div>
+        </template>
+        <template v-else>
+          <div class="text">{{ hot }}</div>
+          <div>{{ i + 1 }}</div>
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    columns: {
+      type: Array,
+      default: () => []
+    },
+    hotData: {
+      type: Array,
+      default: () => [
+        {
+          logo: 'https://baidu.jpg',
+          bandName: '百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度百度',
+          top: 1
+        }
+      ]
+    }
+  },
+  computed: {
+    tabStyle() {
+      const columns = this.columns.map(c => c.labelWidth ? c.labelWidth + 'px' : 'auto').join(' ')
+      return {
+        gridTemplateColumns: columns,
+        gridGap: '10px'
+      }
+    },
+    reloadData() {
+      const d = this.hotData
+      return d.map(e => {
+        if (e) {
+          return e
+        } else return {}
+      })
+    }
+  },
+  methods: {
+    isObj(str) {
+      return (typeof str === 'object')
+    },
+    is_picture(str) {
+      const r = new RegExp(/\.(png|jpe?g|gif|svg|JPEG)(\?.*)?$/)
+      return r.test(str)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.hot-container {
+  border-radius: 12px;
+  overflow: hidden;
+  border: 1px solid #ccc;
+  box-shadow: 0 0 4px #ccc;
+  .head {
+    padding: 10px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+  }
+  .content {
+    padding: 10px;
+    font-size: 14px;
+    .title {
+      display: grid;
+      align-items: center;
+      padding-bottom: 10px;
+      border-bottom: 1px solid #ccc;
+      align-items: center;
+      span {
+        text-align: center;
+      }
+    }
+    .cell {
+      padding: 10px 0;
+      display: grid;
+      box-sizing: border-box;
+      width: 100%;
+      border-bottom: 1px solid #ccc;
+      align-items: center;
+      text-align: center;
+      height: 60px;
+      .picture {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+      .text {
+        text-align: center;
+        display: -webkit-box;
+        -webkit-box-orient: vertical;
+        -webkit-line-clamp: 2;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+      ::v-deep .el-image {
+        width: 60px;
+        height: 40px;
+      }
+    }
+  }
+}
+</style>

+ 136 - 0
src/views/mall-portrait/components/line-echarts.vue

@@ -0,0 +1,136 @@
+<template>
+  <div class="echarts-content">
+    <div ref="echarts" class="line-echarts"></div>
+  </div>
+</template>
+
+<script>
+import echarts from 'echarts'
+import loopEchart from '../mixins/index'
+import resize from '@/components/Charts/mixins/resize'
+
+export default {
+  mixins: [loopEchart, resize],
+  props: {
+    echartsName: {
+      type: String,
+      default: () => '机构新增趋势'
+    },
+    optionData: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      chart: null
+    }
+  },
+  watch: {
+    optionData: {
+      handler(val) {
+        if (val) {
+          clearInterval(this.timeTicket)
+          this.timeTicket = null
+          this.initChart()
+        }
+      },
+      deep: true
+    }
+  },
+  methods: {
+    getXAxis() {
+      return this.optionData['个人机构']?.map(e => e.time)
+    },
+    getLegend() {
+      const a = []
+      for (const key in this.optionData) {
+        a.push(key)
+      }
+      return a
+    },
+    getSeriesData() {
+      const a = []
+      for (const key in this.optionData) {
+        const b = {}
+        b['name'] = key
+        b['type'] = 'line'
+        b['stack'] = 'Total'
+        b['data'] = this.optionData[key]?.map(e => e.num)
+        a.push(b)
+      }
+      return a
+    },
+    initChart() {
+      const myEchart = this.$refs['echarts']
+      this.chart = echarts.init(myEchart)
+      const option = {
+        title: {
+          text: this.echartsName
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross' // 提示框样式为十字准星
+          }
+        },
+        legend: {
+          data: this.getLegend()
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true
+        },
+        toolbox: {
+        },
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          data: this.getXAxis()
+        },
+        yAxis: {
+          type: 'value'
+        },
+        series: this.getSeriesData()
+      }
+      this.chart && this.chart.setOption(option)
+      // this.chart && this.handleChartLineLoop(option, this.chart)
+    },
+    handleMouseOver(e, callback) {
+      if (e) {
+        const myEchart = this.$refs['echarts']
+        myEchart.addEventListener('mouseover', callback)
+      }
+    },
+    handleMouseOut(e, callback) {
+      if (e) {
+        const myEchart = this.$refs['echarts']
+        myEchart.addEventListener('mouseout', callback)
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.echarts-content {
+  width: 100%;
+  height: 350px;
+  border-radius: 12px;
+  border: 1px solid #ccc;
+  box-shadow: 0 0 4px #ccc;
+  overflow: hidden;
+  padding: 10px;
+  margin-bottom: 14px;
+  .title {
+    display: flex;
+    align-items: center;
+  }
+  .line-echarts {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 70 - 0
src/views/mall-portrait/components/pei-echarts.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="echarts-content">
+    <div class="title">
+      <slot name="title"></slot>
+    </div>
+    <pie-echart
+      :title="title"
+      :show-emphasis="true"
+      :option-data="optionData1"
+      :show-graphic="false"
+      :show-animate="true"
+    />
+  </div>
+</template>
+
+<script>
+import PieEchart from './pie-echart.vue'
+
+export default {
+  components: {
+    PieEchart
+  },
+  props: {
+    title: {
+      type: String,
+      default: () => 'myEcharts'
+    },
+    optionData: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      optionData1: {}
+    }
+  },
+  watch: {
+    optionData: {
+      handler(val) {
+        this.optionData1 = val
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  mounted() {},
+  methods: {}
+}
+</script>
+
+<style lang="scss" scoped>
+.echarts-content {
+  width: 100%;
+  height: 300px;
+  border-radius: 12px;
+  border: 1px solid #ccc;
+  box-shadow: 0 0 4px #ccc;
+  overflow: hidden;
+  padding: 10px;
+  .title {
+    display: flex;
+    align-items: center;
+  }
+  .pei-echarts {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 110 - 0
src/views/mall-portrait/components/pie-dub-echarts.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="echarts-content">
+    <div class="title">
+      <slot name="title"></slot>
+    </div>
+    <div class="content">
+      <div class="pei-echarts">
+        <pie-echart
+          :title="title"
+          :option-data="opData1"
+          :show-legend="false"
+          :center="['50%', '50%']"
+          :show-tips="true"
+          :show-graphic="true"
+          :color="['black', 'orange']"
+          :echart-type="1"
+        />
+        <slot name="echarts1"></slot>
+      </div>
+      <div class="pei-echarts">
+        <pie-echart
+          :title="title"
+          :option-data="opData2"
+          :show-legend="false"
+          :center="['50%', '50%']"
+          :show-tips="true"
+          :show-graphic="true"
+          :color="['orange', '#cccccc']"
+          :echart-type="2"
+        />
+        <slot name="echarts2"></slot>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import PieEchart from './pie-echart.vue'
+
+export default {
+  name: 'PieDubEcharts',
+  components: {
+    PieEchart
+  },
+  props: {
+    title: {
+      type: String,
+      default: () => '机构活跃统计'
+    },
+    optionData1: {
+      type: Object,
+      default: () => ({})
+    },
+    optionData2: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      opData1: {},
+      opData2: {}
+    }
+  },
+  watch: {
+    optionData1: {
+      handler(val) {
+        this.opData1 = val
+      },
+      deep: true,
+      immediate: true
+    },
+    optionData2: {
+      handler(val) {
+        this.opData2 = val
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  mounted() {},
+  methods: {}
+}
+</script>
+
+<style lang="scss" scoped>
+.echarts-content {
+  width: 100%;
+  height: 300px;
+  border-radius: 12px;
+  border: 1px solid #ccc;
+  box-shadow: 0 0 4px #ccc;
+  overflow: hidden;
+  padding: 10px;
+  .title {
+    display: flex;
+    align-items: center;
+  }
+  .content {
+    display: grid;
+    width: 100%;
+    height: calc(300px - 24px);
+    grid-template-columns: repeat(2, 1fr);
+    .pei-echarts {
+      width: 100%;
+      height: 200px;
+    }
+  }
+}
+</style>

+ 179 - 0
src/views/mall-portrait/components/pie-echart.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="echarts_content">
+    <div ref="echarts" class="pie_echarts"></div>
+  </div>
+</template>
+
+<script>
+import echarts from 'echarts'
+import loopEchart from '../mixins/index'
+import resize from '@/components/Charts/mixins/resize'
+
+export default {
+  mixins: [loopEchart, resize],
+  props: {
+    echartsName: {
+      type: String,
+      default: () => '机构新增趋势'
+    },
+    center: {
+      type: Array,
+      default: () => ['30%', '50%']
+    },
+    title: {
+      type: String,
+      default: () => 'myEcharts'
+    },
+    showEmphasis: {
+      type: Boolean,
+      default: false
+    },
+    showLabel: {
+      type: Boolean,
+      default: false
+    },
+    showLegend: {
+      type: Boolean,
+      default: true
+    },
+    showGraphic: {
+      type: Boolean,
+      default: false
+    },
+    optionData: {
+      type: Object,
+      default: () => ({})
+    },
+    showTips: {
+      type: Boolean,
+      default: false
+    },
+    color: {
+      type: Array,
+      default: () => []
+    },
+    echartType: {
+      type: Number,
+      default: () => 1
+    },
+    showAnimate: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      chart: null,
+      c_options: null
+    }
+  },
+  watch: {
+    optionData: {
+      handler(val) {
+        if (val) {
+          const a = []
+          let num = 0
+          for (const key in val) {
+            const b = {}
+            b['name'] = key
+            b['value'] = val[key]
+            a.push(b)
+            num += val[key]
+          }
+          const text = this.echartType === 1 ? (val[Object.keys(val)[0]] / num) * 100 : (val[Object.keys(val)[1]] / num) * 100
+          this.c_options = {
+            tooltip: {
+              trigger: 'item'
+            },
+            graphic: this.showGraphic && [
+              {
+                type: 'text',
+                left: 'center',
+                top: 'center',
+                style: {
+                  text: num === 0 ? '0%' : text.toFixed(2) + '%',
+                  textAlign: 'center',
+                  fill: '#000',
+                  fontSize: 16,
+                  fontWeight: 'bold'
+                }
+              }
+            ],
+            legend: this.showLegend && {
+              top: 'center',
+              right: '9%',
+              orient: 'vertical',
+              icon: 'circle'
+            },
+            series: [
+              {
+                name: this.title,
+                type: 'pie',
+                center: this.center, // 饼图位置
+                radius: ['50%', '70%'],
+                avoidLabelOverlap: false,
+                itemStyle: {
+                  borderRadius: 50,
+                  borderColor: '#fff',
+                  borderWidth: 4
+                },
+                label: {
+                  show: this.showLabel,
+                  position: 'center'
+                },
+                emphasis: {
+                  label: {
+                    show: this.showEmphasis,
+                    fontSize: 14,
+                    fontWeight: 'bold',
+                    formatter: (params) => params.name + '\n' + params.value + ' (' + params.percent + '%)'
+                  }
+                },
+                labelLine: {
+                  show: false
+                },
+                data: a
+              }
+            ]
+          }
+        }
+        this.initChart()
+      },
+      deep: true
+    }
+  },
+  methods: {
+    initChart() {
+      const myEchart = this.$refs['echarts']
+      clearInterval(this.timeTicket)
+      this.timeTicket = null
+      this.chart = echarts.init(myEchart)
+      this.chart && this.chart.setOption(this.c_options)
+      this.showAnimate && this.chart && this.handleChartPieLoop(this.c_options, this.chart, 'timeTicket', 'currentIndex', this.showTips)
+    },
+    handleMouseOver(e, callback) {
+      if (e) {
+        const myEchart = this.$refs['echarts']
+        myEchart.addEventListener('mouseover', callback)
+      }
+    },
+    handleMouseOut(e, callback) {
+      if (e) {
+        const myEchart = this.$refs['echarts']
+        myEchart.addEventListener('mouseout', callback)
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.echarts_content {
+  width: 100%;
+  height: 100%;
+  .pie_echarts {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 124 - 0
src/views/mall-portrait/components/select-time.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="time-select">
+    <div
+      v-for="i in timeList"
+      :key="i.id"
+      class="time-tap"
+      :class="activeId === i.id && 'active'"
+      @click="handleTimeSelct(i)"
+    >
+      {{ i.content }}
+    </div>
+    <el-date-picker
+      v-model="time"
+      type="daterange"
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      format="yyyy-MM-dd"
+      value-format="yyyy-MM-dd"
+    />
+  </div>
+</template>
+
+<script>
+import loopEchart from '../mixins/index'
+
+export default {
+  mixins: [loopEchart],
+  props: {
+    timeList: {
+      type: Array,
+      default: () => [
+        {
+          id: 1,
+          content: '昨天'
+        },
+        {
+          id: 2,
+          content: '近1月'
+        },
+        {
+          id: 3,
+          content: '近6月'
+        },
+        {
+          id: 4,
+          content: '近1年'
+        },
+        {
+          id: 0,
+          content: '全部'
+        }
+      ]
+    },
+    activeLinkId: {
+      type: Number,
+      default: 1
+    }
+  },
+  data() {
+    return {
+      time: [],
+      activeId: 1
+    }
+  },
+  watch: {
+    activeId: {
+      handler(e) {
+        const obj = {
+          1: () => this.getYesterDay(1),
+          2: () => this.getMonths(1),
+          3: () => this.getMonths(6),
+          4: () => this.getYears(1),
+          0: () => {
+            this.time = []
+          },
+          5: () => this.getWeeks(1)
+        }
+        this.time = e !== 0 ? [obj[e](), this.getDay()] : ['', '']
+      },
+      immediate: true
+    },
+    time: {
+      handler(e) {
+        this.$emit('handleSelectTime', e)
+      },
+      immediate: true
+    },
+    activeLinkId: {
+      handler(val) {
+        if (val) {
+          this.activeId = val
+        }
+      },
+      immediate: true
+    }
+  },
+  methods: {
+    handleTimeSelct($event) {
+      this.activeId = $event.id
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.time-select {
+  display: flex;
+  align-items: center;
+  padding: 0.5vw 0;
+  .time-tap {
+    margin-right: 1vw;
+    font-size: 14px;
+    color: #ccc;
+    cursor: pointer;
+    &.active {
+      color: rgb(0, 174, 255);
+    }
+  }
+}
+.el-picker-panel {
+  z-index: 9999999 !important;
+}
+</style>

+ 229 - 0
src/views/mall-portrait/index.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="report">
+    <select-time @handleSelectTime="handleSelectTime($event, 1)" />
+    <div class="ins_statistics">
+      <div class="content">
+        <pie-echarts :option-data="dataValue.clubCount.data" :title="'机构统计'">
+          <template #title>
+            <div class="label">机构统计</div>
+            <div class="nums">{{ dataValue.clubCount.count || 0 }}个</div>
+          </template>
+        </pie-echarts>
+      </div>
+      <div class="content">
+        <pie-dub-echarts :option-data1="dataValue.clubActivityCount.data" :option-data2="dataValue.clubActivityCount.data">
+          <template #title>
+            <div class="label">机构活跃统计</div>
+            <div class="nums">{{ dataValue.clubActivityCount.count || 0 }}个</div>
+          </template>
+          <template #echarts1>
+            <div class="e-title">活跃机构</div>
+            <div class="e-title">{{ dataValue.clubActivityCount.data['活跃机构'] || 0 }}个</div>
+          </template>
+          <template #echarts2>
+            <div class="e-title">不活跃机构</div>
+            <div class="e-title">{{ dataValue.clubActivityCount.data['不活跃机构'] || 0 }}个</div>
+          </template>
+        </pie-dub-echarts>
+      </div>
+    </div>
+    <select-time :time-list="timeList" :active-link-id="5" @handleSelectTime="handleSelectTime($event, 2)" />
+    <div class="ins_trends">
+      <line-echarts :option-data="dataValue.addClubCount.data" />
+      <line-echarts :echarts-name="'机构订单趋势'" :option-data="dataValue.clubOrder.data" />
+    </div>
+    <select-time @handleSelectTime="handleSelectTime($event, 3)" />
+    <div class="ins_deStatistics">
+      <div class="content">
+        <pie-dub-echarts :option-data1="dataValue.remarks.data" :option-data2="dataValue.remarks.data" :title="'咨询统计'">
+          <template #title>
+            <div class="label">咨询统计</div>
+            <div class="nums">{{ dataValue.remarks.count }}个</div>
+          </template>
+          <template #echarts1>
+            <div class="e-title">机构咨询</div>
+            <div class="e-title">{{ dataValue.remarks.data['机构咨询'] }}个</div>
+          </template>
+          <template #echarts2>
+            <div class="e-title">游客咨询</div>
+            <div class="e-title">{{ dataValue.remarks.data['游客咨询'] }}个</div>
+          </template>
+        </pie-dub-echarts>
+      </div>
+      <div class="content">
+        <pie-echarts :option-data="dataValue.record.data" :title="'访问统计'">
+          <template #title>
+            <div class="label">访问统计</div>
+            <div class="nums">{{ dataValue.record.count }}个</div>
+          </template>
+        </pie-echarts>
+      </div>
+    </div>
+    <div class="hot-content">
+      <hot-tips :columns="bandColumns" :hot-data="dataValue.brandProductSalesRecords">
+        <template #head>
+          <div class="label">热门品牌</div>
+          <div class="tips">销量排名前10的品牌</div>
+        </template>
+      </hot-tips>
+      <hot-tips :columns="storeColumns" :hot-data="dataValue.productSalesRecords">
+        <template #head>
+          <div class="label">热门商品</div>
+          <div class="tips">销量排名前10的商品</div>
+        </template>
+      </hot-tips>
+      <hot-tips :columns="keyColumns" :hot-data="dataValue.keywords">
+        <template #head>
+          <div class="label">热门搜索词</div>
+          <div class="tips">排名前10的搜索词</div>
+        </template>
+      </hot-tips>
+    </div>
+  </div>
+</template>
+
+<script>
+import SelectTime from './components/select-time'
+import PieEcharts from './components/pei-echarts.vue'
+import PieDubEcharts from './components/pie-dub-echarts.vue'
+import LineEcharts from './components/line-echarts.vue'
+import HotTips from './components/hot-tips.vue'
+import { getIns_statistics, getIns_orderTrends, getIns_accessStatistics } from '@/api/mallPortrait/disitrib'
+
+export default {
+  components: {
+    SelectTime,
+    PieEcharts,
+    PieDubEcharts,
+    LineEcharts,
+    HotTips
+  },
+  data() {
+    return {
+      bandColumns: [{ text: '品牌logo', label: 'image', labelWidth: 80 }, { text: '品牌名称', label: 'name' }, { text: 'TOP', label: 'top', labelWidth: 80 }],
+      storeColumns: [{ text: '商品图片', label: 'image', labelWidth: 80 }, { text: '商品名称', label: 'name' }, { text: 'TOP', label: 'top', labelWidth: 80 }],
+      keyColumns: [{ text: '搜索词', labelWidth: 120 }, { text: 'TOP', label: 'top' }],
+      dataValue: {
+        // 机构活跃度统计
+        clubActivityCount: {
+          data: {
+            '不活跃机构': 0,
+            '活跃机构': 0
+          }
+        },
+        // 机构统计
+        clubCount: {},
+        // 机构新增趋势
+        addClubCount: {},
+        // 机构订单趋势
+        clubOrder: {},
+        // 机构访问统计
+        record: {},
+        // 机构咨询统计
+        remarks: {
+          data: {
+            '游客咨询': 0,
+            '机构咨询': 0
+          }
+        },
+        // 热门品牌
+        brandProductSalesRecords: [],
+        // 热门商品
+        productSalesRecords: [],
+        // 热搜词
+        keywords: []
+      },
+      timeList: [{
+        id: 5,
+        content: '近一周'
+      },
+      {
+        id: 2,
+        content: '近1月'
+      },
+      {
+        id: 3,
+        content: '近6月'
+      },
+      {
+        id: 4,
+        content: '近1年'
+      }]
+    }
+  },
+  methods: {
+    /**
+     * 获取机构统计报表
+     */
+    async getIns_statistics(e) {
+      const { data } = await getIns_statistics({ startCreateTime: e[0], endCreateTime: e[1] })
+      this.dataValue.clubActivityCount = data.clubActivityCount
+      this.dataValue.clubCount = data.clubCount
+    },
+    async getIns_orderTrends(e) {
+      const { data } = await getIns_orderTrends({ startCreateTime: e[0], endCreateTime: e[1] })
+      this.dataValue.addClubCount = data.clubCount
+      this.dataValue.clubOrder = data.clubOrder
+    },
+    async getIns_accessStatistics(e) {
+      const { data } = await getIns_accessStatistics({ startCreateTime: e[0], endCreateTime: e[1] })
+      this.dataValue.record = data.record
+      this.dataValue.remarks = data.remarks
+      this.dataValue.brandProductSalesRecords = data.brandProductSalesRecords
+      this.dataValue.productSalesRecords = data.productSalesRecords
+      this.dataValue.keywords = data.keywords
+    },
+    handleSelectTime(e, num) {
+      const obj = {
+        1: () => this.getIns_statistics(e),
+        2: () => this.getIns_orderTrends(e),
+        3: () => this.getIns_accessStatistics(e)
+      }
+      obj[num]()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.report {
+  padding: 20px;
+  .ins_statistics {
+    display: grid;
+    align-items: center;
+    grid-template-columns: 1fr 1.5fr;
+    grid-gap: 1vw;
+    margin-bottom: 14px;
+  }
+  .ins_deStatistics {
+    display: grid;
+    align-items: center;
+    grid-template-columns: 1.5fr 1fr;
+    grid-gap: 1vw;
+    margin-bottom: 14px;
+  }
+}
+.label {
+  font-size: 16px;
+  font-weight: 600;
+  margin-right: 20px;
+}
+.nums {
+  font-size: 16px;
+  color: rgb(0, 174, 255);
+}
+.tips {
+  font-size: 14px;
+  color: #ccc;
+}
+.e-title {
+  font-size: 14px;
+  color: #ccc;
+  text-align: center;
+}
+.hot-content {
+  display: grid;
+  grid-template-columns: 2fr 2fr 1.5fr;
+  grid-gap: 14px;
+}
+</style>

+ 134 - 0
src/views/mall-portrait/mixins/index.js

@@ -0,0 +1,134 @@
+import moment from 'moment'
+
+export default {
+  data() {
+    return {
+      timeTicket: null,
+      currentIndex: -1
+    }
+  },
+  methods: {
+    handleChartLineLoop(option, myChart) {
+      if (!myChart) {
+        return
+      }
+      const selectPie = () => {
+        const dataLen = option.series[0].data.length
+        this.currentIndex = (this.currentIndex + 1) % dataLen
+        highlightLine()
+      }
+      this.timeTicket = setInterval(selectPie, 1500)
+
+      const highlightLine = () => {
+        // 遍历饼图数据,取消所有图形的高亮效果
+        for (const idx in option.series[0].data) {
+          myChart.dispatchAction({
+            type: 'downplay',
+            seriesIndex: 0,
+            dataIndex: idx
+          })
+        }
+        myChart.dispatchAction({
+          type: 'showTip',
+          seriesIndex: 0,
+          dataIndex: this.currentIndex
+        })
+        myChart.setOption(option)
+      }
+
+      this.handleMouseOver(myChart, () => {
+        clearInterval(this.timeTicket)
+      })
+
+      this.handleMouseOut(myChart, () => {
+        if (this.timeTicket) {
+          clearInterval(this.timeTicket)
+        }
+        this.timeTicket = setInterval(selectPie, 1500)
+      })
+    },
+
+    handleChartPieLoop(option, myChart, pieTimeTicket, currentIndex, showTips = false) {
+      if (!myChart) {
+        return
+      }
+      const selectPie = () => {
+        const dataLen = option.series[0].data.length
+        this[currentIndex] = (this[currentIndex] + 1) % dataLen
+        highlightPie()
+      }
+
+      this[pieTimeTicket] = setInterval(selectPie, 2000) // 设置自动切换高亮图形的定时器
+
+      // 取消所有高亮并高亮当前图形
+      const highlightPie = () => {
+        // 遍历饼图数据,取消所有图形的高亮效果
+        for (const idx in option.series[0].data) {
+          myChart.dispatchAction({
+            type: 'downplay',
+            seriesIndex: 0,
+            dataIndex: idx
+          })
+        }
+        // 高亮当前图形
+        myChart.dispatchAction({
+          type: 'highlight',
+          seriesIndex: 0,
+          dataIndex: this[currentIndex]
+        })
+        showTips && myChart.dispatchAction({
+          type: 'showTip',
+          seriesIndex: 0,
+          dataIndex: this[currentIndex]
+        })
+      }
+      myChart.on('mouseover', (params) => {
+        clearInterval(this[pieTimeTicket])
+        this[currentIndex] = params.dataIndex
+        highlightPie()
+      })
+
+      // 用户鼠标移出时,重新开始自动切换
+      myChart.on('mouseout', () => {
+        if (this[pieTimeTicket]) {
+          clearInterval(this[pieTimeTicket])
+        }
+        this[pieTimeTicket] = setInterval(selectPie, 2000)
+      })
+
+      this.handleMouseOver(myChart, () => {
+        clearInterval(this[pieTimeTicket])
+      })
+
+      this.handleMouseOut(myChart, () => {
+        if (this[pieTimeTicket]) {
+          clearInterval(this[pieTimeTicket])
+        }
+        this[pieTimeTicket] = setInterval(selectPie, 2000)
+      })
+    },
+    /**
+     * @returns 获取今天的时间
+     */
+    getDay() {
+      return moment().format('YYYY-MM-DD')
+    },
+    /**
+     * 获取从今天开始指定天数
+     * @param {Number} num
+     * @returns string
+     */
+    getYesterDay(num) {
+      return moment().subtract(num, 'days').format('YYYY-MM-DD')
+    },
+    getMonths(num) {
+      return moment().subtract(num, 'months').format('YYYY-MM-DD')
+    },
+    getYears(num) {
+      return moment().subtract(num, 'years').format('YYYY-MM-DD')
+    },
+    getWeeks(num) {
+      return moment().subtract(num, 'weeks').format('YYYY-MM-DD')
+    }
+  }
+}

+ 35 - 8
src/views/user/customer/advertis-list.vue

@@ -20,6 +20,18 @@
           <el-option :value="1" label="已下架" />
         </el-select>
       </div>
+      <div class="filter-control">
+        <span>点击日期:</span>
+        <el-date-picker
+          v-model="timesValue"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          format="yyyy-MM-dd"
+          value-format="yyyy-MM-dd"
+        />
+      </div>
       <div class="filter-control">
         <el-button type="primary" @click="getList"> 查询 </el-button>
         <el-button type="primary" @click="handleoOerate('add')"> 添加 </el-button>
@@ -68,6 +80,11 @@
           <el-input v-model="row.sort" maxlength="4" minlength="1" @blur="handleOnInputBlur(row)" />
         </template>
       </el-table-column>
+      <el-table-column label="点击量" width="80" align="center">
+        <template slot-scope="{ row }">
+          {{ row.pv }}
+        </template>
+      </el-table-column>
       <el-table-column prop="addTime" label="添加时间" align="center">
         <template slot-scope="{ row }">
           {{ row.addTime ? row.addTime : '---' }}
@@ -125,7 +142,9 @@ const defaultListQuery = {
   shopName: '', // 供应商名称
   status: '', // 上下架状态 0 已上架 1 已下架
   pageNum: 1,
-  pageSize: 20
+  pageSize: 20,
+  startPvCreateTime: '',
+  endPvCreateTime: ''
 }
 export default {
   name: 'AdvertisList',
@@ -137,11 +156,19 @@ export default {
       time: '',
       listQuery: Object.assign({}, defaultListQuery),
       list: [],
-      total: 0
+      total: 0,
+      timesValue: []
     }
   },
   computed: {},
-  watch: {},
+  watch: {
+    timesValue: {
+      handler() {
+        this.getList()
+      },
+      immediate: true
+    }
+  },
   created() {
     this.getList()
   },
@@ -151,12 +178,12 @@ export default {
     getList() {
       this.listQuery.pageNum = 1
       this.list = []
-      if (this.time && this.time.length > 0) {
-        this.listQuery.startTime = this.time[0]
-        this.listQuery.endTime = this.time[1]
+      if (this.timesValue && this.timesValue.length > 0) {
+        this.listQuery.startPvCreateTime = this.timesValue[0]
+        this.listQuery.endPvCreateTime = this.timesValue[1]
       } else {
-        this.listQuery.startTime = ''
-        this.listQuery.endTime = ''
+        this.listQuery.startPvCreateTime = ''
+        this.listQuery.endPvCreateTime = ''
       }
       this.getShopAdvertisingImage()
     },