|
@@ -1,5 +1,5 @@
|
|
<template>
|
|
<template>
|
|
- <div class="RealOnline">
|
|
|
|
|
|
+ <div class="Region">
|
|
<el-breadcrumb separator-class="el-icon-arrow-right">
|
|
<el-breadcrumb separator-class="el-icon-arrow-right">
|
|
<el-breadcrumb-item>新媒体</el-breadcrumb-item>
|
|
<el-breadcrumb-item>新媒体</el-breadcrumb-item>
|
|
<el-breadcrumb-item>用户分析</el-breadcrumb-item>
|
|
<el-breadcrumb-item>用户分析</el-breadcrumb-item>
|
|
@@ -89,11 +89,93 @@
|
|
</el-card>
|
|
</el-card>
|
|
<br />
|
|
<br />
|
|
<el-card class="box-card">
|
|
<el-card class="box-card">
|
|
- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
|
|
|
- <el-tab-pane name="ShanXi" label="地市">User</el-tab-pane>
|
|
|
|
- <el-tab-pane name="China" label="省">Config</el-tab-pane>
|
|
|
|
|
|
+ <el-tabs
|
|
|
|
+ v-model="form.cityType"
|
|
|
|
+ class="demo-tabs"
|
|
|
|
+ @tab-click="changeTabs"
|
|
|
|
+ >
|
|
|
|
+ <el-tab-pane name="1" label="地市"></el-tab-pane>
|
|
|
|
+ <el-tab-pane name="2" label="省"></el-tab-pane>
|
|
</el-tabs>
|
|
</el-tabs>
|
|
- <div ref="regionChart"></div>
|
|
|
|
|
|
+ <el-row>
|
|
|
|
+ <el-col :span="6">
|
|
|
|
+ <div
|
|
|
|
+ style="
|
|
|
|
+ padding: 10px 0;
|
|
|
|
+ text-align: center;
|
|
|
|
+ color: rgb(14, 14, 14);
|
|
|
|
+ font-size: 20px;
|
|
|
|
+ "
|
|
|
|
+ >
|
|
|
|
+ 活跃用户排行榜
|
|
|
|
+ </div>
|
|
|
|
+ <el-table
|
|
|
|
+ height="500"
|
|
|
|
+ v-if="table.length"
|
|
|
|
+ :data="table"
|
|
|
|
+ style="width: 100%"
|
|
|
|
+ :header-cell-style="{
|
|
|
|
+ backgroundColor: '#f4f5f7',
|
|
|
|
+ color: '#606266',
|
|
|
|
+ }"
|
|
|
|
+ >
|
|
|
|
+ <el-table-column
|
|
|
|
+ header-align="center"
|
|
|
|
+ align="center"
|
|
|
|
+ prop="dt"
|
|
|
|
+ label="地名"
|
|
|
|
+ >
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ {{
|
|
|
|
+ lastParams.cityType === "1"
|
|
|
|
+ ? scope.row.city
|
|
|
|
+ : scope.row.province
|
|
|
|
+ }}
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column
|
|
|
|
+ show-overflow-tooltip
|
|
|
|
+ label="应用名称"
|
|
|
|
+ header-align="center"
|
|
|
|
+ align="center"
|
|
|
|
+ >
|
|
|
|
+ <template #default>
|
|
|
|
+ <span>{{ lastParams.app || "-" }}</span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column
|
|
|
|
+ header-align="center"
|
|
|
|
+ align="center"
|
|
|
|
+ prop="userSum"
|
|
|
|
+ label="新增用户数"
|
|
|
|
+ >
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ <countTo
|
|
|
|
+ :startVal="scope.row.userSum"
|
|
|
|
+ :endVal="scope.row.userSum"
|
|
|
|
+ :duration="100"
|
|
|
|
+ ></countTo>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ </el-table>
|
|
|
|
+ <br />
|
|
|
|
+ <div v-if="item.name" class="item">区域名称:{{ item.name }}</div>
|
|
|
|
+ <div v-if="item.name" class="item">
|
|
|
|
+ 活跃用户数:
|
|
|
|
+ <countTo
|
|
|
|
+ :startVal="item.userSum"
|
|
|
|
+ :endVal="item.userSum"
|
|
|
|
+ :duration="100"
|
|
|
|
+ ></countTo>
|
|
|
|
+ </div>
|
|
|
|
+ <div v-if="item.name" class="item">
|
|
|
|
+ 活跃用户占比:{{ item.distribution }}
|
|
|
|
+ </div>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="18">
|
|
|
|
+ <div ref="regionChart"></div>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
</el-card>
|
|
</el-card>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
@@ -101,8 +183,14 @@
|
|
<script>
|
|
<script>
|
|
import china from "../../assets/map/100000_full.json";
|
|
import china from "../../assets/map/100000_full.json";
|
|
import shanxi from "../../assets/map/610000_shaanxi.json";
|
|
import shanxi from "../../assets/map/610000_shaanxi.json";
|
|
|
|
+import countTo from "@/components/counto/vue-countTo.vue";
|
|
// @ is an alias to /src
|
|
// @ is an alias to /src
|
|
-import { getRule, getAppList, getHistory, getSearchData } from "@/api/index";
|
|
|
|
|
|
+import {
|
|
|
|
+ getRule,
|
|
|
|
+ getAppList,
|
|
|
|
+ getRegionChart,
|
|
|
|
+ getSearchData,
|
|
|
|
+} from "@/api/index";
|
|
|
|
|
|
import * as echarts from "echarts";
|
|
import * as echarts from "echarts";
|
|
|
|
|
|
@@ -112,19 +200,18 @@ export default {
|
|
name: "region",
|
|
name: "region",
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
- type: "",
|
|
|
|
lastParams: {},
|
|
lastParams: {},
|
|
- page: 1,
|
|
|
|
|
|
+ table: [],
|
|
form: {
|
|
form: {
|
|
app: "",
|
|
app: "",
|
|
version: [],
|
|
version: [],
|
|
channel: [],
|
|
channel: [],
|
|
date: [],
|
|
date: [],
|
|
region: "",
|
|
region: "",
|
|
|
|
+ cityType: "1",
|
|
},
|
|
},
|
|
cycle: [],
|
|
cycle: [],
|
|
- oriData: {},
|
|
|
|
- showList: [],
|
|
|
|
|
|
+ item: {},
|
|
version: [],
|
|
version: [],
|
|
channel: [],
|
|
channel: [],
|
|
};
|
|
};
|
|
@@ -150,6 +237,7 @@ export default {
|
|
version: [(this.version[0] || { value: "" }).value],
|
|
version: [(this.version[0] || { value: "" }).value],
|
|
channel: [(this.channel[0] || { value: "" }).value],
|
|
channel: [(this.channel[0] || { value: "" }).value],
|
|
date: [new Date(Date.now() - 604800000), new Date(Date.now() - 86400000)],
|
|
date: [new Date(Date.now() - 604800000), new Date(Date.now() - 86400000)],
|
|
|
|
+ cityType: "1",
|
|
};
|
|
};
|
|
this.onSubmit();
|
|
this.onSubmit();
|
|
},
|
|
},
|
|
@@ -170,19 +258,8 @@ export default {
|
|
}
|
|
}
|
|
return out;
|
|
return out;
|
|
},
|
|
},
|
|
- pagechange(p) {
|
|
|
|
- this.page = p;
|
|
|
|
- this.pushShowList();
|
|
|
|
- },
|
|
|
|
- pushShowList() {
|
|
|
|
- let s = this.page - 1 < 0 ? 0 : (this.page - 1) * 10;
|
|
|
|
- let e = this.page * 10;
|
|
|
|
- let li = JSON.parse(JSON.stringify(this.oriData.list || []));
|
|
|
|
- let out = [];
|
|
|
|
- for (let i = s; i < e; i++) {
|
|
|
|
- li[i] && out.push(li[i]);
|
|
|
|
- }
|
|
|
|
- this.showList = out;
|
|
|
|
|
|
+ changeTabs() {
|
|
|
|
+ this.onSubmit();
|
|
},
|
|
},
|
|
onSubmit() {
|
|
onSubmit() {
|
|
this.lastParams = {
|
|
this.lastParams = {
|
|
@@ -191,139 +268,77 @@ export default {
|
|
end: this.FormData(this.form.date[1]),
|
|
end: this.FormData(this.form.date[1]),
|
|
manufacturer: this.form.channel == -1 ? undefined : this.form.channel,
|
|
manufacturer: this.form.channel == -1 ? undefined : this.form.channel,
|
|
version: this.form.version == -1 ? undefined : this.form.version,
|
|
version: this.form.version == -1 ? undefined : this.form.version,
|
|
|
|
+ cityType: this.form.cityType,
|
|
};
|
|
};
|
|
- getHistory(this.lastParams)
|
|
|
|
|
|
+ getRegionChart(this.lastParams)
|
|
.then(r => {
|
|
.then(r => {
|
|
if (!this.$refs.regionChart) return;
|
|
if (!this.$refs.regionChart) return;
|
|
- let oriData = r || {};
|
|
|
|
- const total = oriData.total || {},
|
|
|
|
- keys = Object.keys(total),
|
|
|
|
- color = ["rgb(244, 127, 146)", "rgb(17, 160, 248)"],
|
|
|
|
- p = [];
|
|
|
|
- let tab = undefined,
|
|
|
|
- tabName = undefined;
|
|
|
|
- for (let i = 0; i < keys.length; i++) {
|
|
|
|
- const v = keys[i];
|
|
|
|
- if (!total[v].value && total[v].value !== 0) continue;
|
|
|
|
- let isNum = /duration/g.test(v);
|
|
|
|
- let value = "";
|
|
|
|
- if (isNum && typeof total[v].value === "number")
|
|
|
|
- value = this.timeFormat(total[v].value);
|
|
|
|
- else value = total[v].value;
|
|
|
|
- if (!tab && v !== "downloads") {
|
|
|
|
- tab = v;
|
|
|
|
- tabName = total[v].name;
|
|
|
|
|
|
+ let list = r || [];
|
|
|
|
+ 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;
|
|
}
|
|
}
|
|
- p.push({
|
|
|
|
- name: total[v].name,
|
|
|
|
- value,
|
|
|
|
- color: color[i % 2],
|
|
|
|
- key: v,
|
|
|
|
- isNum,
|
|
|
|
- });
|
|
|
|
}
|
|
}
|
|
- this.oriData = {
|
|
|
|
- list: oriData.list || [],
|
|
|
|
- total: p,
|
|
|
|
- };
|
|
|
|
- this.changeData(tab, tabName);
|
|
|
|
|
|
+ this.createImage(list);
|
|
chart && chart.hideLoading();
|
|
chart && chart.hideLoading();
|
|
- this.page = 1;
|
|
|
|
- this.pushShowList();
|
|
|
|
})
|
|
})
|
|
.catch(() => {
|
|
.catch(() => {
|
|
- this.oriData = {};
|
|
|
|
- this.pushShowList();
|
|
|
|
chart && chart.clear() && chart.hideLoading();
|
|
chart && chart.clear() && chart.hideLoading();
|
|
});
|
|
});
|
|
},
|
|
},
|
|
- changeData(type, title) {
|
|
|
|
- this.type = type;
|
|
|
|
- const keyList = [],
|
|
|
|
- valueList = [];
|
|
|
|
- (this.oriData.list || []).map(v => {
|
|
|
|
- keyList.push(v.dt);
|
|
|
|
- valueList.push(v[this.type]);
|
|
|
|
- });
|
|
|
|
- chart && chart.clear();
|
|
|
|
- this.createImage(keyList, valueList, title);
|
|
|
|
- },
|
|
|
|
- createImage(keyList, valueList, title) {
|
|
|
|
|
|
+ createImage(list) {
|
|
!chart && (chart = echarts.init(this.$refs.regionChart));
|
|
!chart && (chart = echarts.init(this.$refs.regionChart));
|
|
- console.log(keyList, valueList, title);
|
|
|
|
|
|
+ chart.clear();
|
|
chart.resize({
|
|
chart.resize({
|
|
- height: (this.$refs.regionChart.offsetWidth * 4) / 16,
|
|
|
|
|
|
+ height: this.$refs.regionChart.offsetWidth * 1,
|
|
});
|
|
});
|
|
- chart.setOption({
|
|
|
|
- title: {
|
|
|
|
- text: title,
|
|
|
|
- },
|
|
|
|
|
|
+ let min = list[list.length - 1].userSum;
|
|
|
|
+ const p = {
|
|
tooltip: {
|
|
tooltip: {
|
|
trigger: "item",
|
|
trigger: "item",
|
|
- formatter: "{b}<br/>{c} (p / km2)",
|
|
|
|
|
|
+ formatter: "{b}<br/>{c}",
|
|
},
|
|
},
|
|
- toolbox: {
|
|
|
|
- show: true,
|
|
|
|
- orient: "vertical",
|
|
|
|
- left: "right",
|
|
|
|
- top: "center",
|
|
|
|
- feature: {
|
|
|
|
- dataView: { readOnly: false },
|
|
|
|
- restore: {},
|
|
|
|
- saveAsImage: {},
|
|
|
|
|
|
+ visualMap: {
|
|
|
|
+ min,
|
|
|
|
+ max: list[0].userSum,
|
|
|
|
+ realtime: false,
|
|
|
|
+ calculable: true,
|
|
|
|
+ inRange: {
|
|
|
|
+ color: ["lightskyblue", "yellow", "orangered"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
series: [
|
|
series: [
|
|
{
|
|
{
|
|
- name: title,
|
|
|
|
type: "map",
|
|
type: "map",
|
|
- map: "China",
|
|
|
|
|
|
+ map: this.lastParams.cityType === "1" ? "ShanXi" : "China",
|
|
label: {
|
|
label: {
|
|
show: true,
|
|
show: true,
|
|
},
|
|
},
|
|
- data: [
|
|
|
|
- { name: "中西区", value: 20057.34 },
|
|
|
|
- { name: "湾仔", value: 15477.48 },
|
|
|
|
- { name: "东区", value: 31686.1 },
|
|
|
|
- { name: "南区", value: 6992.6 },
|
|
|
|
- { name: "油尖旺", value: 44045.49 },
|
|
|
|
- { name: "深水埗", value: 40689.64 },
|
|
|
|
- { name: "九龙城", value: 37659.78 },
|
|
|
|
- { name: "黄大仙", value: 45180.97 },
|
|
|
|
- { name: "观塘", value: 55204.26 },
|
|
|
|
- { name: "葵青", value: 21900.9 },
|
|
|
|
- { name: "荃湾", value: 4918.26 },
|
|
|
|
- { name: "屯门", value: 5881.84 },
|
|
|
|
- { name: "元朗", value: 4178.01 },
|
|
|
|
- { name: "北区", value: 2227.92 },
|
|
|
|
- { name: "大埔", value: 2180.98 },
|
|
|
|
- { name: "沙田", value: 9172.94 },
|
|
|
|
- { name: "西贡", value: 3368 },
|
|
|
|
- { name: "离岛", value: 806.98 },
|
|
|
|
- ],
|
|
|
|
- // 自定义名称映射
|
|
|
|
- nameMap: {
|
|
|
|
- "Central and Western": "中西区",
|
|
|
|
- Eastern: "东区",
|
|
|
|
- Islands: "离岛",
|
|
|
|
- "Kowloon City": "九龙城",
|
|
|
|
- "Kwai Tsing": "葵青",
|
|
|
|
- "Kwun Tong": "观塘",
|
|
|
|
- North: "北区",
|
|
|
|
- "Sai Kung": "西贡",
|
|
|
|
- "Sha Tin": "沙田",
|
|
|
|
- "Sham Shui Po": "深水埗",
|
|
|
|
- Southern: "南区",
|
|
|
|
- "Tai Po": "大埔",
|
|
|
|
- "Tsuen Wan": "荃湾",
|
|
|
|
- "Tuen Mun": "屯门",
|
|
|
|
- "Wan Chai": "湾仔",
|
|
|
|
- "Wong Tai Sin": "黄大仙",
|
|
|
|
- "Yau Tsim Mong": "油尖旺",
|
|
|
|
- "Yuen Long": "元朗",
|
|
|
|
- },
|
|
|
|
|
|
+ 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 || {};
|
|
});
|
|
});
|
|
},
|
|
},
|
|
async getAppListFunc() {
|
|
async getAppListFunc() {
|
|
@@ -489,34 +504,18 @@ export default {
|
|
this.form.channel = ver;
|
|
this.form.channel = ver;
|
|
},
|
|
},
|
|
onExport() {
|
|
onExport() {
|
|
- const S = this.form.date[0]
|
|
|
|
- ? this.FormData(this.form.date[0])
|
|
|
|
- : undefined;
|
|
|
|
- const E = this.form.date[1]
|
|
|
|
- ? this.FormData(this.form.date[1])
|
|
|
|
- : undefined;
|
|
|
|
- let p = this.lastParams.app
|
|
|
|
- ? this.lastParams
|
|
|
|
- : {
|
|
|
|
- app: this.form.app,
|
|
|
|
- start: S,
|
|
|
|
- end: E,
|
|
|
|
- manufacturer:
|
|
|
|
- this.form.channel == -1 ? undefined : this.form.channel,
|
|
|
|
- version: this.form.version == -1 ? undefined : this.form.version,
|
|
|
|
- };
|
|
|
|
- getHistory(p).then(r => {
|
|
|
|
|
|
+ const S = this.lastParams.start;
|
|
|
|
+ const E = this.lastParams.end;
|
|
|
|
+ getRegionChart(this.lastParams).then(r => {
|
|
// 生成数据
|
|
// 生成数据
|
|
let strcsv =
|
|
let strcsv =
|
|
- "data:text/csv;charset=utf-8,日期,活跃用户,新增用户,累计用户,人均使用时长,次均使用时长\r\n";
|
|
|
|
- (r.list || []).map(v => {
|
|
|
|
|
|
+ "data:text/csv;charset=utf-8,应用名称,省份,地市,活跃用户数\r\n";
|
|
|
|
+ (r || []).map(v => {
|
|
strcsv += [
|
|
strcsv += [
|
|
- v.dt,
|
|
|
|
- v.activeUser,
|
|
|
|
- v.newUser,
|
|
|
|
- v.totalUser,
|
|
|
|
- this.timeFormat(v.durationUser),
|
|
|
|
- this.timeFormat(v.durationTimes),
|
|
|
|
|
|
+ this.lastParams.app,
|
|
|
|
+ v.province,
|
|
|
|
+ v.city,
|
|
|
|
+ v.userSum,
|
|
"\r\n",
|
|
"\r\n",
|
|
].join(",");
|
|
].join(",");
|
|
});
|
|
});
|
|
@@ -526,22 +525,24 @@ export default {
|
|
link.setAttribute("href", encodeURI(strcsv));
|
|
link.setAttribute("href", encodeURI(strcsv));
|
|
link.setAttribute(
|
|
link.setAttribute(
|
|
"download",
|
|
"download",
|
|
- p.app + "用户趋势" + S + "_" + E + ".csv"
|
|
|
|
|
|
+ this.lastParams.app + "地域分析" + S + "_" + E + ".csv"
|
|
);
|
|
);
|
|
// document.body.appendChild(link);
|
|
// document.body.appendChild(link);
|
|
link.click();
|
|
link.click();
|
|
});
|
|
});
|
|
},
|
|
},
|
|
},
|
|
},
|
|
- components: {},
|
|
|
|
|
|
+ components: {
|
|
|
|
+ countTo,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style>
|
|
<style>
|
|
-.RealOnline {
|
|
|
|
|
|
+.Region {
|
|
margin: 10px 15px;
|
|
margin: 10px 15px;
|
|
}
|
|
}
|
|
-.RealOnline .has-seconds .el-time-spinner__wrapper:last-child {
|
|
|
|
|
|
+.Region .has-seconds .el-time-spinner__wrapper:last-child {
|
|
display: none;
|
|
display: none;
|
|
}
|
|
}
|
|
.head {
|
|
.head {
|
|
@@ -560,4 +561,12 @@ export default {
|
|
color: #396fff;
|
|
color: #396fff;
|
|
font-size: 25px;
|
|
font-size: 25px;
|
|
}
|
|
}
|
|
|
|
+.ttable {
|
|
|
|
+ position: absolute;
|
|
|
|
+ z-index: 2;
|
|
|
|
+ width: 25em;
|
|
|
|
+}
|
|
|
|
+.item {
|
|
|
|
+ line-height: 1.8em;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|