bi/vue/src/views/behavior-analysis/retention.vue
2022-03-31 11:20:22 +08:00

478 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="display:flex;justify-content:space-between">
<div class="content_xwl">
<div class="header_xwl" style="background: white">
<div class="root_xwl">
<div class="main_xwl">
<a-tooltip placement="right" style="cursor: pointer">
<template slot="title">
<span>以某段时间做过初始事件的用户为样本查看在指定日期后用户进行回访事件的留存情况</span>
</template>
<span class="title_xwl" style="color: #202d3f">&nbsp;&nbsp;留存分析 <a-icon
type="question-circle"
/>
<template v-if="reportTableName!=''">
{{ reportTableName }}
</template>
</span>
</a-tooltip>
</div>
<div class="actions_xwl">
<a-tooltip placement="top" style="cursor: pointer">
<template slot="title">
<span>以页面格式下载全量数据</span>
</template>
<a-button type="link" class="actions_xwl_btn" icon="download" @click="download" />
</a-tooltip>
<report-table-list :rt_type="Number(2)" />
</div>
</div>
</div>
<split-pane :min-percent="0" :default-percent="22" split="vertical" @resize="onResize">
<template slot="paneL">
<div
id="scollL"
style="height: 95%;width: 100px;display: inline-block; height: 100%;vertical-align: top;width: 100%;background: white;"
>
<div style="width: 100%;height: calc(100% - 140px); overflow-x: hidden; overflow-y: auto;">
<div style="width: 100%; padding-bottom: 8px;border-bottom: 1px solid #f0f2f5">
<div
style="line-height: 18px;font-weight: 500; font-size: 13px; padding: 10px 16px 12px;font-weight: bolder"
>
初始事件
</div>
<div class="xwl_main">
<div v-for="(v,index) in form.zhibiaoArr" v-if="index == 0">
<div class="row___xwl" style="padding: 10px;">
<el-row>
<el-col :span="2">
<el-tag type="warning" class="drageTag">{{ index + 1 }}</el-tag>
</el-col>
<el-col :span="18">
<el-row :span="15" class="zhibiao">
<a-tooltip placement="topLeft" style="cursor: pointer">
<template slot="title">
<span>点击修改指标名称</span>
</template>
<a-input
:ref="('getFocus'+index).toString()"
v-model:value="form.zhibiaoArr[index].eventNameDisplay"
class="eventNameDisplayInput"
placeholder="请输入..."
autofocus="autofocus"
allow-clear
@change="getFocus1('getFocus'+index)"
/>
</a-tooltip>
</el-row>
<el-row style="padding-top: 5px" :span="6">
<a-select
v-model="form.zhibiaoArr[index].eventName"
dropdown-match-select-width
show-search
default-active-first-option
style="width: 120px"
@change="changeEventNameDisplay(index)"
>
<a-select-option
v-for="(v,k,index) in metaEventList"
:key="index"
:value="v.event_name"
>
{{ v.show_name == '' ? v.event_name : v.show_name }}
</a-select-option>
</a-select>
</el-row>
</el-col>
</el-row>
<div class="filters_xwl">
<filter-where
v-model="form.zhibiaoArr[index].relation"
table-typ="2"
:data-type-map="attrMap"
:options="eventAttrOptions"
/>
</div>
</div>
</div>
</div>
</div>
<div style="width: 100%; padding-bottom: 8px;border-bottom: 1px solid #f0f2f5">
<div
style="line-height: 18px;font-weight: 500; font-size: 13px; padding: 10px 16px 12px;font-weight: bolder"
>
回访事件
</div>
<div class="xwl_main">
<div v-for="(v,index) in form.zhibiaoArr" v-if="index == 1">
<div class="row___xwl" style="padding: 10px;">
<el-row>
<el-col :span="2">
<el-tag type="warning" class="drageTag">{{ index + 1 }}</el-tag>
</el-col>
<el-col :span="18">
<el-row :span="15" class="zhibiao">
<a-tooltip placement="topLeft" style="cursor: pointer">
<template slot="title">
<span>点击修改指标名称</span>
</template>
<a-input
:ref="('getFocus'+index).toString()"
v-model:value="form.zhibiaoArr[index].eventNameDisplay"
class="eventNameDisplayInput"
placeholder="请输入..."
autofocus="autofocus"
allow-clear
@change="getFocus1('getFocus'+index)"
/>
</a-tooltip>
</el-row>
<el-row style="padding-top: 5px" :span="6">
<a-select
v-model="form.zhibiaoArr[index].eventName"
dropdown-match-select-width
show-search
default-active-first-option
style="width: 120px"
@change="changeEventNameDisplay(index)"
>
<a-select-option
v-for="(v,k,index) in metaEventList"
:key="index"
:value="v.event_name"
>
{{ v.show_name == '' ? v.event_name : v.show_name }}
</a-select-option>
</a-select>
</el-row>
</el-col>
</el-row>
<div class="filters_xwl">
<filter-where
v-model="form.zhibiaoArr[index].relation"
table-typ="2"
:data-type-map="attrMap"
:options="eventAttrOptions"
/>
</div>
</div>
</div>
</div>
</div>
<div style="width: 100%; padding-bottom: 8px;border-bottom: 1px solid #f0f2f5">
<div
style="line-height: 18px;font-weight: 500; font-size: 13px; padding: 10px 16px 10px;font-weight: bolder"
>
全局筛选事件维度
</div>
<filter-where
v-model="form.whereFilter"
:data-type-map="attrMap"
table-typ="2"
:options="eventAttrOptions"
/>
</div>
<div style="width: 100%; padding-bottom: 8px;border-bottom: 1px solid #f0f2f5">
<div
style="line-height: 18px;font-weight: 500; font-size: 13px; padding: 10px 16px 10px;font-weight: bolder"
>
全局筛选用户维度
</div>
<filter-where
v-model="form.whereFilterByUser"
:data-type-map="attrMap"
table-typ="1"
:options="userAttrOptions"
/>
</div>
<div style="width: 100%; padding-bottom: 8px;border-bottom: 1px solid #f0f2f5">
<filter-user-group v-model="form.userGroup" />
</div>
</div>
<div
style="width: 100%;height: 50px;margin-bottom: 0px;z-index: 10000;border-top: 1px solid #f0f2f5;background: white;display: flex;align-items: center;justify-content: center"
>
<add-report-table
:rt-type="Number(2)"
:name="currentReportTable.name"
:remark="currentReportTable.remark"
:data="this.form"
style="width: 200px;height: 50px;line-height: 50px;margin: 0px auto"
@go="go"
/>
</div>
</div>
</template>
<template slot="paneR">
<a-spin tip="计算中..." :spinning="spinning">
<div class="spin-content">
<retention-result
v-if="retentionResShow"
ref="retentionRes"
v-model="form.date"
:window-time="form.windowTime"
style="padding: 20px"
:table-header="tableHeader"
:retention-res="retentionRes"
@changeWindowTime="changeWindowTime"
@go="go"
/>
</div>
</a-spin>
</template>
</split-pane>
</div>
</div>
</template>
<script>
import { debounce } from 'lodash'
import moment from 'moment'
import { GetConfigs, RetentionList } from '@/api/analysis'
import { FindRtById } from '@/api/pannel'
export default {
name: 'Retention',
components: {
'FilterWhere': () => import('@/components/AnalyseTools/FilterWhere/index'),
'FilterGroup': () => import('@/components/AnalyseTools/FilterGroup/index'),
'RetentionResult': () => import('@/views/behavior-analysis/components/RetentionResult'),
'ReportTableList': () => import('@/views/behavior-analysis/components/ReportTableList'),
'AddReportTable': () => import('@/views/behavior-analysis/components/AddReportTable'),
'FilterUserGroup': () => import('@/components/AnalyseTools/FilterUserGroup')
},
data() {
return {
currentReportTable: {
name: '',
remark: ''
},
spinning: false,
drawerShow: false,
tableHeader: [],
retentionRes: [],
retentionResShow: true,
prevCount: 0,
metaEventList: [],
userAttr: [],
eventAttr: [],
reportTableName: '',
form: {
zhibiaoArr: [],
groupBy: [],
whereFilter: {
filterType: 'COMPOUND',
filts: [],
relation: '且'
},
whereFilterByUser: {
filterType: 'COMPOUND',
filts: [],
relation: '且'
},
windowTime: 1,
windowTimeFormat: '天',
date: [
moment().startOf('day').subtract(1, 'days').format('YYYY-MM-DD'),
moment().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
]
},
eventAttrOptions: [],
allAttrOptions: [],
userAttrOptions: [],
attrMap: [],
debounceHandleSizeChange: undefined
}
},
async beforeMount() {
await this.initReportData()
this.debounceHandleSizeChange = debounce(this.refreshRes, 500)
},
mounted() {
this.init()
},
methods: {
download() {
this.$refs['retentionRes'].download('全量数据')
},
changeWindowTime(windowTime, windowTimeFormat) {
this.form.windowTime = windowTime
this.form.windowTimeFormat = windowTimeFormat
},
refreshRes() {
this.$nextTick(() => {
this.retentionResShow = true
})
},
onResize() {
this.retentionResShow = false
this.debounceHandleSizeChange()
},
changeEventNameDisplay(index) {
let eventNameDisplay = ''
for (const v of this.metaEventList) {
if (v.event_name == this.form.zhibiaoArr[index].eventName) {
eventNameDisplay = v.show_name == '' ? v.event_name : v.show_name
}
}
this.form.zhibiaoArr[index].eventNameDisplay = eventNameDisplay
},
async initReportData() {
const id = this.$route.params.id
if (id != ':id' && Number(id) != 0) {
const res = await FindRtById({ id: Number(id), 'appid': this.$store.state.baseData.EsConnectID })
if (res.code != 0) {
this.$message({
offset: 60,
type: 'error',
message: res.msg
})
return
}
this.reportTableName = res.data.name
this.currentReportTable.name = res.data.name
this.currentReportTable.remark = res.data.remark
this.form = JSON.parse(res.data.data)
this.form.date = [
moment().startOf('day').subtract(1, 'days').format('YYYY-MM-DD'),
moment().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
]
this.go()
}
},
async init() {
await this.getMetaEventList()
if (this.form.zhibiaoArr.length == 0) {
this.addZhibiao()
this.addZhibiao()
}
},
async getMetaEventList() {
const res = await GetConfigs({ 'appid': this.$store.state.baseData.EsConnectID })
if (res.code != 0) {
this.$message({
type: 'error',
offset: 60,
message: res.msg
})
return
}
this.metaEventList = res.data.event_name_list
const attributeMap = res.data.attributeMap
const allAttrOptions = []
const eventAttrOptions = []
const userAttrOptions = []
this.attrMap = []
this.attrMap = attributeMap
const eventData = { label: '事件', options: [] }
const userData = { label: '用户', options: [] }
if (attributeMap.hasOwnProperty('2')) {
for (const v of attributeMap['2']) {
eventData.options.push({
value: v.attribute_name,
label: v.show_name == '' ? v.attribute_name : v.show_name
})
}
}
if (attributeMap.hasOwnProperty('1')) {
for (const v of attributeMap['1']) {
userData.options.push({
value: v.attribute_name,
label: v.show_name == '' ? v.attribute_name : v.show_name
})
}
}
eventAttrOptions.push(eventData)
userAttrOptions.push(userData)
allAttrOptions.push(eventData, userData)
this.userAttrOptions = userAttrOptions
this.eventAttrOptions = eventAttrOptions
this.allAttrOptions = allAttrOptions
},
moment,
addZhibiao() {
if (this.form.zhibiaoArr.length >= 30) return
this.form.zhibiaoArr.push({
'eventName': this.metaEventList[0].event_name,
'eventNameDisplay': this.metaEventList[0].show_name != '' ? this.metaEventList[0].show_name : this.metaEventList[0].event_name,
'relation': {
filterType: 'COMPOUND',
filts: [],
relation: '且'
}
})
},
getFocus1(f) {
this.$nextTick(function() {
this.$refs[f][0].focus()
})
},
async go() {
this.retentionResShow = false
this.spinning = true
const form = this.form
form['appid'] = this.$store.state.baseData.EsConnectID
const res = await RetentionList(form)
if (res.code != 0) {
this.$message({
type: 'error',
offset: 60,
message: res.msg
})
this.retentionRes = []
} else {
this.retentionRes = res.data.alldata
}
this.spinning = false
this.$nextTick(() => {
this.retentionResShow = true
})
}
}
}
</script>
<style scoped src="@/styles/retention.css"/>
<style>
.eventNameDisplayInput .ant-input {
resize: none;
border: none;
}
.eventNameDisplayInput .ant-input:focus {
border: none;
box-shadow: none;
}
</style>