|
@@ -83,6 +83,23 @@
|
|
</el-option>
|
|
</el-option>
|
|
</el-select>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
+ <el-form-item label="来源">
|
|
|
|
+ <el-select
|
|
|
|
+ collapse-tags
|
|
|
|
+ clearable
|
|
|
|
+ v-model="form.client"
|
|
|
|
+ placeholder="请选择来源"
|
|
|
|
+ >
|
|
|
|
+ <el-option
|
|
|
|
+ v-for="item in client"
|
|
|
|
+ :key="item.value"
|
|
|
|
+ :label="item.label"
|
|
|
|
+ :value="item.value"
|
|
|
|
+ :disabled="item.disabled"
|
|
|
|
+ >
|
|
|
|
+ </el-option>
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
<el-form-item style="float: right">
|
|
<el-form-item style="float: right">
|
|
<el-button type="primary" @click="onSubmit">查询</el-button>
|
|
<el-button type="primary" @click="onSubmit">查询</el-button>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
@@ -94,22 +111,36 @@
|
|
<el-button-group>
|
|
<el-button-group>
|
|
<el-button
|
|
<el-button
|
|
size="small"
|
|
size="small"
|
|
- :type="numType === 'newUser' ? 'primary' : 'plain'"
|
|
|
|
- @click="() => selectNumType('newUser')"
|
|
|
|
|
|
+ :type="numType === 'mau1' ? 'primary' : 'plain'"
|
|
|
|
+ @click="() => selectNumType('mau1')"
|
|
|
|
+ >
|
|
|
|
+ 日活跃用户数
|
|
|
|
+ </el-button>
|
|
|
|
+ <el-button
|
|
|
|
+ size="small"
|
|
|
|
+ :type="numType === 'mau7' ? 'primary' : 'plain'"
|
|
|
|
+ @click="() => selectNumType('mau7')"
|
|
>
|
|
>
|
|
- 新用户数
|
|
|
|
|
|
+ 周活跃用户数
|
|
</el-button>
|
|
</el-button>
|
|
<el-button
|
|
<el-button
|
|
size="small"
|
|
size="small"
|
|
- :type="numType === 'startTimes' ? 'primary' : 'plain'"
|
|
|
|
- @click="() => selectNumType('startTimes')"
|
|
|
|
|
|
+ :type="numType === 'mau30' ? 'primary' : 'plain'"
|
|
|
|
+ @click="() => selectNumType('mau30')"
|
|
>
|
|
>
|
|
- 启动次数
|
|
|
|
|
|
+ 月活跃用户数
|
|
|
|
+ </el-button>
|
|
|
|
+ <el-button
|
|
|
|
+ size="small"
|
|
|
|
+ :type="numType === 'total' ? 'primary' : 'plain'"
|
|
|
|
+ @click="() => selectNumType('total')"
|
|
|
|
+ >
|
|
|
|
+ 累积用户数
|
|
</el-button>
|
|
</el-button>
|
|
</el-button-group>
|
|
</el-button-group>
|
|
</div>
|
|
</div>
|
|
<br />
|
|
<br />
|
|
- <div ref="regionChart"></div>
|
|
|
|
|
|
+ <div ref="regionChart" ></div>
|
|
<br />
|
|
<br />
|
|
<div style="text-align: right">
|
|
<div style="text-align: right">
|
|
<el-button
|
|
<el-button
|
|
@@ -177,6 +208,17 @@
|
|
{{ (100 * scope.row.mau1 / scope.row.total).toFixed(2) }}%
|
|
{{ (100 * scope.row.mau1 / scope.row.total).toFixed(2) }}%
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column
|
|
|
|
+ label="日活/月活"
|
|
|
|
+ header-align="center"
|
|
|
|
+ align="center"
|
|
|
|
+ prop="mau1"
|
|
|
|
+ >
|
|
|
|
+ <template #default="scope">
|
|
|
|
+ {{ (100 * scope.row.mau1 / scope.row.mau30).toFixed(2) }}%
|
|
|
|
+ </template>
|
|
|
|
+
|
|
</el-table-column>
|
|
</el-table-column>
|
|
<el-table-column
|
|
<el-table-column
|
|
label="周活跃用户数"
|
|
label="周活跃用户数"
|
|
@@ -288,31 +330,44 @@ export default {
|
|
version: [],
|
|
version: [],
|
|
channel: [],
|
|
channel: [],
|
|
date: [],
|
|
date: [],
|
|
|
|
+ client: "",
|
|
},
|
|
},
|
|
cycle: [],
|
|
cycle: [],
|
|
showList: [],
|
|
showList: [],
|
|
channel: [],
|
|
channel: [],
|
|
version: [],
|
|
version: [],
|
|
|
|
+ client: [],
|
|
};
|
|
};
|
|
},
|
|
},
|
|
async mounted() {
|
|
async mounted() {
|
|
if (chart && chart.dispose) chart.dispose();
|
|
if (chart && chart.dispose) chart.dispose();
|
|
- const { source, appV, appC, appli, appVLi, appCLi } =
|
|
|
|
|
|
+ const { source, appV, appC, appli, appVLi, appCLi, clentV, clentli } =
|
|
await this.getAppListFunc();
|
|
await this.getAppListFunc();
|
|
const keys = {
|
|
const keys = {
|
|
value: "mname",
|
|
value: "mname",
|
|
label: "mname",
|
|
label: "mname",
|
|
};
|
|
};
|
|
|
|
+ let client = clentli.find(r => r.mdefault).mcode.toString();
|
|
this.cycle = this.verifyList(appli, source, keys, false);
|
|
this.cycle = this.verifyList(appli, source, keys, false);
|
|
this.channel = this.verifyList(appCLi, appC, keys, true);
|
|
this.channel = this.verifyList(appCLi, appC, keys, true);
|
|
- this.version = this.verifyList(appVLi, appV, keys, false);
|
|
|
|
|
|
+ this.version = this.verifyList(appVLi, appV, keys, true);
|
|
|
|
+ this.client = this.verifyList(
|
|
|
|
+ clentli,
|
|
|
|
+ clentV,
|
|
|
|
+ {
|
|
|
|
+ value: "mcode",
|
|
|
|
+ label: "mname",
|
|
|
|
+ },
|
|
|
|
+ true
|
|
|
|
+ );
|
|
|
|
|
|
this.form = {
|
|
this.form = {
|
|
// app: (this.cycle[0] || { value: "" }).value,
|
|
// app: (this.cycle[0] || { value: "" }).value,
|
|
app: "起点新闻",
|
|
app: "起点新闻",
|
|
- version: [],
|
|
|
|
|
|
+ version: [-1],
|
|
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)],
|
|
|
|
+ client
|
|
};
|
|
};
|
|
this.onSubmit();
|
|
this.onSubmit();
|
|
},
|
|
},
|
|
@@ -331,29 +386,21 @@ export default {
|
|
label: v[obj.label],
|
|
label: v[obj.label],
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
+
|
|
return out;
|
|
return out;
|
|
},
|
|
},
|
|
selectNumType(type) {
|
|
selectNumType(type) {
|
|
this.numType = type;
|
|
this.numType = type;
|
|
- getUserActivity({
|
|
|
|
- app: this.lastParams.app,
|
|
|
|
- start: this.lastParams.start,
|
|
|
|
- end: this.lastParams.end,
|
|
|
|
- version: this.form.version == -1 ? undefined : this.form.version,
|
|
|
|
- }).then(resChart => {
|
|
|
|
- const c = resChart || {};
|
|
|
|
|
|
+ let resChart = this.table;
|
|
let keyList = [],
|
|
let keyList = [],
|
|
- valueList = [],
|
|
|
|
- titles = [];
|
|
|
|
|
|
+ valueList = [];
|
|
if ((resChart || []).length && chart && chart.clear) chart.clear();
|
|
if ((resChart || []).length && chart && chart.clear) chart.clear();
|
|
- for (let index = 0; index < (Object.keys(c) || []).length; index++) {
|
|
|
|
- const v = (Object.keys(c) || [])[index];
|
|
|
|
- valueList.push((c[v] || []).map(v => v[this.numType]));
|
|
|
|
- titles.push(v);
|
|
|
|
- if (!keyList.length) keyList = (c[v] || []).map(v => v.dt);
|
|
|
|
- }
|
|
|
|
- this.createImage(keyList, valueList, titles);
|
|
|
|
- });
|
|
|
|
|
|
+ resChart.map(v => {
|
|
|
|
+ keyList.push(v.date);
|
|
|
|
+ valueList.push(v[this.numType]);
|
|
|
|
+ });
|
|
|
|
+ this.createImage(keyList, valueList, '');
|
|
|
|
+
|
|
},
|
|
},
|
|
onSubmit() {
|
|
onSubmit() {
|
|
this.lastParams = {
|
|
this.lastParams = {
|
|
@@ -362,25 +409,25 @@ 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,
|
|
|
|
+ lib: this.form.client == -1 ? undefined : this.form.client,
|
|
};
|
|
};
|
|
|
|
|
|
getUserActivity(this.lastParams).then(resChart => {
|
|
getUserActivity(this.lastParams).then(resChart => {
|
|
this.table = resChart
|
|
this.table = resChart
|
|
|
|
|
|
let keyList = [],
|
|
let keyList = [],
|
|
- valueList = [],
|
|
|
|
- titles = [];
|
|
|
|
|
|
+ valueList = [];
|
|
if ((resChart || []).length && chart && chart.clear) chart.clear();
|
|
if ((resChart || []).length && chart && chart.clear) chart.clear();
|
|
- for (let index = 0; index < resChart.length; index++) {
|
|
|
|
-
|
|
|
|
- valueList.push(resChart.map(v => v[this.numType]));
|
|
|
|
- titles.push(resChart.map(v => v.date));
|
|
|
|
- if (!keyList.length) keyList = resChart.map(v => v.date);
|
|
|
|
- }
|
|
|
|
- this.createImage(keyList, valueList, titles);
|
|
|
|
|
|
+
|
|
|
|
+ resChart.map(v => {
|
|
|
|
+ keyList.push(v.date);
|
|
|
|
+ valueList.push(v[this.numType]);
|
|
|
|
+ });
|
|
|
|
+ this.createImage(keyList, valueList, '');
|
|
});
|
|
});
|
|
},
|
|
},
|
|
createImage(keyList, valueList, title) {
|
|
createImage(keyList, valueList, title) {
|
|
|
|
+ console.log(keyList,valueList,title)
|
|
if (!chart) {
|
|
if (!chart) {
|
|
chart = echarts.init(this.$refs.regionChart);
|
|
chart = echarts.init(this.$refs.regionChart);
|
|
window.onresize = chart.resize;
|
|
window.onresize = chart.resize;
|
|
@@ -403,7 +450,7 @@ export default {
|
|
},
|
|
},
|
|
},
|
|
},
|
|
legend: {
|
|
legend: {
|
|
- data: title,
|
|
|
|
|
|
+ // data: title,
|
|
},
|
|
},
|
|
grid: {
|
|
grid: {
|
|
left: "3%",
|
|
left: "3%",
|
|
@@ -432,28 +479,51 @@ export default {
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
- series: valueList.map((v, i) => {
|
|
|
|
- return {
|
|
|
|
- name: title[i],
|
|
|
|
- data: v,
|
|
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: title,
|
|
|
|
+ data: valueList,
|
|
symbolSize: 0,
|
|
symbolSize: 0,
|
|
lineStyle: {
|
|
lineStyle: {
|
|
width: 1,
|
|
width: 1,
|
|
},
|
|
},
|
|
type: "line",
|
|
type: "line",
|
|
smooth: true,
|
|
smooth: true,
|
|
- };
|
|
|
|
- }),
|
|
|
|
|
|
+ color: "rgba(58,132,255,.9)",
|
|
|
|
+ areaStyle: {
|
|
|
|
+ color: {
|
|
|
|
+ type: "linear",
|
|
|
|
+ x: 0,
|
|
|
|
+ y: 0,
|
|
|
|
+ x2: 0,
|
|
|
|
+ y2: 1,
|
|
|
|
+ colorStops: [
|
|
|
|
+ {
|
|
|
|
+ offset: 0,
|
|
|
|
+ color: "rgba(58,132,255, 0.8)", // 0% 处的颜色
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ offset: 1,
|
|
|
|
+ color: "rgba(58,132,255, 0.1)", // 100% 处的颜色
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ global: false, // 缺省为 false
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
});
|
|
});
|
|
},
|
|
},
|
|
async getAppListFunc() {
|
|
async getAppListFunc() {
|
|
- const { r, li, appVersion, channel } = await this.getAppListOri();
|
|
|
|
|
|
+ const { r, li, appVersion, channel, clientList } = await this.getAppListOri();
|
|
let source = { length: 0 },
|
|
let source = { length: 0 },
|
|
appli = [];
|
|
appli = [];
|
|
let appV = { length: 0 },
|
|
let appV = { length: 0 },
|
|
appVLi = [];
|
|
appVLi = [];
|
|
let appC = { length: 0 },
|
|
let appC = { length: 0 },
|
|
- appCLi = [];
|
|
|
|
|
|
+ appCLi = [],
|
|
|
|
+ clentV = { length: 0 },
|
|
|
|
+ clentli = [];
|
|
let prvList = r.output.data.prvRolectrl || [];
|
|
let prvList = r.output.data.prvRolectrl || [];
|
|
for (let i = 0; i < prvList.length; i++) {
|
|
for (let i = 0; i < prvList.length; i++) {
|
|
const v = prvList[i];
|
|
const v = prvList[i];
|
|
@@ -463,10 +533,13 @@ export default {
|
|
(appV[v.detid] = true), (appV.length = appV.length + 1);
|
|
(appV[v.detid] = true), (appV.length = appV.length + 1);
|
|
if (v.controlid == "CHANNEL")
|
|
if (v.controlid == "CHANNEL")
|
|
(appC[v.detid] = true), (appC.length = appC.length + 1);
|
|
(appC[v.detid] = true), (appC.length = appC.length + 1);
|
|
|
|
+ if (v.controlid == "CLIENT_TYPE")
|
|
|
|
+ (clentV[v.detid] = true), (clentV.length = clentV.length + 1);
|
|
}
|
|
}
|
|
if (li.status === "0") appli = li.output.data || [];
|
|
if (li.status === "0") appli = li.output.data || [];
|
|
if (appVersion.length) appVLi = appVersion || [];
|
|
if (appVersion.length) appVLi = appVersion || [];
|
|
if (channel.length) appCLi = channel || [];
|
|
if (channel.length) appCLi = channel || [];
|
|
|
|
+ if (clientList.length) clentli = clientList || [];
|
|
return {
|
|
return {
|
|
source,
|
|
source,
|
|
appV,
|
|
appV,
|
|
@@ -474,6 +547,8 @@ export default {
|
|
appli,
|
|
appli,
|
|
appVLi,
|
|
appVLi,
|
|
appCLi,
|
|
appCLi,
|
|
|
|
+ clentV,
|
|
|
|
+ clentli
|
|
};
|
|
};
|
|
},
|
|
},
|
|
async getAppListOri() {
|
|
async getAppListOri() {
|
|
@@ -505,7 +580,12 @@ export default {
|
|
gcode: "CHANNEL",
|
|
gcode: "CHANNEL",
|
|
source: defaultAppName,
|
|
source: defaultAppName,
|
|
});
|
|
});
|
|
- return { r, li, appVersion, channel };
|
|
|
|
|
|
+ // 端列表
|
|
|
|
+ const clientList = await getSearchData({
|
|
|
|
+ gcode: "CLIENT_TYPE",
|
|
|
|
+ source: defaultAppName,
|
|
|
|
+ });
|
|
|
|
+ return { r, li, appVersion, channel, clientList };
|
|
},
|
|
},
|
|
disabledDate(time) {
|
|
disabledDate(time) {
|
|
const first = new Date("2021-06-21 00:00:00");
|
|
const first = new Date("2021-06-21 00:00:00");
|
|
@@ -559,7 +639,7 @@ export default {
|
|
gcode: "APP_VERSION",
|
|
gcode: "APP_VERSION",
|
|
source: this.form.app,
|
|
source: this.form.app,
|
|
}).then(r => {
|
|
}).then(r => {
|
|
- let version = [];
|
|
|
|
|
|
+ let version = [{ value: -1, label: "不限" }];
|
|
r.map(v => {
|
|
r.map(v => {
|
|
if ((appV.length && appV[v.mcode]) || appV.length === 0)
|
|
if ((appV.length && appV[v.mcode]) || appV.length === 0)
|
|
version.push({
|
|
version.push({
|
|
@@ -568,15 +648,10 @@ export default {
|
|
});
|
|
});
|
|
});
|
|
});
|
|
this.version = version;
|
|
this.version = version;
|
|
- let V = [];
|
|
|
|
- const len = this.version.length > 5 ? 5 : this.version.length;
|
|
|
|
- for (let i = 0; i < len; i++) {
|
|
|
|
- const item = this.version[i];
|
|
|
|
- V.push(item.value);
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
this.form = {
|
|
this.form = {
|
|
...this.form,
|
|
...this.form,
|
|
- version: V,
|
|
|
|
|
|
+ version: [-1],
|
|
};
|
|
};
|
|
});
|
|
});
|
|
getSearchData({
|
|
getSearchData({
|
|
@@ -606,7 +681,9 @@ export default {
|
|
},
|
|
},
|
|
changeversion(v) {
|
|
changeversion(v) {
|
|
if (!v.length)
|
|
if (!v.length)
|
|
- return (this.form.version = [(this.version[0] || {}).value]);
|
|
|
|
|
|
+ return (this.form.version = [-1]);
|
|
|
|
+ const last = v[v.length - 1];
|
|
|
|
+ if (last == -1) return (this.form.version = [-1]);
|
|
let ver = [];
|
|
let ver = [];
|
|
for (let i = 0; i < v.length; i++) {
|
|
for (let i = 0; i < v.length; i++) {
|
|
const element = v[i];
|
|
const element = v[i];
|
|
@@ -647,17 +724,19 @@ export default {
|
|
getUserActivity(p).then(r => {
|
|
getUserActivity(p).then(r => {
|
|
// 生成数据
|
|
// 生成数据
|
|
let strcsv =
|
|
let strcsv =
|
|
- "data:text/csv;charset=utf-8,\uFEFF版本,应用,升级用户数,新用户数,活跃用户数,启动次数,启动用户(分布),平均使用时长\r\n";
|
|
|
|
|
|
+ "data:text/csv;charset=utf-8,\uFEFF日期,应用,日活跃用户数,日活跃度,日活/月活,周活跃用户数,周活跃度,月活跃用户数,月活跃度,累积用户数\r\n";
|
|
(r || []).map(v => {
|
|
(r || []).map(v => {
|
|
strcsv += [
|
|
strcsv += [
|
|
- v.appVersion,
|
|
|
|
|
|
+ v.date,
|
|
p.app,
|
|
p.app,
|
|
- v.upgradeUser,
|
|
|
|
- v.newUser,
|
|
|
|
- v.activeUser,
|
|
|
|
- v.startTimes,
|
|
|
|
- v.distribution + "%",
|
|
|
|
- this.timeFormat(v.duration),
|
|
|
|
|
|
+ v.mau1,
|
|
|
|
+ (100 * v.mau1 / v.total).toFixed(2) + '%',
|
|
|
|
+ (100 * v.mau1 / v.mau30).toFixed(2) + '%',
|
|
|
|
+ v.mau7,
|
|
|
|
+ (100 * v.mau7 / v.total).toFixed(2) + '%',
|
|
|
|
+ v.mau30,
|
|
|
|
+ (100 * v.mau30 / v.total).toFixed(2) + '%',
|
|
|
|
+ v.total,
|
|
"\r\n",
|
|
"\r\n",
|
|
].join(",");
|
|
].join(",");
|
|
});
|
|
});
|
|
@@ -667,7 +746,7 @@ export default {
|
|
link.setAttribute("href", encodeURI(strcsv));
|
|
link.setAttribute("href", encodeURI(strcsv));
|
|
link.setAttribute(
|
|
link.setAttribute(
|
|
"download",
|
|
"download",
|
|
- p.app + "版本分析" + S + "_" + E + ".csv"
|
|
|
|
|
|
+ p.app + "用户活跃度" + S + "_" + E + ".csv"
|
|
);
|
|
);
|
|
// document.body.appendChild(link);
|
|
// document.body.appendChild(link);
|
|
link.click();
|
|
link.click();
|