Commit 0775d99c by liuchenxi

update: 触达效果

parent 02615737
<template>
<i class="iconfont icon-xinxixianshi"></i>
</template>
<script>
export default {};
</script>
<style scoped>
i {
margin-left: 5px;
font-size: 13px;
color: #909399;
}
</style>
export default {
computed: {
formatterNum() {
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterToFixed() {
return val => (!val ? '0.00' : parseFloat(val).toFixed(2));
},
formatterRate() {
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
}
};
......@@ -58,12 +58,14 @@ import { ecmGuideCluesTable, ecmHeadCluesTaskTab, ecmHeadGuideCluesTable, ecmGui
import { otherTableExoport, allConvTableExport, taskTableExport, headClueTableExport, getTabList, ecmGuideCluesTouchEffectTotalTabHead, ecmGuideCluesTouchEffectTabHead, ecmGuideCluesTaskTableHead, ecmHeadCluesTaskTabHead } from '@/service/api/ecmApi';
import marketList from '@/views/ecm/touch-components/market-list.vue';
import touchCharts from '@/views/ecm/touch-components/touch-charts.vue';
import formatterNum from '@/mixins/validateNum';
export default {
name: 'touch-clue',
components: {
marketList,
touchCharts
},
mixins: [formatterNum],
data() {
return {
tableHeader: [],
......@@ -165,9 +167,7 @@ export default {
meth({ ecmPlanId: this.$route.query.id }).then(res => {
let data = res.result || {};
if (this.type == 1) {
// 导购线索时,加入一个标识,方便组件判断,同时计算任务完成率
data.taskRate = (data.cplTaskTotalCnt / data.taskCnt) * 100;
data.flag = true;
}
data.touchRate = data.touchMbrNum * 1 && data.planMbrNum * 1 ? ((data.touchMbrNum / data.planMbrNum) * 100).toFixed(2) : 0;
data.transformRate = data.convMbrNum * 1 && data.touchMbrNum * 1 ? ((data.convMbrNum / data.touchMbrNum) * 100).toFixed(2) : 0;
......@@ -448,19 +448,19 @@ export default {
'text-align': 'center',
'line-height': '16px'
};
},
formatterNum() {
// return val => (!val && val != 0 ? '--' : parseInt(val).toLocaleString());
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterToFixed() {
// return val => (!val && val != 0 ? '--' : parseFloat(val).toFixed(2));
return val => (!val ? '0.00' : parseFloat(val).toFixed(2));
},
formatterRate() {
// return val => (!val && val != 0 ? '--' : parseFloat(val).toFixed(2) + '%');
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
// formatterNum() {
// // return val => (!val && val != 0 ? '--' : parseInt(val).toLocaleString());
// return val => (!val ? '0' : parseInt(val).toLocaleString());
// },
// formatterToFixed() {
// // return val => (!val && val != 0 ? '--' : parseFloat(val).toFixed(2));
// return val => (!val ? '0.00' : parseFloat(val).toFixed(2));
// },
// formatterRate() {
// // return val => (!val && val != 0 ? '--' : parseFloat(val).toFixed(2) + '%');
// return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
// }
}
// watch: {
// uuid(val) { // 门店选择器
......
<!--群发消息总数居-->
<template>
<div class="dm-wrap batch_list">
<div class="title flex_between">
<div class="flex_between">
<img :src="require('@/assets/img/icon-send.png')" class="img" />
<h2>群发任务</h2>
</div>
<el-link type="primary" :underline="false" style="color: #1890ff">主要链接</el-link>
</div>
</div>
</template>
<script>
import formatterNum from '@/mixins/validateNum';
export default {
name: 'batch-send',
mixins: [formatterNum],
data() {
return {};
},
methods: {}
};
</script>
<style scoped lang="scss">
.batch_list {
padding: 19px 20px 30px !important;
margin-bottom: 10px !important;
font-family: PingFangSC-Regular, PingFang SC;
.title {
height: 22px;
margin-bottom: 30px;
line-height: 22px;
h2 {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 700;
color: #303133;
}
}
.img {
transform: scale(0.5);
}
.flex_between {
display: flex;
justify-content: space-between;
align-items: center;
}
}
</style>
<!--卡券收益总数据-->
<template>
<div class="middle">
<div class="item-bg bg-purper item center_flex min-w-173">
<div>
<p>触达人数<tip /></p>
<p>{{ formatterNum(list.touchMbrNum) }}</p>
</div>
</div>
<div class="item-arrow purper center_flex">
<p>领取率</p>
<p>{{ formatterRate((list.getMbrNum / list.touchMbrNum) * 100) }}</p>
</div>
<div class="item-bg bg-purper item center_flex min-w-173">
<div>
<p>领取人数<tip /></p>
<p>{{ formatterNum(list.getMbrNum) }}</p>
</div>
</div>
<div class="item-arrow purper center_flex">
<p>使用率</p>
<p>{{ formatterRate((list.useMbrNum / list.getMbrNum) * 100) }}</p>
</div>
<div class="item-bg bg-purper item center_flex min-w-214">
<div>
<p>使用人数<tip /></p>
<p>{{ formatterNum(list.useMbrNum) }}</p>
</div>
</div>
<div class="item-bg bg-purper item min-w-517">
<div>
<p>销售单数 <tip /></p>
<p>{{ formatterNum(list.orderCnt) }}</p>
</div>
<div>
<p>销售单金额(元)<tip /></p>
<p>{{ parseFloat(list.salesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }}</p>
</div>
</div>
</div>
</template>
<script>
import formatterNum from '@/mixins/validateNum';
import tip from '@/components/tip';
export default {
name: 'market-list',
components: { tip },
props: {
list: {
type: Array,
default: () => {}
}
},
mixins: [formatterNum]
};
</script>
<style lang="scss" scoped>
.middle {
font-family: PingFangSC-Medium, PingFang SC;
height: 100px;
display: flex;
align-items: center;
justify-content: space-around;
.item-bg {
flex: 1;
height: 100%;
border-radius: 6px;
box-sizing: border-box;
&.bg-green {
background: #e3fff8;
}
&.bg-purper {
background: #f0f5ff;
}
p {
&:nth-child(1) {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
color: #606266;
line-height: 20px;
padding-top: 4px;
margin-bottom: 6px;
}
&:nth-child(2) {
font-size: 24px;
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
line-height: 28px;
}
.active {
color: #f5222d;
margin-left: 4px;
}
}
}
.item-arrow {
width: 96px;
height: 71px;
margin-right: 8px;
&.purper {
background: url('~@/assets/img/arrow_purper.png');
}
&.green {
background: url('~@/assets/img/arrow_green.png');
}
p {
width: 47px;
font-size: 12px;
line-height: 20px;
color: #606266;
font-family: PingFangSC-Regular, PingFang SC;
&:nth-child(2) {
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
}
}
}
}
.center_flex {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.min-w-173 {
min-width: 173px;
}
.min-w-214 {
min-width: 214px;
}
.min-w-517 {
min-width: 517px;
margin-left: 5px;
display: flex;
align-items: center;
padding: 0 42px 0 47px;
box-sizing: border-box;
div {
flex: 1;
text-align: left;
}
}
</style>
......@@ -5,37 +5,8 @@
<span>计划中的卡券,计划中卡券触达的人群使用该卡券消费的收益,包含已过计划收益有效期的数据</span>
</div>
<template v-if="tableData.length">
<div class="list">
<div class="list_left">
<div>
<p>触达人数</p>
<p>{{ formatterNum(list.touchMbrNum) }}</p>
</div>
<div>
<p>
领取人数<span> (领取率 {{ formatterRate((list.getMbrNum / list.touchMbrNum) * 100) }}</span>
</p>
<p>{{ formatterNum(list.getMbrNum) }}</p>
</div>
<div>
<p>
使用人数<span> (使用率 {{ formatterRate((list.useMbrNum / list.getMbrNum) * 100) }}</span>
</p>
<p>{{ formatterNum(list.useMbrNum) }}</p>
</div>
</div>
<div class="list_right">
<div>
<p>销售单数</p>
<p>{{ formatterNum(list.orderCnt) }}</p>
</div>
<div>
<p>销售单金额(元)</p>
<p>{{ parseFloat(list.salesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }}</p>
</div>
</div>
</div>
<el-table :data="tableData" v-if="tableData.length > 1" style="margin-bottom:47px" max-height="710">
<card-profit-sum :list="list" />
<el-table :data="tableData" v-if="tableData.length > 1" style="margin:20px 0 20px" max-height="710">
<el-table-column :prop="cardName" label="卡券名称" min-width="150">
<template slot-scope="scope">
<div class="name" v-if="scope.row.cardName">
......@@ -69,6 +40,8 @@
</template>
<script>
import formatterNum from '@/mixins/validateNum';
import cardProfitSum from './card-profit-sum.vue';
export default {
name: 'card-profit',
props: {
......@@ -81,6 +54,8 @@ export default {
default: () => []
}
},
mixins: [formatterNum],
components: { cardProfitSum },
data() {
return {
tableHeader: [
......@@ -99,21 +74,13 @@ export default {
if (row.status == 0) return;
window.open(window.location.origin + `/marketing/#/card/edit/${row.cardId}`);
}
},
computed: {
formatterNum() {
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterRate() {
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
}
};
</script>
<style scoped lang="scss">
.card_profit {
padding: 19px 34px 7px 28px !important;
padding: 20px !important;
margin-bottom: 10px !important;
font-family: PingFangSC-Regular, PingFang SC;
.title {
......@@ -134,59 +101,6 @@ export default {
color: #909399;
}
}
.list {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
.list_left {
margin-right: 10px;
}
.list_left,
.list_right {
background: #f0f5ff;
border-radius: 6px;
flex: 1;
height: 115px;
padding-left: 40px;
box-sizing: border-box;
display: flex;
div {
flex: 1;
padding-top: 26px;
&:nth-of-type(1) {
flex: 0.8;
}
p {
&:nth-child(1) {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #606266;
line-height: 20px;
padding-top: 4px;
margin-bottom: 6px;
}
&:nth-child(2) {
font-size: 24px;
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
line-height: 28px;
}
.active {
color: #f5222d;
margin-left: 4px;
}
span {
color: #303133;
}
}
}
}
.list_right div {
text-align: center;
}
}
.name {
color: #303133;
line-height: 22px;
......
......@@ -6,55 +6,62 @@
</div>
<div class="middle" v-for="(item, index) in data" :key="index">
<template v-if="item.isSales == 1">
<div class="left" v-if="isReference && !isCluePage">
<div class="left center_flex" v-if="isReference && !isCluePage">
<img :src="require('@/assets/img/experimentIcon.png')" class="icon" />
<span class="title">实验组</span>
<span>-计划触达</span>
<div style="margin-top: 7px">
<span class="title">实验组</span>
<span>-计划触达</span>
</div>
</div>
</template>
<template v-else>
<div class="left" v-if="isReference && !isCluePage">
<div class="left center_flex" v-if="isReference && !isCluePage">
<img :src="require('@/assets/img/referenceIcon.png')" class="icon" />
<span class="title">参照组</span>
<span>-计划非触达</span>
<div style="margin-top: 7px">
<span class="title">参照组</span>
<span>-计划非触达</span>
</div>
</div>
</template>
<!--非线索页列表-->
<div class="right" :class="[isCluePage ? 'cluePage' : '']" ref="right">
<!-- <div v-if="!isCluePage">
<p>计划人次</p>
<p>{{ formatterNum(item.planMbrTimes) }}</p>
</div> -->
<div>
<p>{{ isCluePage ? '计划触达人数' : '计划人数' }}</p>
<p>{{ formatterNum(item.planMbrNum) }}</p>
<div class="right">
<div class="item-bg bg-purper item center_flex min-w-173">
<div>
<p>{{ item.isSales == 1 ? '计划触达人数' : '参照组人数' }}<tip /></p>
<p>{{ formatterNum(item.planMbrNum) }}</p>
</div>
</div>
<div v-if="isCluePage && item.flag" class="taskRate">
<p>
任务完成率<span>(任务总数 {{ formatterNum(item.taskCnt) }})</span>
</p>
<p>{{ formatterRate(item.taskRate) }}</p>
<div class="item-arrow purper center_flex" v-if="item.isSales == 1">
<p>触达率</p>
<p>{{ formatterRate(item.touchRate) }}</p>
</div>
<div v-if="isCluePage || item.isSales == 1" class="touchMbr">
<p>
触达人数<span>(触达率 {{ formatterRate(item.touchRate) }})</span>
</p>
<p>{{ formatterNum(item.touchMbrNum) }}</p>
<div v-else style="width: 96px"></div>
<div class="item-bg bg-purper item center_flex min-w-173" v-if="item.isSales == 1">
<div>
<p>实际触达人数<tip /></p>
<p>{{ formatterNum(item.touchMbrNum) }}</p>
</div>
</div>
<div v-else class="touchMbr touchMbr2">- -</div>
<div class="convMbr">
<p>
转化人数<span :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">(转化率 {{ formatterRate(item.transformRate) }})</span>
</p>
<p>{{ formatterNum(item.convMbrNum) }}</p>
<div class="item-none center_flex min-w-173" v-else>- -</div>
<div class="item-arrow green center_flex">
<p :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">转化率</p>
<p :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">{{ formatterRate(item.transformRate) }}</p>
</div>
<div>
<p>转化订单数</p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
<div class="item-bg bg-green item center_flex min-w-206">
<div>
<p>{{ item.isSales == 1 ? '触达顾客转化人数' : '转化人数' }}<tip /></p>
<p>{{ formatterNum(item.convMbrNum) }}</p>
</div>
</div>
<div>
<p>转化收益</p>
<p>{{ parseFloat(item.convSalesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }}</p>
<div class="item-bg bg-green item min-w-389">
<div>
<p>{{ item.isSales == 1 ? '触达顾客订单数' : '转化订单数' }}<tip /></p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
</div>
<div>
<p>{{ item.isSales == 1 ? '触达顾客转化收益' : '转化收益' }}<tip /></p>
<p>{{ parseFloat(item.convSalesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }}</p>
</div>
</div>
</div>
</div>
......@@ -62,8 +69,11 @@
</template>
<script>
import formatterNum from '@/mixins/validateNum';
import tip from '@/components/tip';
export default {
name: 'market-list',
components: { tip },
props: {
// 是否是线索页面
isCluePage: {
......@@ -88,14 +98,7 @@ export default {
batchTimes: String,
batchNum: Number | String
},
computed: {
formatterNum() {
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterRate() {
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
}
mixins: [formatterNum]
};
</script>
<style lang="scss" scoped>
......@@ -111,8 +114,7 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
margin-top: 10px;
margin: 10px 0;
.left {
font-size: 16px;
font-weight: 600;
......@@ -128,20 +130,16 @@ export default {
}
}
.middle {
margin-bottom: 4px;
height: 100px;
background: #f0f5ff;
border-radius: 6px;
display: flex;
align-items: center;
&:nth-of-type(3) {
margin: 20px 0 3px;
}
.left {
width: 226px;
height: 55px;
min-width: 173px;
height: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
padding: 0 35px 0 25px;
border-right: 1px solid#E4E7ED;
span {
font-size: 14px;
font-weight: 400;
......@@ -162,18 +160,26 @@ export default {
}
}
.right {
padding: 0 28px 0 32px;
height: 58px;
flex: 1;
display: flex;
justify-content: space-between;
div {
align-items: center;
justify-content: space-around;
flex: 1;
height: 100px;
.item-bg {
flex: 1;
height: 100%;
border-radius: 6px;
box-sizing: border-box;
&.bg-green {
background: #e3fff8;
}
&.bg-purper {
background: #f0f5ff;
}
p {
&:nth-child(1) {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #606266;
line-height: 20px;
padding-top: 4px;
......@@ -192,24 +198,61 @@ export default {
}
}
}
.touchMbr2 {
align-items: center;
padding-top: 19px;
.item-arrow {
width: 96px;
height: 71px;
&.purper {
background: url('../../../assets/img/arrow_purper.png');
}
&.green {
background: url('../../../assets/img/arrow_green.png');
}
p {
width: 47px;
font-size: 12px;
line-height: 20px;
color: #606266;
font-family: PingFangSC-Regular, PingFang SC;
&:nth-child(2) {
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
}
}
}
.convMbr,
.touchMbr,
.taskRate {
flex: 1.3;
.item-none {
flex: 1;
height: 100px;
font-family: PingFangSC-Regular, PingFang SC;
color: #606266;
line-height: 20px;
}
}
.cluePage {
padding-left: 65px;
.center_flex {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.active {
color: #f5222d !important;
}
&:nth-of-type(3) {
background: #f5f7fa;
margin-bottom: 7px;
.left {
padding-right: 21px;
.min-w-173 {
min-width: 173px;
}
.min-w-206 {
min-width: 206px;
}
.min-w-389 {
min-width: 389px;
margin-left: 5px;
display: flex;
align-items: center;
padding: 0 42px 0 47px;
box-sizing: border-box;
div {
flex: 1;
text-align: left;
}
}
}
......
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