浏览代码

feat: data panel

chenlei 1 年之前
父节点
当前提交
ebd6f381a3
共有 9 个文件被更改,包括 574 次插入2 次删除
  1. 1 0
      package.json
  2. 1 1
      public/static/css/main.css
  3. 12 0
      src/api.js
  4. 二进制
      src/assets/images/img_person.png
  5. 二进制
      src/assets/images/img_viewer.png
  6. 二进制
      src/assets/images/img_vr.png
  7. 514 0
      src/components/DataPanel.vue
  8. 26 1
      src/views/gui/UserInfo.vue
  9. 20 0
      yarn.lock

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
     "animate.css": "^4.1.1",
     "axios": "^1.1.3",
     "core-js": "^3.8.3",
+    "echarts": "^5.4.3",
     "element-ui": "^2.15.6",
     "html2canvas": "^1.3.3",
     "install": "^0.13.0",

+ 1 - 1
public/static/css/main.css

@@ -3405,7 +3405,7 @@ a.hasHover:hover, a:active {
     bottom: 0px;
     width: 100%;
     transition: all .5s;
-    z-index: 9998;
+    z-index: 1000;
     position: absolute;
 }
 

+ 12 - 0
src/api.js

@@ -132,4 +132,16 @@ export default {
     })
     return res.data.data
   },
+  async getReportCount(data) {
+    const lastToken = localStorage.getItem('token')
+    const res = await axios({
+      method: 'get',
+      url: `${process.env.VUE_APP_API_PREFIX}/api/cms/report/count`,
+      params: data,
+      headers: {
+        token: lastToken,
+      }
+    })
+    return res.data
+  },
 }

二进制
src/assets/images/img_person.png


二进制
src/assets/images/img_viewer.png


二进制
src/assets/images/img_vr.png


+ 514 - 0
src/components/DataPanel.vue

@@ -0,0 +1,514 @@
+<template>
+  <el-dialog
+    class="data-panel-dialog"
+    :visible.sync="show"
+    width="85vw"
+    append-to-body
+    top="0"
+    :show-close="false"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+  >
+    <div
+      v-loading="loading"
+      class="data-panel"
+    >
+      <div class="data-panel__header">
+        <div>
+          <p>使用统计</p>
+          <el-select
+            v-model="num"
+            clearable
+            @change="getDetail"
+          >
+            <el-option
+              v-for="item in options"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <!-- <el-date-picker
+            v-model="date"
+            type="daterange"
+            align="left"
+            unlink-panels
+            value-format="YYYY-mm-dd"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            :picker-options="pickerOptions"
+          /> -->
+        </div>
+        <img
+          width="30"
+          src="@/assets/images/close.png"
+          @click="show = false"
+        >
+      </div>
+
+      <el-row
+        class="data-panel__panels"
+        :gutter="20"
+      >
+        <el-col :span="8">
+          <div class="data-panel-p a">
+            <div>
+              <p>{{ detail?.sceneUser }}</p>
+              <span>线上展厅访客数(人)</span>
+            </div>
+            <img src="@/assets/images/img_person.png">
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div class="data-panel-p b">
+            <div>
+              <p style="color: #83C0C6">
+                {{ detail?.scenePcs }}
+              </p>
+              <span>线上展厅访问数(次)</span>
+            </div>
+            <img src="@/assets/images/img_viewer.png">
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div class="data-panel-p c">
+            <div>
+              <p style="color: #C3BC94">
+                {{ detail?.deviceStart }}
+              </p>
+              <span>穿戴设备体验数(次)</span>
+            </div>
+            <img src="@/assets/images/img_vr.png">
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row
+        :gutter="20"
+        style="margin-top: 28px;"
+      >
+        <el-col :span="8">
+          <div class="rating">
+            <div id="rating-echart" />
+          </div>
+        </el-col>
+        <el-col :span="16">
+          <div class="average">
+            <p style="top: 29px">
+              <span>线上展厅评价数</span>{{ detail?.sceneComment.total }}
+            </p>
+            <p style="top: 57px">
+              <span>平均评分</span>{{ detail?.sceneComment.avg }}
+            </p>
+            <div id="average-echart" />
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import * as echarts from "echarts"
+import api from '@/api'
+
+export default {
+  props: {
+    visible: Boolean
+  },
+  data() {
+    // const defaultEnd = new Date()
+    // const defaultStart = new Date()
+    // defaultStart.setTime(defaultStart.getTime() - 3600 * 1000 * 24 * 7)
+
+    return {
+      loading: false,
+      ratingChat: null,
+      averageChat: null,
+      num: 7,
+      options: [{
+        label: '当日',
+        value: 1
+      }, {
+        label: '近七日',
+        value: 7
+      }, {
+        label: '近三十日',
+        value: 30
+      }],
+      detail: null,
+      // date: [defaultStart, defaultEnd],
+      // pickerOptions: {
+      //   disabledDate(time) {
+      //     return time.getTime() > Date.now()
+      //   },
+      //   shortcuts: [{
+      //     text: '今日',
+      //     onClick(picker) {
+      //       picker.$emit('pick', [new Date(), new Date()])
+      //     },
+      //   }, {
+      //     text: '最近一周',
+      //     onClick(picker) {
+      //       const end = new Date()
+      //       const start = new Date()
+      //       start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+      //       picker.$emit('pick', [start, end])
+      //     }
+      //   }, {
+      //     text: '最近一个月',
+      //     onClick(picker) {
+      //       const end = new Date()
+      //       const start = new Date()
+      //       start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+      //       picker.$emit('pick', [start, end])
+      //     }
+      //   }]
+      // },
+    }
+  },
+  computed: {
+    show: {
+      get() {
+        return this.visible
+      },
+      set(v) {
+        this.$emit('update:visible', v)
+      }
+    }
+  },
+  watch: {
+    show(v) {
+      if (v) {
+        this.getDetail()
+      } else {
+        this.disposeChat()
+      }
+    }
+  },
+  methods: {
+    disposeChat() {
+      this.ratingChat && this.ratingChat.dispose()
+      this.averageChat && this.averageChat.dispose()
+      this.ratingChat = null
+      this.averageChat = null
+    },
+    async getDetail() {
+      try {
+        this.loading = true
+        this.disposeChat()
+        const { data } = await api.getReportCount({
+          num: this.num
+        })
+        this.detail = data
+
+        this.$nextTick(() => {
+          this.initRatingChart()
+          this.initAverageChat()
+        })
+      } finally {
+        this.loading = false
+      }
+    },
+    initAverageChat() {
+      if (this.averageChat) return
+
+      this.averageChat = echarts.init(document.getElementById("average-echart"))
+
+      this.averageChat.setOption({
+        xAxis: {
+          data: [
+            {
+              value: '一星',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F',
+                padding: [10, 0, 0, 0]
+              }
+            },
+            {
+              value: '二星',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F',
+                padding: [10, 0, 0, 0]
+              }
+            },
+            {
+              value: '三星',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F',
+                padding: [10, 0, 0, 0]
+              }
+            },
+            {
+              value: '四星',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F',
+                padding: [10, 0, 0, 0]
+              }
+            },
+            {
+              value: '五星',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F',
+                padding: [10, 0, 0, 0]
+              }
+            }
+          ],
+          axisLine: {
+            lineStyle: {
+              color: '#A4573F'
+            }
+          },
+        },
+        yAxis: {
+          axisLabel: {
+            show: false
+          },
+          splitLine: {
+            show: false
+          },
+        },
+        series: [
+          {
+            type: 'bar',
+            data: [
+              {
+                value: this.detail.sceneComment[1],
+                itemStyle: {
+                  color: '#9A2D0A'
+                },
+              },
+              {
+                value: this.detail.sceneComment[2],
+                itemStyle: {
+                  color: '#A4573F'
+                }
+              },
+              {
+                value: this.detail.sceneComment[3],
+                itemStyle: {
+                  color: '#FBC63E'
+                }
+              },
+              {
+                value: this.detail.sceneComment[4],
+                itemStyle: {
+                  color: '#E1D3AD'
+                }
+              },
+              {
+                value: this.detail.sceneComment[5],
+                itemStyle: {
+                  color: '#C7EC7A'
+                }
+              }
+            ],
+            barWidth: '20%',
+            label: {
+              show: true,
+              position: 'top',
+              textStyle: {
+                fontSize: 20,
+                color: '#A4573F'
+              }
+            }
+          }
+        ]
+      })
+    },
+    initRatingChart() {
+      if (this.ratingChat) return
+
+      this.ratingChat = echarts.init(document.getElementById("rating-echart"))
+
+      this.ratingChat.setOption({
+        title: [
+          {
+            text: this.detail.deviceComment.total,
+            textStyle: {
+              color: '#583329',
+              fontSize: 85,
+              fontWeight: 'bold'
+            },
+            left: 'center',
+            bottom: 190
+          },
+          {
+            text: '穿戴设备评价数',
+            textStyle: {
+              color: '#583329',
+              fontSize: 17,
+              fontWeight: 'bold',
+            },
+            left: 'center',
+            bottom: 170
+          }
+        ],
+        series: [
+          {
+            type: 'pie',
+            data: [
+              {
+                value: this.detail.deviceComment.bad,
+                name: '差评',
+                label: {
+                  color: '#583329',
+                }
+              },
+              {
+                value: this.detail.deviceComment.good,
+                name: '好评',
+                label: {
+                  color: '#3C5A53',
+                },
+              },
+            ],
+            color: ['#FF9878', '#7DEED3'],
+            radius: ['45%', '55%'],
+            label: {
+              normal: {
+                fontSize: '20',
+                fontWeight: 'bold',
+                formatter: '{b}{d}%',
+              }
+            },
+          },
+          {
+            type: 'pie',
+            cursor: 'default',
+            hoverAnimation: false,
+            legendHoverLink: false,
+            silent: true,
+            labelLine: {
+              normal: {
+                show: false
+              }
+            },
+            radius: '45%',
+            data: [{
+              value: 0,
+              itemStyle: {
+                normal: {
+                  color: '#FCF7F2'
+                }
+              }
+            }]
+          }
+        ]
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.data-panel {
+  padding: 28px 43px 48px;
+  background: #EFE9E3;
+  border-radius: 5px;
+
+  &__header {
+    display: flex;
+    align-items: center;
+
+    > div {
+      flex: 1;
+      display: flex;
+      align-items: center;
+
+      p {
+        margin-right: 20px;
+        font-size: 18px;
+        color: #A4573F;
+        line-height: 58px;
+        font-weight: bold;
+      }
+    }
+    > img {
+      cursor: pointer;
+    }
+  }
+  &__panels {
+    margin-top: 36px;
+  }
+  &-p {
+    position: relative;
+    padding: 13px 27px;
+    height: 213px;
+    background: white;
+    border-radius: 5px;
+    box-sizing: border-box;
+    box-shadow: 0px 0px 27px 0px rgba(102,64,64,0.1);
+
+    img {
+      position: absolute;
+      right: 0;
+      bottom: 0;
+    }
+    > div {
+      position: relative;
+      z-index: 1;
+    }
+    p {
+      font-size: 96px;
+      font-family: Source Han Sans CN, Source Han Sans CN;
+      font-weight: bold;
+      color: #A4573F;
+      line-height: 144px;
+    }
+    span {
+      font-size: 20px;
+      font-family: Source Han Sans CN, Source Han Sans CN;
+      font-weight: bold;
+      color: #583329;
+      line-height: 23px;
+    }
+  }
+  .rating,
+  .average {
+    position: relative;
+    height: 454px;
+    background: white;
+  }
+  .average {
+    p {
+      position: absolute;
+      left: 36px;
+      font-size: 14px;
+
+      span {
+        display: inline-block;
+        width: 130px;
+      }
+    }
+  }
+  #rating-echart,
+  #average-echart {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+  #average-echart {
+    top: 60px;
+  }
+}
+</style>
+
+<style lang="less">
+.data-panel-dialog {
+  .el-dialog__header {
+    padding: 0;
+  }
+  .el-date-editor .el-range__icon {
+    color: #A4573F;
+  }
+}
+</style>

+ 26 - 1
src/views/gui/UserInfo.vue

@@ -3,6 +3,7 @@
     <div
       v-if="!isMobile"
       class="pc-wrapper"
+      :style="{ width: isAdmin ? '168px' : '158px', height: isAdmin ? '547px' : '514px' }"
     >
       <div
         class="top-wrapper"
@@ -125,6 +126,17 @@
           分享成绩
         </div>
       </button>
+      <button
+        v-if="isAdmin"
+        style="margin-top: 10px;"
+      >
+        <div
+          class="text-wrapper"
+          @click="dataVisible = true"
+        >
+          数据统计
+        </div>
+      </button>
     </div>
     <div
       v-if="isMobile"
@@ -147,16 +159,26 @@
         >
       </button>
     </div>
+
+    <DataPanel
+      v-if="isAdmin"
+      :visible.sync="dataVisible"
+    />
   </div>
 </template>
 
 <script>
 import globalApi from "@/api"
+import DataPanel from '@/components/DataPanel.vue'
 
 export default {
+  components: {
+    DataPanel
+  },
   data() {
     return {
       isPulledDown: false,
+      dataVisible: false
     }
   },
   computed: {
@@ -171,6 +193,9 @@ export default {
       'badgeHistoryForShow',
       'badgeProtectorForShow',
     ]),
+    isAdmin() {
+      return this.userInfo.isAdmin === 1
+    },
     badgeArchBgStyle() {
       if (this.badgeArchForShow === this.badgeArchGoal) {
         return 'transparent'
@@ -351,7 +376,7 @@ export default {
           color: #A26751;
         }
       }
-      &:last-of-type {
+      &:not(:first-of-type) {
         > .text-wrapper {
           height: 100%;
           width: 100%;

+ 20 - 0
yarn.lock

@@ -2827,6 +2827,14 @@ easy-stack@1.0.1:
   resolved "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz"
   integrity sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==
 
+echarts@^5.4.3:
+  version "5.4.3"
+  resolved "https://registry.npmjs.org/echarts/-/echarts-5.4.3.tgz#f5522ef24419164903eedcfd2b506c6fc91fb20c"
+  integrity sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.4.4"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz"
@@ -5855,6 +5863,11 @@ tr46@~0.0.3:
   resolved "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz"
   integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
 
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
 tslib@^1.10.0:
   version "1.14.1"
   resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz"
@@ -6391,3 +6404,10 @@ yorkie@^2.0.0:
     is-ci "^1.0.10"
     normalize-path "^1.0.0"
     strip-indent "^2.0.0"
+
+zrender@5.4.4:
+  version "5.4.4"
+  resolved "https://registry.npmjs.org/zrender/-/zrender-5.4.4.tgz#8854f1d95ecc82cf8912f5a11f86657cb8c9e261"
+  integrity sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==
+  dependencies:
+    tslib "2.3.0"