Commit ad7d2b78 by 黑潮

Merge branch 'feature/销售线索2期' into master

# Conflicts:
#	index.html
#	src/views/ecm/form.js
parents 868209b3 1c7ff8b3
......@@ -42,6 +42,6 @@
<script src="//web-1251519181.file.myqcloud.com/components/card.2.0.20.js"></script><!-- 卡券弹窗 -->
<script src="//web-1251519181.file.myqcloud.com/components/radio-transfer.1.0.13.js"></script><!-- 单选穿梭框 -->
<script src="//web-1251519181.file.myqcloud.com/components/steps.1.0.1.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/material.1.0.4.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/material.1.0.7.js"></script>
</body>
</html>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1622596985820" class="icon" viewBox="0 0 1194 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9365" xmlns:xlink="http://www.w3.org/1999/xlink" width="233.203125" height="200"><defs><style type="text/css"></style></defs><path d="M340.423263 869.316447c0 14.674888-11.944676 26.704883-26.619564 26.704883H196.831195a26.704883 26.704883 0 0 1-26.619564-26.704883V538.790202c0-14.674888 11.944676-26.704883 26.619564-26.704883h116.887185c14.674888 0 26.619563 11.944676 26.619564 26.704883v330.526245z m340.167305 0c0 14.674888-11.944676 26.619563-26.619563 26.619564h-116.887186a26.704883 26.704883 0 0 1-26.619563-26.619564V26.875521c0-14.674888 11.944676-26.704883 26.619563-26.704883h116.887186c14.674888 0 26.619563 11.944676 26.619563 26.704883v842.440926z m340.252625 0c0 14.674888-11.944676 26.704883-26.619564 26.704883h-116.887185a26.704883 26.704883 0 0 1-26.619563-26.704883V282.832861c0-14.674888 11.944676-26.704883 26.534244-26.704882h116.972504c14.674888 0 26.619563 11.944676 26.619564 26.704882v586.483586zM69.27912 1024a26.704883 26.704883 0 0 1-26.619563-26.704883v-31.909348C42.659557 950.625562 54.604233 938.680887 69.27912 938.680887h1052.581903c14.504249 0 26.534244 11.944676 26.534244 26.704882v31.909348c0 14.674888-11.944676 26.704883-26.534244 26.704883H69.27912z" fill="#1989FA" p-id="9366"></path></svg>
\ No newline at end of file
......@@ -134,7 +134,7 @@
height: 57px;
line-height: 57px;
border-top: 1px solid #E4E7ED;
z-index: 999;
z-index: 1001;
&.on{
width: calc(100% - 200px);
left: 200px;
......@@ -264,4 +264,4 @@
background:rgba(255,247,230,1);
border:1px solid rgba(255,213,145,1);
}
}
\ No newline at end of file
}
......@@ -64,7 +64,7 @@ export default {
return this.$store.state.marketing.layoutTips;
},
showDescription() {
return (this.$route.path.indexOf('ecm/list') >= 0 || this.$route.path.indexOf('ecm/clue') >= 0 || this.$route.path.indexOf('ecm/touch') >= 0) && this.$store.state.marketing.xsxsFlag;
return (this.$route.path.indexOf('ecm/list') >= 0 && this.$store.state.marketing.xsxsFlag) || this.$route.path.indexOf('ecm/clue') >= 0 || this.$route.path.indexOf('ecm/touch') >= 0;
}
},
// watch:{
......
......@@ -8,5 +8,6 @@ export const ecmTypes = [
{ value: 'teltask', label: '话务', visible: true },
{ value: 'integral', label: '积分', visible: true },
{ value: 'grade', label: '会员卡升级', visible: false },
{ value: 'qywx', label: '企微任务', visible: false }
{ value: 'qywx', label: '企微任务', visible: false },
{ value: 'qfxx', label: '群发消息', visible: false }
];
......@@ -53,7 +53,7 @@ export default {
handleXxSyspClick(visible = true) {
this.xxSysp.visible = visible;
// refs.selector放在modal里,初始化的时候并没有渲染
if (!this.xxSysp.isResign && (this.isEdit || this.isInfo)) {
if (!this.xxSysp.isResign && (this.isEdit || this.isInfo || this.isCopyEcm)) {
this.$nextTick(() => {
this.$refs.selector.conditions = this.xxSysp.conditionList;
this.xxSysp.isResign = true;
......
......@@ -6,6 +6,23 @@ export default {
redirect: '/ecm/list',
children: [
{
path: 'log',
name: '操作日志',
component: () => import(/* webpackChunkName: "ecm" */ '../../views/ecm/operator-log.vue'),
meta: {
path: '/ecm/list'
}
},
{
path: 'copy/:id',
name: '智能营销新增',
component: () => import(/* webpackChunkName: "ecm" */ '../../views/ecm/form.vue'),
meta: {
type: 'copy',
path: '/ecm/list'
}
},
{
path: 'list',
name: '智能营销',
component: () => import(/* webpackChunkName: "ecm" */ '../../views/ecm/list.vue'),
......
......@@ -51,21 +51,36 @@ export const ecmTouchEffectColumnDiagram = params => requests(PREFIX + 'ecmTouch
export const ecmTouchEffectTable = params => requests(PREFIX + 'ecmTouchEffectTable', params);
export const ecmTouchEffectFunnelChart = params => requests(PREFIX + 'ecmTouchEffectFunnelChart', params);
export const ecmPlanTouchConfig = params => requests(PREFIX + 'ecmPlanTouchConfig', params);
export const getCardLeads = params => requests(PREFIX + 'getCardLeads', params); // 卡券收益
export const getCardLeadsList = params => requests(PREFIX + 'getCardLeadsList', params); // 卡券收益表格
// 智能营销--导购线索
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 ecmGuideCluesTouchEffectTotalTab = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTotalTab', params); // 触达效果-整体转化-表格
export const ecmGuideCluesTouchEffectTabTotalHead = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTabTotalHead', params); // 触达效果-整体转化-表头
export const ecmGuideCluesTouchEffectTab = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTab', params); // 触达效果-其他tab-表格
export const ecmGuideCluesTouchEffectTabHead = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTabHead', params); // 触达效果-其他tab-表头
// 智能营销--后台线索
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); //表头1 计划整体
export const ecmGuideCluesTouchEffectTabTotalHead1 = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTabTotalHead1', params); //表头2 区域合计
// 智能营销--表格的合计和计划整体数据
export const ecmGuideCluesTouchEffectTotalTabHead = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTotalTabHead', params); // 导购线索-触达效果整体计划表头
export const ecmGuideCluesTouchEffectTabHead = params => requests(PREFIX + 'ecmGuideCluesTouchEffectTabHead', params); //导购线索--触达效果其他表格表头
export const ecmGuideCluesTaskTableHead = params => requests(PREFIX + 'ecmGuideCluesTaskTableHead', params); // 触达效果-导购线索-任务完成情况表头
export const ecmHeadCluesTaskTabHead = params => requests(PREFIX + 'ecmHeadCluesTaskTabHead', params); // 总部线索表头
// 智能营销线索页tree
export const getGroupTree = params => requests(PREFIX + 'getGroupTree', params);
// export
export const getTabList = params => requests(PREFIX + 'ecmGuideCluesTableLabel', params);
// 智能营销导出表格
export const otherTableExoport = config.api + PREFIX + 'ecmGuideCluesTouchEffectTabExport'; // // 触达效果-其他tab-表格
export const allConvTableExport = config.api + PREFIX + 'ecmGuideCluesTouchEffectTotalTabExport'; // 触达效果-整体转化-表格
export const taskTableExport = config.api + PREFIX + 'ecmGuideCluesTaskTableExport'; // 触达效果-任务完成情况表格
export const headClueTableExport = config.api + PREFIX + 'ecmHeadCluesTaskTabExport'; // 触达效果-后台线索表格
export const getUseStoredFalg = params => requests(PREFIX + 'get-ecm-store-flag', params);
export const getXsxsFalg = params => requests(PREFIX + 'get-xsxs-open-flag', params);
export const getEcmLog = params => requests(PREFIX + 'get-ecm-operator_log', params);
......@@ -122,6 +122,7 @@ export default {
getXsxsFalg().then(res => {
if (res.result) {
// this.typeOptions.find(el => el.value == 'qywx').visible = true;
this.typeOptions.find(el => el.value == 'qfxx').visible = true;
}
});
getUseStoredFalg().then(res => {
......
......@@ -64,7 +64,7 @@
import { ecmCurrentSendInfos, ecmCurrentSendDetails, exportCurrentSendDetails } from '@/service/api/ecmApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
import memberInfo from '@/components/member-info/index.vue';
const typeOptions = [{ value: '', label: '所有类型' }, { value: 'teletext', label: '图文' }, { value: 'text', label: '文本' }, { value: 'image', label: '图片' }, { value: 'card', label: '卡券' }, { value: 'wxa', label: '小程序' }, { value: 'message', label: '短信' }, { value: 'teltask', label: '话务' }, { value: 'integral', label: '积分' }]; // eslint-disable-line
const typeOptions = [{ value: '', label: '所有类型' }, { value: 'teletext', label: '图文' }, { value: 'text', label: '文本' }, { value: 'image', label: '图片' }, { value: 'card', label: '卡券' }, { value: 'wxa', label: '小程序' }, { value: 'message', label: '短信' }, { value: 'teltask', label: '话务' }, { value: 'integral', label: '积分' }, { value: 'qywx', label: '企微任务' }, { value: 'qfxx', label: '群发消息' }]; // eslint-disable-line
export default {
name: 'ecm-current-list',
components: {
......@@ -96,7 +96,7 @@ export default {
loading: false,
infoHeader: [
{
label: '回复方式',
label: '营销方式',
prop: 'marketingType',
align: 'left',
formatter(row) {
......
......@@ -8,15 +8,35 @@
<el-option v-for="(v, i) in showMarketingTypeOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<dm-activity-select class="vertical-middle w150 mr5" onlySelect width="150" :actId.sync="listParams.marketingActivityId" @actUpdate="search"></dm-activity-select>
<el-select class="dm-select" clearable v-model="listParams.onlineStatus" placeholder="选择上线状态" @change="search">
<el-select class="dm-select" clearable v-model="listParams.onlineStatus" placeholder="选择状态" @change="search">
<el-option v-for="(v, i) in onlineOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-input v-model="listParams.searchName" class="w200" placeholder="输入计划名称" clearable @change="search"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-checkbox class="vertical-middle" v-if="$store.state.marketing.isShowSelf" v-model="listParams.showSelfFlag" :true-label="1" :false-label="0" label="仅看本人" border @change="search" />
<el-button class="fr" type="primary" @click="$router.push('/ecm/add')">新建计划</el-button>
<el-tooltip class="fr" content="只记录删除计划或编辑“进行中”计划的操作日志" placement="top" open-delay="200">
<el-button style="margin-right:10px" @click="$router.push('/ecm/log')">操作日志</el-button>
</el-tooltip>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width: 100%" element-loading-text="拼命加载中">
<el-table-column align="left" fixed="left" :min-width="200" show-overflow-tooltip label="计划名称">
<template slot-scope="scope">
<el-button v-if="xsxsFlag && isOpenFlag(scope.row.analyseJson)" style="vertical-align: middle;text-overflow: ellipsis;overflow: hidden;white-space: nowrap;width: 100%;text-align:left" type="text" @click="toTouch(scope.row)">
<img style="vertical-align: middle;width:14px;height:14px" src="../../assets/img/data.svg" />
<span style="vertical-align: middle;">{{ scope.row.ecmPlanName }}</span>
</el-button>
<span v-else>{{ scope.row.ecmPlanName }}</span>
</template>
</el-table-column>
<el-table-column v-for="(v, i) in tableHeader" :fixed="v.fixed" :show-overflow-tooltip="v.tooltip" :width="v.width" :min-width="v.minWidth" :align="v.align" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter">
<template slot="header" slot-scope="scope">
<span>
<span>{{ v.label }}</span>
<el-tooltip v-if="v.prop == 'timesForPeople'" content="刷新获取最新营销人次统计" placement="top" :open-delay="200">
<i class="el-icon-refresh" @click="loadEcmList"></i>
</el-tooltip>
</span>
</template>
<template slot-scope="scope">
<span class="vertical-middle" v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{ scope.row[v.prop] }}</span>
......@@ -26,8 +46,8 @@
<el-table-column label="操作" align="left" width="160" fixed="right">
<template slot-scope="scope">
<template v-if="scope.row.canEdit !== false">
<el-button type="text" v-if="scope.row.effectType == 0 || scope.row.effectType == 1" @click="editData(scope.row)">编辑</el-button>
<el-button type="text" v-if="scope.row.effectType == 2" @click="$router.push(`/ecm/info/${scope.row.ecmPlanId}`)">详情</el-button>
<el-button type="text" v-if="(scope.row.effectType == 0 || scope.row.effectType == 1) && scope.row.onlineStatus != 2" @click="editData(scope.row)">编辑</el-button>
<el-button type="text" v-else-if="scope.row.effectType == 2 || scope.row.onlineStatus == 2" @click="$router.push(`/ecm/info/${scope.row.ecmPlanId}`)">详情</el-button>
</template>
<!-- <dm-delete v-if="scope.row.effectType !== 2 && scope.row.onlineStatus === 1 && scope.row.canEdit !== false" @confirm="offlineEcmPlan(scope.row)" tips="是否下线该计划?">
<el-button type="text">下线</el-button>
......@@ -43,6 +63,7 @@
<el-dropdown-item :command="1">记录</el-dropdown-item>
<el-dropdown-item v-if="scope.row.effectType !== 2 && scope.row.onlineStatus === 1 && scope.row.canEdit !== false" @confirm="offlineEcmPlan(scope.row)" :command="2">下线</el-dropdown-item>
<el-dropdown-item v-if="xsxsFlag && isOpenFlag(scope.row.analyseJson)" :command="3">触达效果</el-dropdown-item>
<!-- <el-dropdown-item :command="4">复制新建</el-dropdown-item> -->
</el-dropdown-menu>
</el-dropdown>
</template>
......@@ -68,10 +89,10 @@ export default {
{ value: 2, label: '单次' }
], // eslint-disable-line
onlineOptions: [
{ value: '', label: '所有上线状态' },
{ value: 0, label: '待上线' },
{ value: 1, label: '已上线' },
{ value: 2, label: '已下线' }
{ value: '', label: '所有状态' },
{ value: 0, label: '未开始' },
{ value: 1, label: '进行中(待执行)' },
{ value: 2, label: '已结束(执行完毕)' }
], // eslint-disable-line
marketingTypeOptions,
listParams: {
......@@ -88,7 +109,6 @@ export default {
total: 0,
loading: false,
tableHeader: [
{ label: '计划名称', prop: 'ecmPlanName', minWidth: '160', align: 'left', fixed: 'left' },
{
label: '时效',
prop: 'effectType',
......@@ -123,7 +143,7 @@ export default {
},
{ label: '营销人次', prop: 'timesForPeople', minWidth: '120', align: 'left' },
{
label: '上线状态',
label: '状态',
prop: 'onlineStatus',
minWidth: '140',
align: 'left',
......@@ -131,13 +151,13 @@ export default {
let result = '--';
switch (row.onlineStatus) {
case 0:
result = '<span class="dm-status--primary">待上线</span>';
result = '<span class="dm-status--primary">未开始</span>';
break;
case 1:
result = '<span class="dm-status--success">已上线</span>';
result = row.effectType == 2 ? '<span class="dm-status--primary">待执行</span>' : '<span class="dm-status--primary--flash"></span><span class="vertical-middle">进行中</span>';
break;
case 2:
result = '<span class="dm-status--info">已下线</span>';
result = row.effectType == 2 ? '<span class="dm-status--info">执行完毕</span>' : '<span class="dm-status--info">已结束</span>';
break;
}
return result;
......@@ -186,6 +206,17 @@ export default {
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.ecmTranIncome && row.ecmTranIncome != 0) ? '--' : row.ecmTranIncome.toFixed(2);
}
},
{
label: '卡券收益',
prop: 'cardSalesAmt',
minWidth: '120',
align: 'left',
formatter(row) {
let openFlag = JSON.parse(row.analyseJson);
if (!openFlag) openFlag = {};
return openFlag.open_flag == 0 || (!row.cardSalesAmt && row.cardSalesAmt != 0) ? '--' : row.cardSalesAmt.toFixed(2);
}
}
],
tableList: []
......@@ -233,6 +264,10 @@ export default {
this.loading = false;
},
//编辑
copyData(row) {
this.$router.push('/ecm/copy/' + row.ecmPlanId);
},
//编辑
editData(row) {
this.$router.push('/ecm/edit/' + row.ecmPlanId);
},
......@@ -243,7 +278,7 @@ export default {
},
// 触达效果
toTouch(row) {
this.$router.push({ path: `/ecm/touch/${row.ecmPlanId}`, query: { name: row.ecmPlanName } });
this.$router.push({ path: `/ecm/touch/${row.ecmPlanId}`, query: { name: row.ecmPlanName, createTime: row.createTime } });
},
// 删除
async delData(row) {
......@@ -292,6 +327,7 @@ export default {
}
if (this.xsxsFlag) {
// this.marketingTypeOptions.find(el => el.value == 'qywx').visible = true;
this.marketingTypeOptions.find(el => el.value == 'qfxx').visible = true;
}
});
},
......@@ -307,6 +343,8 @@ export default {
this.offlineEcmPlan(row);
} else if (command == 3) {
this.toTouch(row);
} else if (command == 4) {
this.copyData(row);
}
}
},
......@@ -329,3 +367,13 @@ export default {
}
};
</script>
<style lang="scss" scoped>
.el-icon-refresh {
color: #909399;
cursor: pointer;
&:hover {
color: #606266;
}
}
</style>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" fill="#1890ff" xml:space="preserve">
<path d="M192.9,180.5H19.3c-10.5,0-18.6-8.7-18.6-18.6V38c0-10.5,8.7-18.6,18.6-18.6h160.5c10.5,0,18.6,8.7,18.6,18.6v60.1
c0,3.7-3.1,6.2-6.2,6.2c-3.1,0-6.2-3.1-6.2-6.2V38c0-3.1-2.5-6.2-6.2-6.2H19.3c-3.1,0-6.2,2.5-6.2,6.2v123.9c0,3.1,2.5,6.2,6.2,6.2
h173.5c3.7,0,6.2,3.1,6.2,6.2S196,180.5,192.9,180.5L192.9,180.5z"/>
<path d="M99.9,115.4c-1.2,0-3.1-0.6-3.7-1.2L24.9,55.3c-2.5-2.5-3.1-6.2-0.6-8.7c2.5-2.5,6.2-3.1,8.7-0.6l66.9,55.1L166.8,46
c2.5-2.5,6.8-1.9,8.7,0.6c2.5,2.5,1.9,6.8-0.6,8.7l-71.2,58.9C102.4,114.8,101.1,115.4,99.9,115.4z M191.6,129.1h-63.2
c-3.7,0-6.2-3.1-6.2-6.2c0-3.1,3.1-6.2,6.2-6.2h63.2c3.7,0,6.2,3.1,6.2,6.2C197.8,126,194.7,129.1,191.6,129.1z M191.6,154.5h-80.6
c-3.7,0-6.2-3.1-6.2-6.2s3.1-6.2,6.2-6.2H191c3.7,0,6.2,3.1,6.2,6.2S194.7,154.5,191.6,154.5L191.6,154.5z"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" fill="#c0c4cc" xml:space="preserve">
<path d="M192.9,180.5H19.3c-10.5,0-18.6-8.7-18.6-18.6V38c0-10.5,8.7-18.6,18.6-18.6h160.5c10.5,0,18.6,8.7,18.6,18.6v60.1
c0,3.7-3.1,6.2-6.2,6.2c-3.1,0-6.2-3.1-6.2-6.2V38c0-3.1-2.5-6.2-6.2-6.2H19.3c-3.1,0-6.2,2.5-6.2,6.2v123.9c0,3.1,2.5,6.2,6.2,6.2
h173.5c3.7,0,6.2,3.1,6.2,6.2S196,180.5,192.9,180.5L192.9,180.5z"/>
<path d="M99.9,115.4c-1.2,0-3.1-0.6-3.7-1.2L24.9,55.3c-2.5-2.5-3.1-6.2-0.6-8.7c2.5-2.5,6.2-3.1,8.7-0.6l66.9,55.1L166.8,46
c2.5-2.5,6.8-1.9,8.7,0.6c2.5,2.5,1.9,6.8-0.6,8.7l-71.2,58.9C102.4,114.8,101.1,115.4,99.9,115.4z M191.6,129.1h-63.2
c-3.7,0-6.2-3.1-6.2-6.2c0-3.1,3.1-6.2,6.2-6.2h63.2c3.7,0,6.2,3.1,6.2,6.2C197.8,126,194.7,129.1,191.6,129.1z M191.6,154.5h-80.6
c-3.7,0-6.2-3.1-6.2-6.2s3.1-6.2,6.2-6.2H191c3.7,0,6.2,3.1,6.2,6.2S194.7,154.5,191.6,154.5L191.6,154.5z"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" fill="#909399" xml:space="preserve">
<path d="M192.9,180.5H19.3c-10.5,0-18.6-8.7-18.6-18.6V38c0-10.5,8.7-18.6,18.6-18.6h160.5c10.5,0,18.6,8.7,18.6,18.6v60.1
c0,3.7-3.1,6.2-6.2,6.2c-3.1,0-6.2-3.1-6.2-6.2V38c0-3.1-2.5-6.2-6.2-6.2H19.3c-3.1,0-6.2,2.5-6.2,6.2v123.9c0,3.1,2.5,6.2,6.2,6.2
h173.5c3.7,0,6.2,3.1,6.2,6.2S196,180.5,192.9,180.5L192.9,180.5z"/>
<path d="M99.9,115.4c-1.2,0-3.1-0.6-3.7-1.2L24.9,55.3c-2.5-2.5-3.1-6.2-0.6-8.7c2.5-2.5,6.2-3.1,8.7-0.6l66.9,55.1L166.8,46
c2.5-2.5,6.8-1.9,8.7,0.6c2.5,2.5,1.9,6.8-0.6,8.7l-71.2,58.9C102.4,114.8,101.1,115.4,99.9,115.4z M191.6,129.1h-63.2
c-3.7,0-6.2-3.1-6.2-6.2c0-3.1,3.1-6.2,6.2-6.2h63.2c3.7,0,6.2,3.1,6.2,6.2C197.8,126,194.7,129.1,191.6,129.1z M191.6,154.5h-80.6
c-3.7,0-6.2-3.1-6.2-6.2s3.1-6.2,6.2-6.2H191c3.7,0,6.2,3.1,6.2,6.2S194.7,154.5,191.6,154.5L191.6,154.5z"/>
</svg>
......@@ -274,9 +274,16 @@
.dm-text__wrap{
width: 500px;
/* width: 500px;
margin-left: 10px;
line-height: 2;
line-height: 2; */
max-width: 870px;
margin:0 10px;
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
white-space: pre-wrap;
word-break: break-all;
}
.dm-image__item__wrap{
......@@ -405,13 +412,14 @@
/* teltask message */
.dm-message__item__wrap,.dm-teltask__item__wrap{
width: 760px;
width: 870px;
height: 94px;
margin:0 10px;
border: 1px solid rgba(228,231,237,1);
border-radius: 4px;
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
}
.dm-message__item,.dm-teltask__item{
padding: 15px;
......@@ -462,7 +470,7 @@
margin: 0 10px;
}
.dm-qywx__item__wrap {
width: 760px;
width: 870px;
padding: 11px 20px 18px;
border: 1px solid rgba(228,231,237,1);
border-radius:4px;
......@@ -471,4 +479,12 @@
vertical-align: middle;
margin: 0 10px;
color: #303133;
box-sizing: border-box;
}
.dm__is-delete {
display: inline-block;
border: 1px solid rgba(228,231,237,1);
border-radius:4px;
color: #303133;
padding: 20px;
}
<template>
<span class="dm-image__item__wrap">
<div class="dm__is-delete" v-if="item.status == 0">图片-已删除</div>
<span v-else class="dm-image__item__wrap">
<img :src="item.qcloudImageUrl || loadErrorImg" alt="" />
</span>
</template>
......
<template>
<span class="dm-message__item__wrap">
<div class="dm__is-delete" v-if="item.status == 0">短信模板-已删除</div>
<span v-else class="dm-message__item__wrap">
<div class="dm-message__item">
<p class="dm-message__item--title">{{ item.title || '该短信模板已删除' }}</p>
<p class="dm-message__item--content">{{ item.content || '--' }}</p>
......
<template>
<div class="dm-qywx__item__wrap">
<div class="fz16">{{ qfxx.qfxxEnterpriseName }}</div>
<div class="divider"></div>
<div>{{ item.title }}</div>
<div style="margin-top:12px;color:#606266;white-space:pre-wrap;word-break:break-word">{{ qfxx.remark }}</div>
<p style="margin-top:12px;color:#606266">
<span>群发内容:</span>
<span v-for="(el, index) in materials" :key="el">
<template v-if="index !== 0"> / </template>
<span v-html="el"></span>
</span>
</p>
</div>
</template>
<script>
export default {
name: 'item-qfxx',
props: {
item: {
type: Object,
default() {
return {};
}
}
},
computed: {
qfxx() {
return this.item.qfxx || {};
},
materials() {
let mapName = {
1: '文本',
2: '图片',
3: '网页',
4: '视频',
5: '文件',
6: '小程序'
};
let res = JSON.parse(this.item.qfxx.chatContent || '[]');
const items = res.filter(el => el.statusFlag == 0);
return [...new Set(res.map(el => el.type))].map(el => {
if (items.length > 0 && items.some(item => item.type == el)) {
return `${mapName[el]} (<span style="color:#F56C6C">已失效</span>)`;
} else {
return mapName[el];
}
});
}
}
};
</script>
<style lang="scss" scoped>
.divider {
margin-top: 12px;
margin-bottom: 18px;
height: 1px;
background-color: #dcdfe6;
}
/deep/ .el-checkbox {
margin: 0;
margin-right: 20px;
}
</style>
<template>
<div class="dm-imgText__item__wrap">
<div class="dm__is-delete" v-if="item.status == 0">图文-已删除</div>
<div v-else class="dm-imgText__item__wrap">
<div v-for="(v, i) in item.itemList" :key="i">
<div class="dm-imgText__item" v-if="i === 0" @mouseover="itemMouseover(v)" @mouseout="itemMouseout(v)">
<div class="dm-imgText__item__mask" v-if="v.maskShow" @click="preview(v)">预览文章</div>
......
<template>
<!-- <span class="dm-text__wrap ellipsis-l2 inline-block" v-html="item.content"></span> -->
<pre class="inline-block">
<!-- <pre class="inline-block">
<span v-html="item.content"></span>
</pre>
</pre> -->
<span class="dm-text__wrap" v-html="item.content"></span>
</template>
<script>
import options from '../assets/options.js';
......
<template>
<div class="dm-wxa__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div class="dm__is-delete" v-if="item.status == 0">小程序链接-已删除</div>
<div v-else class="dm-wxa__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div class="dm-wxa__item">
<div class="dm-wxa__avatar">
<img :src="item.brandLogo || loadErrorImg" alt="" />
......
<template>
<div>
<el-dialog :title="isEdit && !readOnly ? '编辑群发消息' : '群发消息'" :visible.sync="show" width="680px" @closed="close">
<el-form ref="form" :model="form" :rules="rules" label-width="130px">
<el-form-item label="选择企业" prop="qfxxEnterpriseId">
<el-select v-model="form.qfxxEnterpriseId" @change="onChangeEnterprise" :disabled="isEdit || readOnly">
<el-option v-for="el in entepriseList" :key="el.wxEnterpriseId" :value="el.wxEnterpriseId" :label="el.corpName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="任务标题" prop="title">
<template slot="label">
<span>任务标题</span>
<el-tooltip open-delay="200" placement="top">
<i style="cursor:pointer;color:#909399;font-size:14px;" class="iconfont icon-xinxixianshi"></i>
<div slot="content" style="width:376px;line-height: 22px;">计划创建保存后,若修改任务标题,该计划历史已下发任务的标题不变,仅影响后续新下发的任务</div>
</el-tooltip>
</template>
<el-input show-word-limit v-model.trim="form.title" :maxlength="20" :disabled="readOnly"></el-input>
</el-form-item>
<el-form-item label="任务描述" prop="remark">
<template slot="label">
<span>任务描述</span>
<el-tooltip open-delay="200" placement="top">
<i style="cursor:pointer;color:#909399;font-size:14px;" class="iconfont icon-xinxixianshi"></i>
<div slot="content" style="width:376px;line-height: 22px;">计划创建保存后,若修改任务描述内容,除了影响新下发的任务之外,该计划历史已下发任务的任务描述也会修改</div>
</el-tooltip>
</template>
<el-input class="task-desc" type="textarea" show-word-limit v-model="form.remark" :rows="4" :maxlength="200" resize="none" :disabled="readOnly"></el-input>
</el-form-item>
<el-form-item label="任务逾期判定" prop="expireDays">
<el-input-number v-model="form.expireDays" :min="0" controls-position="right" :disabled="readOnly"></el-input-number>
<span>天之后</span>
</el-form-item>
<el-form-item class="is-required" label="添加群发内容" prop="">
<template slot="label">
<span>添加群发内容</span>
<el-tooltip open-delay="200" placement="top">
<i style="cursor:pointer;color:#909399;font-size:14px;" class="iconfont icon-xinxixianshi"></i>
<div slot="content" style="width:376px;line-height: 22px;">计划创建保存后,若修改群发内容,该计划历史已下发任务的群发内容不变,仅影响后续新下发的任务</div>
</el-tooltip>
</template>
<div style="font-size:12px;color:#6B6D71;margin-bottom:10px">建议以文本+其他类型组合创建群发内容;文本内容最多只能添加 1 个</div>
<div style="margin-bottom:16px;display:flex;flex-wrap:wrap;margin-top:12px">
<material-item class="card-item" v-for="item in materials" :key="item.relation_id" :item="item" @delete="onDeleteMaterial" :read-only="readOnly"></material-item>
<div v-show="materials.length < 3 && !readOnly" class="chat-item card-item" @click="openMaterialDialog">
<i class="el-icon-plus"></i>
<span class="add-text">添加素材 (最多3个)</span>
</div>
</div>
</el-form-item>
</el-form>
<template slot="footer">
<el-button style="width:74px" @click="close">取消</el-button>
<el-button style="width:74px" type="primary" @click="addItem">确定</el-button>
</template>
</el-dialog>
<dm-material :visible.sync="materialVisible" :types="[1, 2, 3, 4, 6]" @select="onSelectMaterial" :wx-enterprise-id="this.form.qfxxEnterpriseId" projectName="marketing" :material-ids="materials.map(el => el.relation_id)" :disableTypes="disableTypes"></dm-material>
</div>
</template>
<script>
import { getEntepriseList } from '../assets/api';
import MaterialItem from './material-item';
export default {
name: 'lib-qfxx',
props: {
item: {
type: Object,
default() {
return {};
}
},
show: {
type: Boolean,
default: false
},
readOnly: {
type: Boolean,
default: false
}
},
components: {
MaterialItem
},
data() {
return {
form: {
qfxxEnterpriseId: '',
qfxxEnterpriseName: '',
remark: '',
title: '',
expireDays: 0,
chatContent: null
},
rules: {
qfxxEnterpriseId: { required: true, message: '请选择企业', trigger: 'change' },
title: { required: true, message: '请填写任务标题', trigger: 'blur' },
remark: { required: true, message: '请填写任务描述', trigger: 'blur' },
expireDays: { required: true, message: '请填写', trigger: 'blur' }
},
entepriseList: [],
isEdit: false,
ecmMarketingTypeRelationId: '',
materials: [],
materialVisible: false
};
},
created() {
this.getEntepriseList();
},
methods: {
openMaterialDialog() {
if (!this.form.qfxxEnterpriseId) {
return this.$message.warning('请选择企业');
}
this.materialVisible = true;
},
close() {
this.form = {
qfxxEnterpriseId: '',
qfxxEnterpriseName: '',
remark: '',
title: '',
expireDays: 0,
chatContent: null
};
this.ecmMarketingTypeRelationId = '';
this.materials = [];
this.$refs.form.resetFields();
this.$emit('update:show', false);
},
async addItem() {
const valid = await this.$refs.form.validate();
if (!valid) return;
let qfxx = { ...this.form };
if (this.materials.length == 0) {
this.$message.warning('请添加群发内容');
return;
} else if (this.materials.every(el => el.type != 1)) {
this.$message.warning('请添加一条文本内容');
return;
}
delete qfxx.title;
qfxx.chatContent = JSON.stringify(this.materials.map(el => ({ relation_id: el.relation_id })));
this.$emit('sendItem', { title: this.form.title, qfxx, comName: 'qfxx', ecmMarketingTypeRelationId: this.ecmMarketingTypeRelationId });
this.close();
},
getEntepriseList() {
getEntepriseList()
.then(res => {
if (res.errorCode === 0) {
this.entepriseList = res.result || [];
} else {
this.$message({ message: res.message, type: 'error' });
}
})
.catch(err => {
this.$message({ message: err.data.message, type: 'error' });
});
},
onChangeEnterprise(v) {
let item = this.entepriseList.find(el => el.wxEnterpriseId == v);
this.materials = [];
if (item) {
this.form.qfxxEnterpriseName = item.corpName;
} else {
this.form.qfxxEnterpriseName = '';
}
},
onDeleteMaterial(id) {
this.materials = this.materials.filter(el => el.relation_id != id);
},
onSelectMaterial(item) {
this.materials.push({
type: item.materialType,
relation_id: item.materialId,
title: item.materialTitle,
content: item.materialContent || item.link,
img: item.imgUrl
});
}
},
computed: {
disableTypes() {
if (this.materials.some(el => el.type == 1)) {
return [1];
} else {
return [];
}
}
},
watch: {
show: {
handler() {
if (Object.keys(this.item).length > 0) {
this.isEdit = true;
for (let key in this.form) {
if (this.item.qfxx[key] !== undefined) {
this.form[key] = this.item.qfxx[key];
}
}
if (this.item.qfxx && this.item.qfxx.chatContent) {
this.materials = JSON.parse(this.item.qfxx.chatContent || '[]');
}
this.form.title = this.item.title;
this.ecmMarketingTypeRelationId = this.item.ecmMarketingTypeRelationId;
} else {
this.isEdit = false;
}
},
immediate: true,
deep: true
}
}
};
</script>
<style lang="scss" scoped>
.chat-item {
width: 240px;
height: 100px;
border: 1px dashed #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
cursor: pointer;
.el-icon-plus {
color: #1890ff;
font-size: 24px;
}
.add-text {
margin-top: 12px;
font-size: 14px;
font-weight: 400;
color: #303133;
}
&:hover {
border-color: #1890ff;
}
}
.task-desc {
/deep/ .el-input__count {
background-color: transparent;
bottom: 0;
}
}
.card-item {
margin-top: 10px;
margin-right: 10px;
}
</style>
......@@ -146,7 +146,7 @@
</template>
</el-dialog>
<template-dialog ref="template-dialog" @sendItem="onInsertTemplate"></template-dialog>
<dm-material :visible.sync="materialVisible" @select="onSelectMaterial" :wx-enterprise-id="this.form.qywxEnterpriseId" projectName="marketing" :material-ids="materials.map(el => el.relation_id)"></dm-material>
<dm-material :visible.sync="materialVisible" :types="[1, 2, 3, 4, 5]" @select="onSelectMaterial" :wx-enterprise-id="this.form.qywxEnterpriseId" projectName="marketing" :material-ids="materials.map(el => el.relation_id)"></dm-material>
</div>
</template>
......@@ -454,7 +454,7 @@ export default {
.chat-item {
width: 240px;
height: 100px;
border: 1px dotted #dcdfe6;
border: 1px dashed #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
......
<template>
<el-dialog title="编辑话务" :visible.sync="show" width="600px" :before-close="close">
<el-form ref="form" :model="form" label-width="110px" :rules="rules" v-loading="loading">
<el-dialog title="编辑话务" :visible.sync="show" width="620px" :before-close="close">
<el-form ref="form" :model="form" label-width="130px" :rules="rules" v-loading="loading">
<el-form-item label="话务任务标题" class="pb10" prop="title">
<template slot="label">
<span>话务任务标题</span>
<el-tooltip open-delay="200" placement="top">
<i style="cursor:pointer;color:#909399;font-size:14px;" class="iconfont icon-xinxixianshi"></i>
<div slot="content" style="width:376px;line-height: 22px;">计划创建保存后,若修改任务标题,该计划历史已下发任务的标题不变,仅影响后续新下发的任务</div>
</el-tooltip>
</template>
<dm-input class="w400" v-model="form.title" placeholder="限制20个字符" :maxlength="20"></dm-input>
</el-form-item>
<el-form-item label="话务任务内容" class="pb10" prop="content">
<template slot="label">
<span>话务任务内容</span>
<el-tooltip open-delay="200" placement="top">
<i style="cursor:pointer;color:#909399;font-size:14px;" class="iconfont icon-xinxixianshi"></i>
<div slot="content" style="width:376px;line-height: 22px;">计划创建保存后,若修改任务内容,除了影响新下发的任务之外,该计划历史已下发任务的任务内容也会修改</div>
</el-tooltip>
</template>
<dm-input class="w400" :rows="4" type="textarea" v-model="form.content" placeholder="限制200个字符" :maxlength="200"></dm-input>
</el-form-item>
<el-form-item label="任务逾期判定" prop="lateDays"> <el-input-number controls-position="right" class="w400" v-model="form.lateDays"></el-input-number> 天之后 </el-form-item>
......
......@@ -26,6 +26,18 @@
<img style="width:36px;height:36px;margin-left:15px" :src="fileImg" />
<span class="file__title">{{ item.title }}</span>
</div>
<template v-else-if="item.type == 6">
<div class="wxa">
<img style="width:56px;height:56px" :src="item.img" />
<div style="margin-left:10px;flex:1;min-width:0">
<div style="display:flex;align-items:center">
<img style="width:14px;height:14px;margin-right:4px" src="../assets/img/wxa-circle.svg" />
<div class="wxa_title">{{ item.title }}</div>
</div>
<div class="wxa_content">{{ item.content }}</div>
</div>
</div>
</template>
</div>
</template>
......@@ -67,6 +79,7 @@ export default {
position: absolute;
top: 10px;
right: 10px;
font-size: 12px;
}
.text-center {
text-align: center;
......@@ -142,4 +155,22 @@ img {
height: 56px;
width: 56px;
}
.wxa {
margin-top: 21px;
display: flex;
align-items: center;
&_content {
margin-top: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&_title {
min-width: 0;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>
<template>
<div class="dm-wrap" v-loading="loading">
<el-row>
<el-input class="w300" v-model="listParams.search" placeholder="请输入操作对象/操作内容" clearable @change="onSearch">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<el-date-picker v-model="dateRange" value-format="yyyy-MM-dd" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="onSearch"></el-date-picker>
<span class="layout--tips" style="margin-top:0;margin-left:20px"><i class="el-icon-info"></i>只记录删除计划或编辑“进行中”计划的操作日志</span>
</el-row>
<el-table class="mt20" :data="tableList">
<el-table-column prop="operatorName" label="操作人">
<template slot-scope="{ row }">{{ row.operatorPhone }}-{{ row.operatorName }}{{ row.superAdmin ? '-超管' : '' }}</template>
</el-table-column>
<el-table-column prop="ecmPlanName" label="操作对象"></el-table-column>
<el-table-column label="操作时间">
<template slot-scope="{ row }">
<p class="cell-time">
{{ formatDateTimeByType(row.createTime, 'yyyy-MM-dd-HH-mm-ss', true).y }}<br />
<span>{{ formatDateTimeByType(row.createTime, 'yyyy-MM-dd-HH-mm-ss', true).h }}</span>
</p>
</template>
</el-table-column>
<el-table-column prop="context" label="操作内容" width="width"></el-table-column>
</el-table>
<dm-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[20, 40, 60, 80]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></dm-pagination>
</div>
</template>
<script>
import { getEcmLog } from '@/service/api/ecmApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
export default {
name: 'ecm-operator-Log',
data() {
return {
formatDateTimeByType,
loading: false,
tableList: [],
total: 0,
listParams: {
pageSize: 20,
currentPage: 1,
beginTime: '',
endTime: '',
search: ''
}
};
},
mounted() {
this.$store.commit('mutations_breadcrumb', [
{ name: '营销管理', path: '' },
{ name: '智能营销', path: '/ecm' },
{ name: '操作日志', path: '' }
]);
this.getList();
},
methods: {
handleSizeChange(val) {
this.listParams.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.getList();
},
async getList() {
this.loading = true;
const { errorCode, result, message } = await getEcmLog(this.listParams).finally(() => (this.loading = false));
if (errorCode == 0) {
this.tableList = result.result || [];
this.total = result.totalCount;
} else {
this.$message.warning(message);
}
},
onSearch() {
this.listParams.currentPage = 1;
this.getList();
}
},
computed: {
dateRange: {
set(val) {
if (val) {
this.listParams.beginTime = val[0];
this.listParams.endTime = val[1];
} else {
this.listParams.beginTime = '';
this.listParams.endTime = '';
}
},
get() {
return [this.listParams.beginTime, this.listParams.endTime];
}
}
}
};
</script>
<template>
<div class="dm-wrap card_profit">
<div class="title">
<h2>卡券收益</h2>
<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">
<el-table-column :prop="cardName" label="卡券名称" min-width="150">
<template slot-scope="scope">
<div class="name" v-if="scope.row.cardName">
<div class="top">
<span v-if="scope.row.cardName.length <= 15">{{ scope.row.cardName }}</span>
<el-tooltip open-delay="100" placement="top-start" :content="scope.row.cardName" v-else>
<span class="card_name">{{ scope.row.cardName }}</span>
</el-tooltip>
<span class="type">{{ scope.row.cardType == 0 ? '抵金券' : scope.row.cardType == 1 ? '折扣券' : scope.row.cardType == 2 ? '兑换券' : '--' }}</span>
</div>
<p>{{ scope.row.subTitle }}</p>
</div>
</template>
</el-table-column>
<el-table-column v-for="(v, i) in tableHeader" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter">
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{ scope.row[v.prop] }}</span>
</template>
</el-table-column>
</el-table>
</template>
<div class="empty" v-else>
<div class="content">
<span class="icon iconfont">&#xe76e;</span>
<span class="none-tip">本计划未使用卡券营销</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'card-profit',
props: {
list: {
type: Object,
default: () => {}
},
tableData: {
type: Array,
default: () => []
}
},
data() {
return {
tableHeader: [
{ label: '触达人数', prop: 'touchMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.touchMbrNum) },
{ label: '领取人数', prop: 'getMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.getMbrNum) },
{ label: '领取率', prop: 'drawRate', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterRate((row.getMbrNum / row.touchMbrNum) * 100) },
{ label: '使用人数', prop: 'useMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.useMbrNum) },
{ label: '使用率', prop: 'useRate', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterRate((row.useMbrNum / row.getMbrNum) * 100) },
{ label: '销售单数', prop: 'orderCnt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.orderCnt) },
{ label: '销售单金额', prop: 'salesAmt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => parseFloat(row.salesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }
]
};
},
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;
margin-bottom: 10px !important;
font-family: PingFangSC-Regular, PingFang SC;
.title {
padding-left: 4px;
margin-bottom: 20px;
display: flex;
align-items: center;
h2 {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 700;
color: #303133;
line-height: 22px;
}
span {
margin-left: 28px;
font-size: 14px;
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;
.top {
display: flex;
align-items: center;
.type {
display: inline-block;
width: 42px;
height: 16px;
line-height: 16px;
text-align: center;
background: #fff1f0;
border-radius: 2px;
border: 1px solid #ffa39e;
font-size: 12px;
color: #f5222d;
margin-left: 6px;
}
.card_name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 210px;
}
}
p {
font-size: 12px;
color: #909399;
line-height: 22px;
}
}
.empty {
display: flex;
align-items: center;
justify-content: center;
height: 200px;
color: #909399;
font-size: 16px;
.content {
display: flex;
align-items: center;
.icon {
font-size: 30px;
margin-right: 10px;
}
.none-tip {
color: #c0c4cc;
font-size: 14px;
}
}
}
}
</style>
<template>
<!--文字说明-->
<el-drawer title="触达效果说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h670" v-if="contentTitle == '触达效果'">
<el-drawer title="触达效果说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h790" v-if="contentTitle == '触达效果'">
<ul class="content">
<li>数据更新频率:1天1次</li>
<li>
......@@ -38,6 +38,16 @@
</p>
</div>
</li>
<li>
<span class="squre"></span>
<div class="text">
<p>5. 卡券收益</p>
<p>
① 计划中的卡券,计划中卡券触达的人群使用该卡券消费的收益,包含已过计划收益有效期的数据。<br />
② 一个计划中使用多张卡券的,计划粒度上卡券收益去重,各卡券数据相加可能不等于整体数据。
</p>
</div>
</li>
</ul>
</el-drawer>
<el-drawer title="【导购线索】说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h850" v-else-if="contentTitle == '导购线索'">
......@@ -89,7 +99,7 @@
</li>
</ul>
</el-drawer>
<el-drawer title="【后台线索】说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h670" v-else-if="contentTitle == '后台线索'">
<el-drawer title="【后台线索】说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h762" v-else-if="contentTitle == '后台线索'">
<ul class="content">
<li>数据更新频率:1天1次</li>
<!-- <li>导购线索:计划中触达到门店导购端的都归入导购线索,包括话务、企微任务。</li>
......@@ -128,20 +138,24 @@
<div class="text">
<p>4.区域详情</p>
<p>区域粒度的『触达人数』和『触达率』按服务门店统计,其余转化信息按消费门店统计。</p>
<p>无归属门店:触达的人群无服务门店,归入无归属;</p>
<p>计划整体:按计划整体去重,同一用户被触达多次,转化多次,只记1人;</p>
<p>区域合计:按所选范围下的门店汇总,同一用户在不同门店下消费,汇总不去重</p>
</div>
</li>
</ul>
</el-drawer>
<el-drawer title="数据说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h380" v-else-if="contentTitle == '智能营销'">
<el-drawer title="数据说明" :visible.sync="drawer" :direction="direction" custom-class="touch_drawer h440" v-else-if="contentTitle == '智能营销'">
<ul class="content">
<li>更新频率:营销人次实时统计,其余指标1天更新1次;</li>
<li>
①【营销人次】:本智能营销计划预计营销的人次;<br />
②【计划触达人数】:本计划计划触达的人数。一些预计营销的人中因为一些系统特殊情况导致触发失败的,不会计入在内;重复营销的,只记1人<br />
②【计划触达人数】:本计划计划触达的人数。一些预计营销的人中因为一些系统特殊情况导致触发失败的,不会计入在内;重复营销的,只记1人<br />
③【触达人数】:通过计划中各种营销方式触达到的会员人数;<br />
④【转化人数】:触达的人中,在触达收益有效期内前来消费的人数;<br />
⑤【线索转化收益】:转化人数带来的收益,只计销售单,不看退货单和换货单;金额是应付还是实付看ERP传入的值。<br />
⑥ 说明:未开启营销分析开关的,除营销人次外其他指标信息不统计;
⑤【线索转化收益】:转化人数带来的收益,只计销售单,不看退货单和换货单;金额是应付还是实付看ERP传入的值;<br />
⑥【卡券收益】:计划中的卡券,计划中卡券触达的人群使用该卡券消费的收益,包含已过计划收益有效期的数据;<br />
⑦ 说明:未开启营销分析开关的,除营销人次外其他指标信息不统计;
</li>
</ul>
</el-drawer>
......@@ -172,11 +186,14 @@ export default {
.h850 {
height: 850px !important;
}
.h670 {
height: 670px !important;
.h790 {
height: 790px !important;
}
.h762 {
height: 762px !important;
}
.h380 {
height: 380px !important;
.h440 {
height: 440px !important;
}
.touch_drawer {
width: 395px !important;
......
......@@ -97,17 +97,10 @@ export default {
height: 20px;
line-height: 20px;
&:nth-child(1) {
// margin: 0 0 23px 15px;
margin-bottom: 23px;
// margin-left: 15px;
}
&:nth-child(2) {
// margin: 0 0 33px -14px;
margin-bottom: 33px;
// margin-left: -14px;
}
&:nth-child(3) {
// margin: 0 0 0 -44px;
}
}
span {
......
......@@ -23,38 +23,39 @@
<div class="right" :class="[isCluePage ? 'cluePage' : '']" ref="right">
<div v-if="!isCluePage">
<p>计划人次</p>
<p>{{ item.planMbrTimes ? item.planMbrTimes.toLocaleString() : 0 }}</p>
<!-- <p>{{ item.planMbrTimes ? item.planMbrTimes.toLocaleString() : 0 }}</p> -->
<p>{{ formatterNum(item.planMbrTimes) }}</p>
</div>
<div>
<p>{{ isCluePage ? '计划触达人数' : '计划人数' }}</p>
<p>{{ item.planMbrNum ? item.planMbrNum.toLocaleString() : 0 }}</p>
<p>{{ formatterNum(item.planMbrNum) }}</p>
</div>
<div v-if="isCluePage && item.flag" class="taskRate">
<p>
任务完成率<span>(任务总数 {{ item.taskCnt ? item.taskCnt.toLocaleString() : 0 }})</span>
任务完成率<span>(任务总数 {{ formatterNum(item.taskCnt) }})</span>
</p>
<p>{{ item.taskRate ? item.taskRate.toFixed(2) + '%' : '0.00%' }}</p>
<p>{{ formatterRate(item.taskRate) }}</p>
</div>
<div v-if="isCluePage || item.isSales == 1" class="touchMbr">
<p>
触达人数<span>(触达率 {{ item.touchRate ? item.touchRate + '%' : '0.00%' }})</span>
触达人数<span>(触达率 {{ formatterRate(item.touchRate) }})</span>
</p>
<p>{{ item.touchMbrNum ? item.touchMbrNum.toLocaleString() : 0 }}</p>
<p>{{ formatterNum(item.touchMbrNum) }}</p>
</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 }">(转化率 {{ item.transformRate ? item.transformRate + '%' : '0.00%' }})</span>
转化人数<span :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">(转化率 {{ formatterRate(item.transformRate) }})</span>
</p>
<p>{{ item.convMbrNum ? item.convMbrNum.toLocaleString() : 0 }}</p>
<p>{{ formatterNum(item.convMbrNum) }}</p>
</div>
<div>
<p>转化订单数</p>
<p>{{ item.convOrderCnt ? item.convOrderCnt.toLocaleString() : 0 }}</p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
</div>
<div>
<p>转化收益</p>
<p>{{ convSalesAmt(item.convSalesAmt) }}</p>
<p>{{ parseInt(item.convSalesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }) }}</p>
</div>
</div>
</div>
......@@ -89,18 +90,11 @@ export default {
batchNum: Number | String
},
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.00';
}
};
formatterNum() {
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterRate() {
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
}
};
......
......@@ -13,7 +13,8 @@ import { ecmTouchEffectColumnDiagram, ecmGuideCluesColumnDiagram, ecmHeadCluesCo
export default {
name: 'touch-charts',
props: {
type: String // 0是触达效果 1导购线索 2后台线索
type: String, // 0是触达效果 1导购线索 2后台线索
createTime: Number
},
data() {
return {
......@@ -27,6 +28,7 @@ export default {
methods: {
draw() {
this.chartData = this.formatterData(this.chartData);
if (!this.chartData.length) return (this.isNone = true); // 营销开始时间为当天
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的情况
......@@ -139,11 +141,9 @@ export default {
let arr = [];
let date = new Date().getTime();
let oneDay = 86400000;
this.createTime = parseInt(this.createTime) || 0;
for (let i = 1; i <= 15; i++) {
let year = new Date(date - oneDay * i).getFullYear();
let month = String(new Date(date - oneDay * i).getMonth() + 1).padStart('2', '0');
let day = String(new Date(date - oneDay * i).getDate()).padStart('2', '0');
let item = year + '-' + month + '-' + day;
let item = date - oneDay * i;
let obj = [
{ date: item, value: 0, name: '触达人数' },
{ date: item, value: 0, name: '转化人数' },
......@@ -163,6 +163,16 @@ export default {
}
});
});
// 过滤出从营销计划开始之后的数据
dateArr = dateArr.filter(item => {
if (item.date >= this.createTime) {
let year = new Date(item.date).getFullYear();
let month = String(new Date(item.date).getMonth() + 1).padStart('2', '0');
let day = String(new Date(item.date).getDate()).padStart('2', '0');
item.date = year + '-' + month + '-' + day;
return item;
}
});
return dateArr;
}
}
......
<template>
<div class="touch" v-loading="loading">
<div class="dm-wrap">
<div class="dm-wrap effect">
<div class="title">
<h2>触达效果</h2>
</div>
<touch-charts :type="0" />
<touch-charts :type="0" :createTime="$route.query.createTime" />
<market-list v-if="marketListData.length" :isRepeat="isRepeat" :data="marketListData" :isReference="isReference" :batchNum="batchNum" :batchTimes="batchTime" />
</div>
<div class="dm-wrap table">
<card-profit :list="cardLead" :tableData="cardLeadTable" />
<div class="dm-wrap effect table">
<div class="title">
<span>线索转化对比</span>
</div>
......@@ -68,8 +69,8 @@
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 { ecmTouchEffectTable, ecmTouchEffectFunnelChart, ecmPlanTouchConfig } from '@/service/api/ecmApi.js';
// ecmPlanTouchConfig
import cardProfit from '@/views/ecm/touch-components/card-profit.vue';
import { ecmTouchEffectTable, ecmTouchEffectFunnelChart, ecmPlanTouchConfig, getCardLeads, getCardLeadsList } from '@/service/api/ecmApi.js';
export default {
name: 'ecm',
data() {
......@@ -77,34 +78,40 @@ export default {
marketListData: [],
funnelData: [],
clueRate: [],
loading: true,
loading: false,
ecmPlanId: '',
isRepeat: false, // 是否重复营销
isReference: false,
batchNum: 0, // 批次数量
batchTime: '' // 批次时间
batchTime: '', // 批次时间
cardLead: {},
cardLeadTable: []
};
},
components: {
marketList,
touchCharts,
funnel
funnel,
cardProfit
},
mounted() {
this.ecmPlanId = this.$route.params.id;
let planName = this.$route.query.name;
this.$store.commit('mutations_breadcrumb', [{ name: '营销管理', path: '' }, { name: '智能营销', path: '/ecm' }, { name: `${planName} - 触达效果` }]); // eslint-disable-line
this.getMarketList();
this.getFunnelData();
this.getTouchConfig();
this.getCardLeadsSum();
this.getCardLeadTable();
this.getFunnelData();
},
methods: {
toClue(type) {
let planName = this.$route.query.name;
let id = this.$route.params.id;
let createTime = this.$route.query.createTime;
this.ecmPlanId = id;
// type 1为导购线索 2为后台线索
this.$router.push({ path: '/ecm/clue', query: { planName, id, type } });
this.$router.push({ path: '/ecm/clue', query: { planName, id, type, createTime } });
},
getMarketList() {
ecmTouchEffectTable({ ecmPlanId: this.ecmPlanId }).then(res => {
......@@ -114,10 +121,10 @@ export default {
item.transformRate = item.touchMbrNum * 1 && item.planMbrNum * 1 ? ((item.convMbrNum / item.planMbrNum) * 100).toFixed(2) : 0;
return item;
});
console.log(this.marketListData);
});
},
getFunnelData() {
this.loading = true;
ecmTouchEffectFunnelChart({ ecmPlanId: this.ecmPlanId }).then(res => {
let checkData = res.result.map(item => {
if (!item || !Object.keys(item).length) {
......@@ -132,17 +139,7 @@ export default {
return item;
});
this.funnelData = this.formatFunnelData(checkData);
this.clueRate = checkData.map(item => {
if (item.线索转化收益 && parseFloat(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.00';
}
});
this.clueRate = checkData.map(item => parseInt(item.convSalesAmt || 0).toLocaleString('zh', { minimumFractionDigits: 2 }));
this.loading = false;
});
},
......@@ -155,14 +152,14 @@ export default {
action: i,
value: parseInt(item[i]) ? item[i] * 1 : 0, // 防止字符串的null
rateAction: '触达率',
rate: (item.触达人数 / item.计划触达人数) * 100 ? ((item.触达人数 / item.计划触达人数) * 100).toFixed(2) + '%' : '0.00%'
rate: this.formatterRate((item.触达人数 / item.计划触达人数) * 100)
});
} else if (i != '收益' && i == '转化人数' && item[i] && item.计划触达人数) {
obj.unshift({
action: i,
value: parseInt(item[i]) ? item[i] * 1 : 0,
rateAction: '转化率',
rate: (item.转化人数 / item.触达人数) * 100 ? ((item.转化人数 / item.触达人数) * 100).toFixed(2) + '%' : '0.00%'
rate: this.formatterRate((item.转化人数 / item.触达人数) * 100)
});
} else {
if (i != '线索转化收益') {
......@@ -180,11 +177,7 @@ export default {
getTouchConfig() {
ecmPlanTouchConfig({ ecmPlanId: this.ecmPlanId }).then(res => {
this.batchNum = res.result.times ? res.result.times : 0;
if (res.result.lastTime) {
this.batchTime = new Date(parseInt(res.result.lastTime)).toLocaleString('zh', { hour12: false }).replace(/[\/]/g, '-');
} else {
this.batchTime = '--';
}
this.batchTime = res.result.lastTime ? new Date(parseInt(res.result.lastTime)).toLocaleString('zh', { hour12: false }).replace(/[\/]/g, '-') : '--';
let analyseJson = JSON.parse(res.result.analyseJson);
if (analyseJson && analyseJson.crowd_flag == 1) {
// 是否开启了参照组开关
......@@ -196,15 +189,33 @@ export default {
if (item.isSales == 0) this.marketListData.splice(index, 1);
});
}
if (res.result.effectType == 1) this.isRepeat = true;
else this.isRepeat = false;
this.isRepeat = res.result.effectType == 1 ? true : false;
});
},
// 获取卡券收益数据
getCardLeadsSum() {
getCardLeads({ ecmPlanId: this.$route.params.id }).then(res => {
this.cardLead = res.result || {};
});
},
getCardLeadTable() {
getCardLeadsList({ ecmPlanId: this.$route.params.id }).then(res => {
this.cardLeadTable = res.result || [];
});
}
},
computed: {
formatterNum() {
return val => (!val ? '0' : parseInt(val).toLocaleString());
},
formatterRate() {
return val => (!val ? '0.00%' : parseFloat(val).toFixed(2) + '%');
}
}
};
</script>
<style lang="scss" scoped>
.dm-wrap {
.effect {
padding: 16px 20px 27px !important;
margin-bottom: 10px !important;
.title {
......
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