Commit 4c29c632 by liuchenxi

触达效果

parent ab57751f
......@@ -14,7 +14,7 @@
<span>{{ contentTitle }}</span>
<div v-html="layoutTips" class="layout--tips--wrap"></div>
</div>
<div><span class="intro" @click="() => (drawer = true)">指标说明</span></div>
<div v-if="$route.path.indexOf('ecm') >= 0 && xsxsFlag == 1" class="intro_wrap"><img :src="require('@/assets/img/introlIcon.png')" class="introlIcon" alt="" /><span class="intro" @click="() => (drawer = true)">指标说明</span></div>
</h3>
</div>
<div class="layout-content__wrap">
......@@ -31,6 +31,7 @@
<script>
import asideMenu from '../aside-menu';
import description from '@/views/ecm/touch-components/description.vue';
import { getXsxsFalg } from '@/service/api/ecmApi.js';
export default {
components: {
asideMenu,
......@@ -43,7 +44,8 @@ export default {
leftModulesName: '公众号配置',
bodyHeight: 0,
drawer: false,
direction: 'rtl'
direction: 'rtl',
xsxsFlag: 0
};
},
mounted() {
......@@ -79,13 +81,22 @@ export default {
// 折叠事件
collapseTagHandler(val) {
this.collapseFlag = val;
},
setXsxsFlag() {
getXsxsFalg().then(res => {
this.xsxsFlag = res.result;
});
}
},
watch: {
$route: {
handler: function() {
handler: function(to) {
if (to.path.indexOf('ecm') >= 0) {
this.setXsxsFlag();
}
this.drawer = false;
}
},
immediate: true
}
}
};
......@@ -143,16 +154,25 @@ export default {
font-weight: 500;
display: flex;
justify-content: space-between;
.intro {
width: 64px;
height: 22px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #1890ff;
line-height: 22px;
cursor: pointer;
padding-right: 30px;
.intro_wrap {
display: flex;
align-items: center;
.introlIcon {
width: 20px;
height: 20px;
margin-right: 8px;
}
.intro {
width: 64px;
height: 22px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #1890ff;
line-height: 22px;
cursor: pointer;
padding-right: 30px;
}
}
}
}
......
......@@ -50,9 +50,12 @@ export const ecmTouchEffectFunnelChart = params => requests(PREFIX + 'ecmTouchEf
// 智能营销--导购线索
export const ecmGuideCluesColumnDiagram = params => requests(PREFIX + 'ecmGuideCluesColumnDiagram', params);
export const ecmGuideCluesTable = params => requests(PREFIX + 'ecmGuideCluesTable', params);
export const ecmGuideCluesTaskTable = params => requests(PREFIX + 'ecmGuideCluesTaskTable', params); // 任务完成情况表格
// 智能营销--后台线索
export const ecmHeadCluesColumnDiagram = params => requests(PREFIX + 'ecmHeadCluesColumnDiagram', params);
export const ecmHeadGuideCluesTable = params => requests(PREFIX + 'ecmHeadGuideCluesTable', params);
export const ecmHeadCluesTaskTab = params => requests(PREFIX + 'ecmHeadCluesTaskTab', params); // 表格
export const ecmHeadCluesTaskTabHead = params => requests(PREFIX + 'ecmHeadCluesTaskTabHead', params); //表头
export const getUseStoredFalg = params => requests(PREFIX + 'get-ecm-store-flag', params);
......
......@@ -36,7 +36,7 @@
<el-button type="text">删除</el-button>
</dm-delete>
<el-button type="text" @click="toRecord(scope.row)">记录</el-button>
<el-button type="text" @click="toTouch(scope.row)">触达效果</el-button>
<el-button type="text" @click="toTouch(scope.row)" v-if="xsxsFlag && isOpenFlag(scope.row.analyseJson)">触达效果</el-button>
</template>
</el-table-column>
</el-table>
......@@ -45,7 +45,7 @@
</template>
<script>
import activitySelect from '@/components/activity-select/index.vue';
import { loadEcmList, deleteEcm, offlineEcmPlan, getUseStoredFalg } from '@/service/api/ecmApi.js';
import { loadEcmList, deleteEcm, offlineEcmPlan, getUseStoredFalg, getXsxsFalg } from '@/service/api/ecmApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
const marketingTypeOptions = [
{ value: '', label: '所有发送类型' },
......@@ -81,7 +81,8 @@ export default {
searchName: '',
currentPage: 1,
pageSize: 20,
showSelfFlag: ''
showSelfFlag: '',
xsxsFlag: 0 // 销售线索开关 1 开 0 关
},
total: 0,
loading: false,
......@@ -141,10 +142,50 @@ export default {
return result;
}
},
{ label: '营销人数', prop: 'ecmMemberNum', minWidth: '120', align: 'left' },
{ label: '触达人数', prop: 'ecmSuccessNum', minWidth: '120', align: 'left' },
{ label: '转换人数', prop: 'ecmTranNum', minWidth: '120', align: 'left' },
{ label: '线索转化收益', prop: 'ecmTranIncome', minWidth: '120', align: 'left' }
{
label: '计划触达人数',
prop: 'ecmMemberNum',
minWidth: '120',
align: 'left',
formatter(row) {
let openFlag = JSON.parse(row.analyseJson);
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.ecmMemberNum && row.ecmMemberNum != 0) ? '--' : row.ecmMemberNum;
}
},
{
label: '触达人数',
prop: 'ecmSuccessNum',
minWidth: '120',
align: 'left',
formatter(row) {
let openFlag = JSON.parse(row.analyseJson);
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.ecmSuccessNum && row.ecmSuccessNum != 0) ? '--' : row.ecmSuccessNum;
}
},
{
label: '转换人数',
prop: 'ecmTranNum',
minWidth: '120',
align: 'left',
formatter(row) {
let openFlag = JSON.parse(row.analyseJson);
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.ecmTranNum && row.ecmTranNum != 0) ? '--' : row.ecmTranNum;
}
},
{
label: '线索转化收益',
prop: 'ecmTranIncome',
minWidth: '120',
align: 'left',
formatter(row) {
let openFlag = JSON.parse(row.analyseJson);
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.ecmTranIncome && row.ecmTranIncome != 0) ? '--' : row.ecmTranIncome;
}
}
],
tableList: []
};
......@@ -158,12 +199,12 @@ export default {
this.marketingTypeOptions = [...this.marketingTypeOptions, { value: 'grade', label: '会员卡升级' }];
}
});
this.loadEcmList();
this.$store.commit('aside_handler', false);
this.$store.commit('mutations_breadcrumb', [
{ name: '营销管理', path: '' },
{ name: '智能营销', path: '/ecm' }
]); // eslint-disable-line
this.setXsxsFlag();
},
methods: {
search() {
......@@ -185,7 +226,6 @@ export default {
params.effectType = -1; // 全部时效的默认值是-1
}
let res = await loadEcmList(params);
console.log(res);
this.tableList = res.result.result || [];
this.total = res.result.totalCount;
this.loading = false;
......@@ -233,6 +273,35 @@ export default {
} catch (err) {
this.$tips({ type: 'error', message: '下线失败!' });
}
},
setXsxsFlag() {
// 是否开启销售线索
getXsxsFalg().then(res => {
this.xsxsFlag = res.result;
if (this.xsxsFlag == 0) {
let arr = [];
this.tableHeader.forEach(item => {
if (item.label != '线索转化收益' && item.label != '转换人数' && item.label != '触达人数' && item.label != '计划触达人数') {
arr.push(item);
}
});
this.tableHeader = arr;
}
this.loadEcmList();
});
}
},
computed: {
isOpenFlag(item) {
return item => {
item = JSON.parse(item);
if (!item) item = {};
if (item.open_flag == 0) {
return false;
} else {
return true;
}
};
}
}
};
......
......@@ -132,6 +132,19 @@
</li>
</ul>
</el-drawer>
<el-drawer title="数据说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h650" v-else-if="contentTitle == '智能营销'">
<ul class="content">
<li>更新频率:营销人次实时统计,其余指标1天更新1次;</li>
<li>
①【营销人次】:本智能营销计划预计营销的人次;<br />
②【计划触达人数】:本计划计划触达的人数。一些预计营销的人中因为一些系统特殊情况导致触发失败的,不会计入在内;重复营销的,只记1人。<br />
③【触达人数】:通过计划中各种营销方式触达到的会员人数;<br />
④【转化人数】:触达的人中,在触达收益有效期内前来消费的人数;<br />
⑤【线索转化收益】:转化人数带来的收益,只计销售单,不看退货单和换货单;金额是应付还是实付看ERP传入的值。<br />
⑥ 说明:未开启营销分析开关的,除营销人次外其他指标信息不统计;
</li>
</ul>
</el-drawer>
</template>
<script>
......
<template>
<div class="funnel_wrap" v-if="data.length">
<div class="funnel_wrap">
<div :id="nodeName"></div>
<div class="funnelDesc">
<p>
......
......@@ -7,14 +7,14 @@
<div class="middle" v-for="(item, index) in data" :key="index">
<template v-if="item.isSales == 0">
<div class="left" v-if="isReference && !isCluePage">
<i class="iconfont icon"></i>
<img :src="require('@/assets/img/experimentIcon.png')" class="icon" />
<span class="title">实验组</span>
<span>-计划触达</span>
</div>
</template>
<template v-else>
<div class="left" v-if="isReference && !isCluePage">
<i class="iconfont icon"></i>
<img :src="require('@/assets/img/referenceIcon.png')" class="icon" />
<span class="title">参照组</span>
<span>-计划非触达</span>
</div>
......@@ -53,40 +53,9 @@
</div>
<div>
<p>转化收益</p>
<p>{{ item.convSalesAmt.toLocaleString() }}</p>
<p>{{ convSalesAmt(item.convSalesAmt) }}</p>
</div>
</div>
<!--线索页的列表-->
<!-- <div class="right" v-else :class="[isCluePage ? 'cluePage' : '']">
<div>
<p>计划触达人次</p>
<p>{{ item.touchMbrNum.toLocaleString() }}</p>
</div>
<div>
<p>任务完成率</p>
<p>{{ item.taskRate ? item.taskRate.toFixed(2) + '%' : '-' }}</p>
</div>
<div>
<p>
触达人数<span>(触达率 {{ item.touchRate + '%' }})</span>
</p>
<p>{{ item.touchMbrNum.toLocaleString() }}</p>
</div>
<div>
<p>
转化人数
</p>
<p>{{ item.convMbrNum.toLocaleString() }}</p>
</div>
<div>
<p>转化订单数</p>
<p>{{ item.convOrderCnt.toLocaleString() }}</p>
</div>
<div>
<p>转化收益</p>
<p>{{ item.convSalesAmt.toLocaleString() }}</p>
</div>
</div> -->
</div>
</div>
</template>
......@@ -115,6 +84,21 @@ export default {
type: Array,
default: () => []
}
},
computed: {
convSalesAmt(num) {
return num => {
if (num) {
num = parseFloat(num).toFixed(2);
let i = num.indexOf('.');
let before = parseInt(num.slice(0, i)).toLocaleString();
let last = num.slice(i);
return before + last;
} else {
return '0';
}
};
}
}
};
</script>
......
......@@ -5,26 +5,33 @@
</template>
<script>
import * as G2 from '@antv/g2';
import { ecmTouchEffectColumnDiagram, ecmGuideCluesColumnDiagram, ecmHeadGuideCluesTable } from '@/service/api/ecmApi.js';
export default {
name: 'touch-charts',
props: {
data: {
type: Array,
default: () => []
}
type: String // 0是触达效果 1导购线索 2后台线索
},
data() {
return {
chartData: []
};
},
mounted() {
this.draw();
this.getChartData();
},
methods: {
draw() {
let valueArr = this.chartData.filter(item => item.value >= 0);
let rateArr = this.chartData.filter(item => item.rate >= 0);
let valueFlag = valueArr.every(item => item.value == 0); // value是否出现全为0的情况
let rateFlag = rateArr.every(item => item.rate == 0); // rate是否出现全为0的情况
const chart = new G2.Chart({
container: 'draw_g2',
forceFit: true,
height: 376,
padding: [55, 90, 46, 68]
});
chart.source(this.data);
chart.source(this.chartData);
chart.tooltip({
showMarkers: false,
shared: true
......@@ -50,10 +57,12 @@ export default {
},
value: {
min: 0,
ticks: valueFlag ? [0, 2, 4, 6, 8] : '',
tickCount: 5
},
rate: {
min: 0,
ticks: rateFlag ? [0, 2, 4, 6, 8] : '',
tickCount: 5
}
});
......@@ -84,6 +93,25 @@ export default {
.position('date*rate')
.color('name', ['#FF9F40']);
chart.render();
},
getChartData() {
let meth;
if (this.type == 0) meth = ecmTouchEffectColumnDiagram;
else if (this.type == 1) meth = ecmGuideCluesColumnDiagram;
else meth = ecmHeadGuideCluesTable;
meth({ ecmPlanId: 1 }).then(res => {
this.chartData = res.result.map(item => {
if (item.name == '线索转化收益') {
item.rate = item.vaule * 1;
delete item.vaule;
} else {
item.value = item.vaule * 1;
delete item.vaule;
}
return item;
});
this.draw();
});
}
}
};
......
<template>
<div class="touch">
<div class="touch" v-loading="loading">
<div class="dm-wrap">
<div class="title">
<h2>触达效果</h2>
</div>
<touch-charts v-if="chartData.length" :data="chartData" />
<market-list :isRepeat="true" :data="marketListData" />
<touch-charts :type="0" />
<market-list v-if="marketListData.length" :isRepeat="true" :data="marketListData" />
</div>
<div class="dm-wrap table">
<div class="title">
<span>触达方式对比</span>
<span>文案说明文案说明文案说明文案说明</span>
</div>
<div class="content">
<div class="left">
......@@ -30,10 +29,10 @@
<div class="right">
<div class="top">
<div class="detail" @click="toClue(1)">查看详情</div>
<div class="allPlan_title">整体计划</div>
<div class="allPlan_title">导购线索</div>
<div class="allPlan_content">
<div class="left">
<i class="icon"></i>
<img :src="require('@/assets/img/funnelIcon1.png')" class="icon" />
<div>
<p>线索转化收益</p>
<p>{{ clueRate[1] }}</p>
......@@ -46,10 +45,10 @@
</div>
<div class="bottom">
<div class="detail" @click="toClue(2)">查看详情</div>
<div class="allPlan_title">整体计划</div>
<div class="allPlan_title">后台线索</div>
<div class="allPlan_content">
<div class="left">
<i class="icon"></i>
<img :src="require('@/assets/img/funnelIcon2.png')" class="icon" />
<div>
<p>线索转化收益</p>
<p>{{ clueRate[2] }}</p>
......@@ -69,15 +68,15 @@
import funnel from '@/views/ecm/touch-components/funnel.vue';
import marketList from '@/views/ecm/touch-components/market-list.vue';
import touchCharts from '@/views/ecm/touch-components/touch-charts.vue';
import { ecmTouchEffectColumnDiagram, ecmTouchEffectTable, ecmTouchEffectFunnelChart } from '@/service/api/ecmApi.js';
import { ecmTouchEffectTable, ecmTouchEffectFunnelChart } from '@/service/api/ecmApi.js';
export default {
name: 'ecm',
data() {
return {
marketListData: [],
chartData: [],
funnelData: [],
clueRate: []
clueRate: [],
loading: true
};
},
components: {
......@@ -89,7 +88,6 @@ export default {
let planName = this.$route.query.name;
this.$store.commit('mutations_breadcrumb', [{ name: '营销管理', path: '' }, { name: '智能营销', path: '/ecm' }, { name: `${planName} - 触达效果` }]); // eslint-disable-line
this.getMarketList();
this.getChartData();
this.getFunnelData();
},
methods: {
......@@ -99,20 +97,6 @@ export default {
// type 1为导购线索 2为后台线索
this.$router.push({ path: '/ecm/clue', query: { planName, id, type } });
},
getChartData() {
ecmTouchEffectColumnDiagram({ ecmPlanId: 1 }).then(res => {
this.chartData = res.result.map(item => {
if (item.name == '线索转化收益') {
item.rate = item.vaule * 1;
delete item.vaule;
} else {
item.value = item.vaule * 1;
delete item.vaule;
}
return item;
});
});
},
getMarketList() {
ecmTouchEffectTable({ ecmPlanId: 1 }).then(res => {
this.marketListData = res.result.map(item => {
......@@ -126,15 +110,18 @@ export default {
ecmTouchEffectFunnelChart({ ecmPlanId: 1 }).then(res => {
this.funnelData = this.formatFunnelData(res.result);
this.clueRate = res.result.map(item => {
console.log(item);
item = parseFloat(item).toFixed(2);
let i = item.indexOf('.');
let before = parseInt(item.slice(0, i)).toLocaleString();
let last = item.slice(i);
return before + last;
if (item.线索转化收益) {
item = parseFloat(item.线索转化收益).toFixed(2);
let i = item.indexOf('.');
let before = parseInt(item.slice(0, i)).toLocaleString();
let last = item.slice(i);
return before + last;
} else {
return '0';
}
});
this.loading = false;
});
console.log(this.clueRate);
},
formatFunnelData(obj) {
obj = obj.map((item, index) => {
......@@ -219,6 +206,8 @@ export default {
margin-right: 5px;
.top {
height: 185px;
background: url('~@/assets/img/fuunelBg2.png') no-repeat;
background-size: cover;
box-sizing: border-box;
.title {
width: 64px;
......@@ -276,6 +265,8 @@ export default {
background: #f0f5ff;
border-radius: 6px;
position: relative;
background: url('~@/assets/img/fuunelBg1.png') no-repeat;
background-size: cover;
.detail {
position: absolute;
width: 83px;
......@@ -317,6 +308,7 @@ export default {
font-family: PingFangSC-Regular, PingFang SC;
margin-top: 48px;
padding-left: 27px;
background: transparent;
.icon {
width: 56px;
height: 56px;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment