snow 2 anos atrás
pai
commit
9895b97678
4 arquivos alterados com 852 adições e 2 exclusões
  1. 2 2
      package.json
  2. 13 0
      src/api/index.js
  3. 7 0
      src/router/index.js
  4. 830 0
      src/views/NMedia/Overview.vue

+ 2 - 2
package.json

@@ -3,8 +3,8 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "serve": "set NODE_OPTIONS=--openssl-legacy-provider & vue-cli-service serve",
-    "build": "set NODE_OPTIONS=--openssl-legacy-provider & vue-cli-service build & node ./saveGit.js",
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build & node ./saveGit.js",
     "lint": "vue-cli-service lint",
     "dll": "webpack --progress --config ./webpack.dll.js"
   },

+ 13 - 0
src/api/index.js

@@ -1054,3 +1054,16 @@ export function getChannlePage(data) {
     data,
   });
 }
+
+/**
+ * 获取大数据平台概览数据;
+ * @return {AxjxPromise}
+ */
+export function getOverviewPage(data) {
+  return ajax({
+    urlType: "leverAudience",
+    url: "/cxzx-program/new-media/overview",
+    method: "POST",
+    data,
+  });
+}

+ 7 - 0
src/router/index.js

@@ -184,6 +184,13 @@ const routes = [
     component: () =>
       import(/* webpackChunkName: "Key" */ "../views/Key.vue"),
   },
+  //   新媒体概览
+  {
+    path: "/newMediaOverview",
+    name: "newMediaOverview",
+    component: () =>
+      import(/* webpackChunkName: "newMediaOverview" */ "../views/NMedia/Overview.vue"),
+  },
 
   countryRouter, // 全国数据
 ];

+ 830 - 0
src/views/NMedia/Overview.vue

@@ -0,0 +1,830 @@
+<template>
+  <el-main>
+    <el-row :gutter="20">
+        <el-col :span="10"><div class="grid-content bg-purple">
+            <el-card v-if='overview.userOverview' class="user-overview">
+              <el-row type="flex">
+                <el-col :span="6" class="overview-item">本月新用户数</el-col>
+                <el-col :span="4" class="overview-item"><countTo
+                        :startVal="overview.userOverview.newUser || 0"
+                        :endVal="overview.userOverview.newUser || 0"
+                        :duration="100"
+                      ></countTo></el-col>
+                <el-col :span="2" class="overview-item">人</el-col>
+              </el-row>
+              <el-row type="flex">
+                <el-col :span="6" class="overview-item">累计用户数</el-col>
+                <el-col :span="4" class="overview-item"><countTo
+                        :startVal="overview.userOverview.totalUser || 0"
+                        :endVal="overview.userOverview.totalUser || 0"
+                        :duration="100"
+                      ></countTo></el-col>
+                <el-col :span="2" class="overview-item">人</el-col>
+              </el-row>
+
+            </el-card>
+            <el-card class="margin-10">
+                <div>
+                    <h3>用户数排行TOP5</h3>
+                    <el-table
+                        :data="overview.areaTop5 || []"
+                        style="width: 100%"
+                        :header-cell-style="{
+                          backgroundColor: '#f4f5f7',
+                          color: '#606266',
+                        }"
+                      >
+                      <el-table-column
+                      prop="appVersion"
+                      header-align="center"
+                      align="center"
+                      label="地市"
+                      show-overflow-tooltip
+                    >
+                      <template #default="scope">
+                        <span v-text="scope.row.city || '-'"></span>
+                      </template>
+                    </el-table-column>
+                    <el-table-column
+                      label="用户数"
+                      header-align="center"
+                      align="center"
+                      prop="startTimes"
+                    >
+                      <template #default="scope">
+                        <countTo
+                          :startVal="scope.row.userSum || 0"
+                          :endVal="scope.row.userSum || 0"
+                          :duration="100"
+                        ></countTo>
+                      </template>
+                    </el-table-column>
+                    </el-table>
+
+                </div>
+                
+            </el-card>
+
+            <el-card  class="margin-10">
+                <div class="cardtit"><h3>用户按应用分布</h3></div>
+                <div ref="platformChartEle"></div>
+            </el-card>
+        </div></el-col>
+        <el-col :span="14">
+            <div >
+                <el-card>
+                    <div style="position: relative">
+            <div style="position: absolute; top: 10px; left: 10px">
+              <div class="item"><h3>地域分布</h3></div>
+             
+            </div>
+            <div ref="regionChart"></div>
+          </div>
+                </el-card> 
+            </div>
+        </el-col>
+    </el-row>
+    <el-row>
+      <el-col :span="24">
+        <el-card class="margin-10">
+          <div><h3>昨日比对</h3></div>
+      <div ref="appUserChart"></div>
+    </el-card>
+      </el-col>
+      
+    </el-row>
+
+    <el-row>
+            <el-card class="card-100 margin-10">
+      <div><h3>活跃用户留存</h3></div>
+      <el-table
+        v-if="overview.keep"
+        :data="overview.keep"
+        style="width: 100%"
+        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
+      >
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="dt"
+          label="日期"
+        />
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r0"
+          label="活跃用户数"
+        >
+          <template #default="scope">
+            <countTo
+              :startVal="scope.row.r0"
+              :endVal="scope.row.r0"
+              :duration="100"
+            ></countTo>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r1"
+          label="第1天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r1
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r1"
+                :endVal="scope.row.r1"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r2"
+          label="第2天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r2
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r2"
+                :endVal="scope.row.r2"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r3"
+          label="第3天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r3
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r3"
+                :endVal="scope.row.r3"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r4"
+          label="第4天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r4
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r4"
+                :endVal="scope.row.r4"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r5"
+          label="第5天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r5
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r5"
+                :endVal="scope.row.r5"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r6"
+          label="第6天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r6
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r6"
+                :endVal="scope.row.r6"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r7"
+          label="第7天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r7
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r7"
+                :endVal="scope.row.r7"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r15"
+          label="第15天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r15
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r15"
+                :endVal="scope.row.r15"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          align="center"
+          prop="r30"
+          label="第30天"
+        >
+          <template #default="scope">
+            <div
+              class="keepNmuber"
+              :style="
+                scope.row.r30
+                  ? 'background-color: #69c0ff;color: #fff'
+                  : 'background-color: #ebf0ff;color: #ddd'
+              "
+            >
+              <countTo
+                :startVal="scope.row.r30"
+                :endVal="scope.row.r30"
+                :duration="100"
+              ></countTo>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+    </el-row>
+    <el-row>
+      <el-col :span="24">
+        <el-card class="margin-10">
+          <div><h3>累计用户</h3></div>
+      <div ref="appTotalChart"></div>
+    </el-card>
+      </el-col>
+      
+    </el-row>
+  </el-main>
+</template>
+
+<script>
+import china from "../../assets/map/100000_full.json";
+import shanxi from "../../assets/map/610000_shaanxi.json";
+import countTo from "@/components/counto/vue-countTo.vue";
+import {
+    getRule,
+    getRegionChart,
+    getSearchData,
+    getOverviewPage,
+} from "@/api/index";
+import * as echarts from "echarts";
+
+// import config from "@/config/index";
+let chart = undefined;
+export default {
+    name: "region",
+    data() {
+        return {
+            lastParams: {},
+            table: [],
+            form: {
+         
+                cityType: "1",
+            },
+            cycle: [],
+            item: {},
+            version: [],
+            channel: [],
+            overview:{}
+        };
+    },
+    async mounted() {
+        if (echarts) {
+            echarts.registerMap("China", china);
+            echarts.registerMap("ShanXi", shanxi);
+        }
+        
+        this.onSubmit();
+    },
+    computed: {},
+    methods: {
+    verifyList(list, verify, obj, more) {
+      if (!obj) return;
+      let li = list || [];
+      const out = [];
+      more && out.push({ value: -1, label: "不限" });
+      for (let i = 0; i < li.length; i++) {
+        const v = li[i];
+        if (verify.length !== 0 && !verify[v.mcode]) continue;
+        out.push({
+          value: v[obj.value],
+          label: v[obj.label],
+        });
+      }
+      return out;
+    },
+    changeTabs() {
+      this.onSubmit();
+    },
+    onSubmit() {
+      
+      getOverviewPage()
+        .then(r => {
+            this.overview = r
+            this.overview.areaTop5 = r.area.slice(0,5)
+            console.log(r)
+          if (!this.$refs.regionChart) return;
+          let list = r.area || [];
+          chart && chart.clear();
+          this.lastParams.cityType = this.form.cityType;
+          this.table = list;
+          for (let i = 0; i < list.length; i++) {
+            const v = list[i];
+            if (v.city === "西安市" || v.province === "陕西省") {
+              this.item = {
+                name: v.city === "西安市" ? "西安市" : "陕西省",
+                value: v.userSum,
+                distribution: (v.distribution * 100).toFixed(2) - 0 + "%",
+                userSum: v.userSum,
+              };
+              break;
+            }
+          }
+          this.createImage(list);
+          this.platformChar(r.appTrend);
+          this.appUserImage(r.compare, 'appUserChart', 'active', 'recMinute');
+          this.appUserImage(r.userTotal, 'appTotalChart', 'totalUser', 'dt');
+          chart && chart.hideLoading();
+        })
+        .catch(() => {
+          chart && chart.clear() && chart.hideLoading();
+        });
+    },
+    createImage(list) {
+      if (!chart) {
+        chart = echarts.init(this.$refs.regionChart);
+        window.onresize = chart.resize;
+      }
+      chart.clear();
+      chart.resize({
+        height: this.$refs.regionChart.offsetWidth * 1,
+      });
+      //   let min = list[list.length - 1].userSum;
+      const p = {
+        tooltip: {
+          confine: true,
+          trigger: "item",
+          formatter: function (e) {
+            if (e.value) return e.name + "<br />" + (e.value || "");
+            return "";
+          },
+        },
+        visualMap: {
+          //   min,
+          //   max: list[0].userSum,
+          //   realtime: false,
+          //   calculable: true,
+          //   inRange: {
+          //     color: ["lightskyblue", "yellow", "orangered"],
+          //   },
+          type: "piecewise",
+          pieces: [
+            {min: 0, max: 500, label: "活跃用户数< 5000", color: '#02a7f0'},
+            {max: 1000, min: 500, label: "500<=活跃用户数< 1000", color: '#95f204'},
+            {min: 1000, max: 2500, label: "1000<=活跃用户数< 2500", color: '#ffff00'},
+            {min: 2500, max: 5000, label: "2500<= 活跃用户数< 5000", color: '#f59a23'},
+            {min: 5000, label: "活跃用户数>= 5000", color: '#d9001b'},
+          ],
+        },
+        series: [
+          {
+            type: "map",
+            map: this.lastParams.cityType === "1" ? "ShanXi" : "China",
+            label: {
+              show: true,
+            },
+            data: list.map(v => {
+              return {
+                name: this.lastParams.cityType === "1" ? v.city : v.province,
+                value: v.userSum,
+                distribution: (v.distribution * 100).toFixed(2) - 0 + "%",
+                userSum: v.userSum,
+              };
+            }),
+          },
+        ],
+      };
+      chart.setOption(p);
+      const _this = this;
+      chart.on("click", function (params) {
+        _this.item = params.data || {};
+      });
+    },
+
+    appUserImage(compare, chartName, valueKey, xKey) {
+      let keyList = [], valueList=[], titles = [];
+      const c = compare || {};
+      for (let index = 0; index < (Object.keys(c) || []).length; index++) {
+          const v = (Object.keys(c) || [])[index];
+          valueList.push((c[v] || []).map(v => v[valueKey]));
+          titles.push(v);
+          if (!keyList.length) keyList = (c[v] || []).map(v => v[xKey]);
+        }
+    
+        let chart = echarts.init(this.$refs[chartName]);
+ 
+      
+      chart.resize({
+        height: 400,
+      });
+      chart.clear();
+      chart.setOption({
+        tooltip: {
+          confine: true,
+          trigger: "axis",
+        },
+       
+        legend: {
+          data: titles,
+        },
+        grid: {
+          left: "3%",
+          right: "4%",
+          bottom: "3%",
+          containLabel: true,
+        },
+        xAxis: {
+          type: "category",
+          boundaryGap: true,
+          data: keyList,
+        },
+        yAxis: {
+          type: "value",
+          scale: true,
+          minInterval: 1,
+          axisLabel: {
+            formatter(v) {
+              let val = v;
+              if (val >= 100000000) {
+                val = (val / 100000000).toFixed(2) + "亿";
+              } else if (val >= 10000) {
+                val = (val / 10000).toFixed(2) + "万";
+              }
+              return val;
+            },
+          },
+        },
+        series: valueList.map((v, i) => {
+          return {
+            name: titles[i],
+            data: v,
+            symbolSize: 0,
+            lineStyle: {
+              width: 1,
+            },
+            type: "line",
+            smooth: true,
+          };
+        }),
+      });
+    },
+
+    platformChar(ls) {
+      let lineH = 550;
+      this.chart2 = echarts.init(this.$refs.platformChartEle);
+      this.chart2.resize({
+        height: lineH,
+      });
+      const val = [];
+      ls.map(v => {
+        val.push({
+          name: v['app'],
+          value: v['data'],
+        });
+      });
+      this.chart2.setOption({
+        tooltip: {
+          confine: true,
+          trigger: "item",
+          formatter: v => {
+            return v.name + ":" + this.numform(v.value);
+          },
+        },
+        legend: {
+          show: false,
+        },
+        series: [
+          {
+            type: "pie",
+            radius: [0, 160],
+            left: "center",
+            width: 700,
+            itemStyle: {
+              borderColor: "#fff",
+              borderWidth: 1,
+            },
+            label: {
+              alignTo: "edge",
+              formatter: "{name|{b}}\n{time|{d} %}",
+              minMargin: 5,
+              edgeDistance: 10,
+              lineHeight: 18,
+              rich: {
+                time: {
+                  fontSize: 14,
+                  color: "#999",
+                },
+                name: {
+                  fontSize: 14
+                }
+              },
+            },
+            labelLine: {
+              length: 15,
+              length2: 0,
+              maxSurfaceAngle: 80,
+            },
+    
+            data: val,
+          },
+        ],
+      });
+    },
+    numform(n) {
+      let num = n;
+      if (isNaN(n)) num = "0";
+      else if (n >= 100000000)
+        num =
+          ((n / 100000000).toFixed(2) - 0 + "").replace(
+            /\B(?=(?:\d{3})+\b)/g,
+            ","
+          ) + "亿";
+      else if (n >= 10000)
+        num =
+          ((n / 10000).toFixed(2) - 0 + "").replace(
+            /\B(?=(?:\d{3})+\b)/g,
+            ","
+          ) + "万";
+      else num = (num + "").replace(/\B(?=(?:\d{3})+\b)/g, ",");
+      return num;
+    },
+   
+    disabledDate(time) {
+      const first = new Date("2021-06-21 00:00:00");
+      return (
+        time.getTime() > Date.now() - 86400000 ||
+        time.getTime() < first.getTime()
+      );
+    },
+    FormData(date) {
+      const d = new Date(date || Date.now() - 86400000);
+      const year = d.getFullYear();
+      const month =
+        d.getMonth() <= 8 ? "0" + (d.getMonth() + 1) : d.getMonth() + 1;
+      const day = d.getDate() <= 9 ? "0" + d.getDate() : d.getDate();
+      return [year, month, day].join("-");
+    },
+    timeFormat(t) {
+      const Time = t || 0;
+      const mH = Time % 3600;
+      let hour = (Time - mH) / 3600;
+      let min = (mH - (mH % 60)) / 60;
+      let son = Number(mH % 60).toFixed(0);
+      hour = hour <= 9 ? "0" + hour : hour;
+      min = min <= 9 ? "0" + min : min;
+      son = son <= 9 ? "0" + son : son;
+      let out = [];
+      if (hour * 1 > 0) out.push(hour);
+      out.push(...[min, son]);
+      return out.join(":");
+    },
+    change() {
+      const roleid = JSON.parse(
+        window.parent.localStorage.userinfo || "{}"
+      ).roleid;
+      const appV = {
+        length: 0,
+      };
+      getRule({
+        db: "authplat",
+        exportMark: "0",
+        menuid: 399,
+        roleid,
+      }).then(rule => {
+        let prvList = rule.output.data.prvRolectrl || [];
+        for (let i = 0; i < prvList.length; i++) {
+          const v = prvList[i];
+          if (v.controlid == "APP_VERSION")
+            (appV[v.detid] = true), (appV.length = appV.length + 1);
+        }
+        getSearchData({
+          gcode: "APP_VERSION",
+          source: this.form.app,
+        }).then(r => {
+          let version = [
+            {
+              label: "不限",
+              value: -1,
+            },
+          ];
+          r.map(v => {
+            if ((appV.length && appV[v.mcode]) || appV.length === 0)
+              version.push({
+                value: v.mname,
+                label: v.mname,
+              });
+          });
+          this.version = version;
+          this.form = {
+            ...this.form,
+            version: [-1],
+          };
+        });
+      });
+    },
+    changeversion(v) {
+      if (!v.length) return (this.form.version = [-1]);
+      const last = v[v.length - 1];
+      if (last == -1) return (this.form.version = [-1]);
+      let ver = [];
+      for (let i = 0; i < v.length; i++) {
+        const element = v[i];
+        if (element == -1) continue;
+        ver.push(element);
+      }
+      this.form.version = ver;
+    },
+    changechannel(v) {
+      if (!v.length) return (this.form.channel = [-1]);
+      const last = v[v.length - 1];
+      if (last == -1) return (this.form.channel = [-1]);
+      let ver = [];
+      for (let i = 0; i < v.length; i++) {
+        const element = v[i];
+        if (element == -1) continue;
+        ver.push(element);
+      }
+      this.form.channel = ver;
+    },
+    onExport() {
+      const S = this.lastParams.start;
+      const E = this.lastParams.end;
+      getRegionChart(this.lastParams).then(r => {
+        // 生成数据
+        let strcsv =
+          "data:text/csv;charset=utf-8,\uFEFF应用名称,省份,地市,活跃用户数\r\n";
+        (r || []).map(v => {
+          strcsv += [
+            this.lastParams.app,
+            v.province,
+            v.city,
+            v.userSum,
+            "\r\n",
+          ].join(",");
+        });
+        // 导出
+        let link = document.createElement("a");
+        link.id = "download-csv";
+        link.setAttribute("href", encodeURI(strcsv));
+        link.setAttribute(
+          "download",
+          this.lastParams.app + "地域分析" + S + "_" + E + ".csv"
+        );
+        // document.body.appendChild(link);
+        link.click();
+      });
+    },
+  },
+  components: {
+    countTo,
+  },
+};
+</script>
+
+<style>
+.Region {
+    margin: 10px 15px;
+}
+.Region .has-seconds .el-time-spinner__wrapper:last-child {
+    display: none;
+}
+.head {
+    display: flex;
+    font-weight: 500;
+}
+.head .head-item {
+    flex: 1;
+    text-align: center;
+    font-size: 0.8em;
+    border-top: 3px solid #fff;
+    padding-top: 10px;
+}
+.head .value {
+    margin: 15px 0;
+    color: #396fff;
+    font-size: 25px;
+}
+.ttable {
+    position: absolute;
+    z-index: 2;
+    width: 25em;
+}
+.item {
+    line-height: 1.8em;
+}
+
+.user-overview .overview-item{
+  font-weight: 800;
+  font-size: 1.2em;
+}
+.user-overview .overview-item span{
+  color: rgb(249, 142, 83);
+}
+
+.margin-10{
+  margin-top: 10px;
+}
+
+.card-100{
+  width: 100%;
+}
+
+</style>