|
@@ -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>
|