Commit 7bb080ab by liuchenxi

update: 营销记录

parent 7c9410d7
......@@ -3,9 +3,10 @@
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="./favicon.ico" />
<script src="//web-1251519181.file.myqcloud.com/lib/vue/2.5.2/vue.min.js"></script>
<style href="//at.alicdn.com/t/font_2996579_ubjq74uy5wj.css"></style>
<!-- <script src="//web-1251519181.file.myqcloud.com/lib/vue/2.5.2/vue.min.js"></script>
<script src="//web-1251519181.file.myqcloud.com/lib/vue-router/3.0.2/vue-router.min.js"></script>
<script src="//web-1251519181.file.myqcloud.com/lib/vuex/3.1.0/vuex.min.js"></script>
<script src="//web-1251519181.file.myqcloud.com/lib/vuex/3.1.0/vuex.min.js"></script> -->
<script src="//web-1251519181.file.myqcloud.com/lib/lodash.min.js"></script>
<title>memberproject</title>
<!-- GrowingIO Analytics code version 2.1 -->
......
......@@ -381,8 +381,8 @@
border: 1px solid #e4e7ed;
box-sizing: border-box;
.el-icon-arrow-right {
align-self: flex-end;
margin-bottom: 9px;
align-self: center;
}
}
}
......
......@@ -320,6 +320,9 @@
<div class="middle">
<div class="label">
{{ item.label }}
<el-tooltip v-if="item.key == 'numOfMarket'" content="只统计近一年的数据" placement="top">
<i class="iconfont icon-QuestionCircleOutlined" style="color: #909399" />
</el-tooltip>
</div>
<div class="num">
{{ memberInfo[item.key]
......@@ -328,7 +331,7 @@
</div>
</div>
</div>
<div class="el-icon-arrow-right" v-if="item.key==='numOfTel'" />
<div class="el-icon-arrow-right" />
</li>
</ul>
</div>
......
......@@ -169,7 +169,7 @@ export default {
icon: 'icon-yingxiaojilu',
iconTheme: 'market',
key: 'numOfMarket',
path: '',
path: '/marketing-record',
},
{
label: '通话记录',
......
import axios from 'axios'
import qs from 'qs'
import { checkFalse } from '../../../static/js/checkStatus';
export function fetch(url, options) {
return new Promise((resolve, reject) => {
axios.post(url, options).then(res => {
resolve(res);
if (res.data.errorCode == 0) {
resolve(res);
} else {
checkFalse(res.data.message);
return reject();
}
}).catch(err => {
reject(err);
checkFalse(err);
return reject(err);
});
})
}
export function fetchqs(url,options) {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(options)).then(res => {
resolve(res);
if (res.data.errorCode == 0) {
resolve(res);
} else {
checkFalse(res.data.message);
return reject();
}
}).catch(err => {
reject(err);
checkFalse(err);
return reject(err);
});
})
}
......@@ -23,9 +36,15 @@ export function fetchqs(url,options) {
export function fetchGet(url, options) {
return new Promise((resolve, reject) => {
axios.get(url, { params: options }).then(res => {
resolve(res);
if (res.data.errorCode == 0) {
resolve(res);
} else {
checkFalse(res.data.message);
return reject();
}
}).catch(err => {
reject(err);
checkFalse(err);
return reject(err);
})
})
}
\ No newline at end of file
}
......@@ -89,7 +89,11 @@ const urlConfig = {
addToWhiteList: '/api-member/update-member-white-list', // 加入白名单
memberTagGroupDetail: '/api-member/member-tag-group-detail', //会员分组详情
memberStoredRecordList: '/api-member/member-stored-record-list', //储值列表(储值明细列表)
memberStoredTypeList: '/api-member/member-stored-type-list'// 储值列表-储值类型
memberStoredTypeList: '/api-member/member-stored-type-list',// 储值列表-储值类型
ecmLogPage: '/api-marketing/member/ecm-log-page',
cardLogPage: '/api-marketing/member/card-log-page',
messageLogPage: '/api-marketing/member/sms-log-page',
wechatLogPage: '/api-marketing/member/wechat-log-page'
}
const defaultUrl = Object.assign({}, urlConfig);
......
.w160 {
width: 160px;
}
.w256 {
width: 256px;
}
.w260 {
width: 260px;
}
.mb20 {
margin-bottom: 20px;
}
.mt5 {
margin-top: 5px;
}
.ml10 {
margin-left: 10px;
}
.mb25 {
margin-bottom: 25px;
}
.search {
font-size: 0;
}
<template>
<div class="card mt5">
<div class="search">
投放时间:<el-date-picker :clearable="false" class="w256" value-format="yyyy-MM-dd" :pickerOptions="pickerOptions" v-model="launchTime" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
领取时间:<el-date-picker class="ml10 w256" value-format="yyyy-MM-dd" :pickerOptions="pickerOptions" v-model="collectionTime" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
<el-input
v-model="search.search"
placeholder="请输入卡券券码"
class="w260 ml10"
prefix-icon="el-icon-search"
@change="change"
clearable
/>
<el-select class="ml10 w160" v-model="search.cardType" placeholder="所有卡券类型" clearable @change="change">
<el-option v-for="item in cardTypeList" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
<el-select class="ml10 w160" v-model="search.receiveCode" placeholder="所有投放渠道" clearable @change="change">
<el-option v-for="item in tableData.dictList" :key="item.dictCode" :value="item.dictCode" :label="item.dictName" />
</el-select>
</div>
<div class="table">
<el-table
v-loading="load"
:data="tableData.data"
tooltip-effect="dark"
style="width: 100%"
>
<el-table-column
v-for="(v, i) in tableHeader"
:key="i"
:prop="v.prop"
:min-width="v.minWidth"
:label="v.label"
:formatter="v.formatter"
:fixed="v.fixed"
show-overflow-tooltip
>
<template slot-scope="{ row }">
<span v-if="v.formatter" v-html="v.formatter(row)"></span>
<span v-else>{{ row[v.prop] || "--" }}</span>
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="tableData.total > 0">
<dm-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="tableData.currentPage"
:page-sizes="tableData.pageSizeList"
:page-size="tableData.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
>
</dm-pagination>
</div>
</div>
</div>
</template>
<script>
import dickerPickMixin from './datePickRule';
export default {
name: 'card',
props: {
tableData: Object
},
mixins: [ dickerPickMixin ],
data() {
return {
launchTime: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), new Date()],
collectionTime: [],
search: {
putBeginTime: '', // 投放开始时间
endputEndTimeTime: '', // 投放结束时间
receiveBeginTime: '', // 领取开始时间
receiveEndTime: '', // 结束
search: '', // 卡券代码
receiveCode: null, // 投放渠道
},
cardTypeList: [
{ label: '抵金券', value: 1 },
{ label: '折扣券', value: 2 },
{ label: '兑换券', value: 3 }
]
}
},
methods: {
change() {
const { currentPage, pageSize } = this.tableData;
const { launchTime, collectionTime } = this;
this.search.putBeginTime = launchTime ? ( typeof launchTime[0] == 'string ' ? launchTime[0] : this.dateformat(launchTime[0], 'yyyy-MM-dd')) : null;
this.search.endputEndTimeTime = launchTime ? ( typeof launchTime[0] == 'string ' ? launchTime[1] : this.dateformat(launchTime[1], 'yyyy-MM-dd')) : null;
this.search.receiveBeginTime = collectionTime ? collectionTime[0] : null;
this.search.receiveEndTime = collectionTime ? collectionTime[1] : null;
const commonObj = { currentPage, pageSize };
this.$emit("changeSearch", Object.assign({}, this.search, commonObj));
},
}
};
</script>
<style scoped>
@import './all.scss';
</style>
import { dateformat } from '@/utils/formatTime';
export default {
data() {
return {
// 四个页面选择器的禁用规则
pickerOptions: {
disabledDate: val => {
// 只能筛选一年之内得数据,并且最大跨度为六个月
const yearBefore = Date.now() - 12 * 30 * 24 * 60 * 60 * 1000;
const threeMonths = 3 * 30 * 24 * 60 * 60 * 1000;
if (this.minDate) {
const curTime = this.minDate.getTime();
const min = curTime - threeMonths > yearBefore ? curTime - threeMonths : yearBefore;
return val.getTime() > curTime + threeMonths || val.getTime() < min || val.getTime() > this.maxDate;
}
return val.getTime() <= yearBefore || val.getTime() > this.maxDate.getTime();
},
onPick: ({ maxDate, minDate }) => {
this.minDate = minDate;
}
},
minDate: null,
maxDate: new Date(),
dateformat: dateformat
}
}
}
<template>
<div class="ecm mt5">
<div class="search">
<el-input
v-model="search.search"
placeholder="请输入计划名称"
class="w260"
prefix-icon="el-icon-search"
clearable
@change="change"
/>
<el-date-picker class="ml10 w256" value-format="yyyy-MM-dd" :pickerOptions="pickerOptions" v-model="time" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
<el-select class="ml10 w160" v-model="search.marketingType" placeholder="所有营销方式" clearable @change="change">
<el-option v-for="item in ecmTypeList" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</div>
<div class="table">
<el-table
v-loading="load"
:data="tableData.data"
tooltip-effect="dark"
style="width: 100%"
>
<el-table-column
v-for="(v, i) in tableData.tableHeader"
:key="i"
:prop="v.prop"
:min-width="v.minWidth"
:label="v.label"
:formatter="v.formatter"
:fixed="v.fixed"
show-overflow-tooltip
>
<template slot-scope="{ row }">
<span v-if="v.formatter" v-html="v.formatter(row)"></span>
<span v-else>{{ row[v.prop] || "--" }}</span>
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="tableData.total > 0">
<dm-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="tableData.currentPage"
:page-sizes="tableData.pageSizeList"
:page-size="tableData.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
>
</dm-pagination>
</div>
</div>
</div>
</template>
<script>
import dickerPickMixin from './datePickRule';
export default {
name: "ecm",
props: {
tableData: Object
},
mixins: [ dickerPickMixin ],
data() {
return {
time: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), new Date()],
search: {
search: '',
beginTime: '',
endTime: '',
marketingType: null
},
ecmTypeList: [
{ label: '积分', value: 'integral' },
{ label: '短信', value: 'message' },
{ label: '话务', value: 'teltask' },
{ label: '群发任务', value: 'qfxx' },
{ label: '图文消息', value: 'teletext' },
{ label: '图片', value: 'image' },
{ label: '文本消息', value: 'text' },
{ label: '小程序', value: 'wxa' },
{ label: '卡券', value: 'card' }
]
};
},
methods: {
change() {
const { currentPage, pageSize } = this.tableData;
this.search.beginTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[0] : this.dateformat(this.time[0], 'yyyy-MM-dd')) : null;
this.search.endTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[1] : this.dateformat(this.time[1], 'yyyy-MM-dd')) : null;
const commonObj = { currentPage, pageSize };
this.$emit("changeSearch", Object.assign({}, this.search, commonObj));
}
}
};
</script>
<style scoped>
@import "./all.scss";
</style>
<template>
<div class="message mt5">
<div class="search">
<el-date-picker value-format="yyyy-MM-dd" class="w256" :pickerOptions="pickerOptions" v-model="time" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
<el-select class="ml10 w160" v-model="search.sysType" placeholder="所有模板类型" clearable @change="change">
<el-option v-for="item in sysTypeList" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</div>
<div class="table">
<el-table
v-loading="load"
:data="tableData.data"
tooltip-effect="dark"
style="width: 100%"
>
<el-table-column
v-for="(v, i) in tableHeader"
:key="i"
:prop="v.prop"
:min-width="v.minWidth"
:label="v.label"
:formatter="v.formatter"
:fixed="v.fixed"
show-overflow-tooltip
>
<template slot-scope="{ row }">
<span v-if="v.formatter" v-html="v.formatter(row)"></span>
<span v-else>{{ row[v.prop] || "--" }}</span>
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="tableData.total > 0">
<dm-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="tableData.currentPage"
:page-sizes="tableData.pageSizeList"
:page-size="tableData.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
>
</dm-pagination>
</div>
</div>
</div>
</template>
<script>
import dickerPickMixin from './datePickRule';
export default {
name: 'message',
props: {
tableData: Object
},
mixins: [ dickerPickMixin ],
data() {
return {
time: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), new Date()],
search: {
beginTime: '',
endTime: '',
sysType: null
},
sysTypeList: [
{ label: '普通短信', value: 0 },
{ label: '营销短信', value: 1 }
]
}
},
methods: {
change() {
const { currentPage, pageSize } = this.tableData;
this.search.beginTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[0] : this.dateformat(this.time[0], 'yyyy-MM-dd')) : null;
this.search.endTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[1] : this.dateformat(this.time[1], 'yyyy-MM-dd')) : null;
const commonObj = { currentPage, pageSize };
this.$emit("changeSearch", Object.assign({}, this.search, commonObj));
}
}
};
</script>
<style scoped>
@import './all.scss';
</style>
<template>
<div class="wechat mt5">
<div class="search">
<el-date-picker value-format="yyyy-MM-dd" class="w256" :pickerOptions="pickerOptions" v-model="time" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
<el-select class="ml10 w160" v-model="search.contentType" placeholder="所有素材类型" clearable @change="change">
<el-option v-for="item in contentTypeList" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</div>
<div class="table">
<el-table
v-loading="load"
:data="tableData.data"
tooltip-effect="dark"
style="width: 100%"
>
<el-table-column
v-for="(v, i) in tableHeader"
:key="i"
:prop="v.prop"
:min-width="v.minWidth"
:label="v.label"
:formatter="v.formatter"
:fixed="v.fixed"
show-overflow-tooltip
>
<template slot-scope="{ row }">
<span v-if="v.formatter" v-html="v.formatter(row)"></span>
<span v-else>{{ row[v.prop] || "--" }}</span>
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="tableData.total > 0">
<dm-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="tableData.currentPage"
:page-sizes="tableData.pageSizeList"
:page-size="tableData.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
>
</dm-pagination>
</div>
</div>
</div>
</template>
<script>
import dickerPickMixin from './datePickRule';
export default {
name: 'wechat',
props: {
tableData: Object
},
mixins: [ dickerPickMixin ],
data() {
return {
time: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), new Date()],
search: {
beginTime: '',
endTime: '',
contentType: null
},
contentTypeList: [
{ label: '图文消息', value: 0 },
{ label: '文本消息', value: 1 },
{ label: '图片', value: 2 }
]
}
},
methods: {
change() {
const { currentPage, pageSize } = this.tableData;
this.search.beginTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[0] : this.dateformat(this.time[0], 'yyyy-MM-dd')) : null;
this.search.endTime = this.time ? ( typeof this.time[0] == 'string ' ? this.time[1] : this.dateformat(this.time[1], 'yyyy-MM-dd')) : null;
const commonObj = { currentPage, pageSize };
this.$emit("changeSearch", Object.assign({}, this.search, commonObj));
}
}
};
</script>
<style scoped>
@import './all.scss';
</style>
<template>
<div class="marketing-record">
<div class="min100">
<nav-path :navpath="navpath"></nav-path>
<div class="marketing-container">
<div class="tabs" v-loading="load">
<el-tabs v-model="tabsIndex" @tab-click="handleClick">
<el-tab-pane label="智能营销" name="1">
<ecm-record ref="ecmRecord" :tableData="ecmTableData" @changeSearch="onChange" />
</el-tab-pane>
<el-tab-pane label="卡券营销" name="2">
<card-record :tableData="cardTableData" @changeSearch="onChange" />
</el-tab-pane>
<el-tab-pane label="短信营销" name="3">
<message-record :tableData="messageTableData" @changeSearch="onChange" />
</el-tab-pane>
<el-tab-pane label="微信营销" name="4">
<wechat-record :tableData="wechatTableData" @changeSearch="onChange" />
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</div>
</template>
<script>
import NavPath from "@/common/navbar/navbar.vue";
import ecmRecord from "./components/ecm.vue";
import cardRecord from "./components/card.vue";
import wechatRecord from "./components/wechat.vue";
import messageRecord from "./components/message.vue";
import { doFetch, doFetchqs } from "@/components/axios/api";
import url from "@/components/axios/url";
const { ecmLogPage, cardLogPage, messageLogPage, wechatLogPage, cardChannelType } = url;
export default {
name: "marketing-record",
components: {
NavPath,
ecmRecord,
cardRecord,
wechatRecord,
messageRecord
},
data() {
return {
navpath: [
{ name: "首页", path: "" },
{ name: "客户列表", path: "" },
{ name: "客户详情", path: '/customerDetail?memberId=' + this.$route.query.memberId || '' },
{ name: "营销记录", path: "" }
],
tabsIndex: "1",
search: {
msg: ""
},
ecmTableData: {
data: [],
currentPage: 1,
pageSizeList: [20, 40, 60, 80],
pageSize: 20,
total: 0,
tableHeader: []
},
wechatTableData: {
data: [],
currentPage: 1,
pageSizeList: [20, 40, 60, 80],
pageSize: 20,
total: 0,
tableHeader: []
},
messageTableData: {
data: [],
currentPage: 1,
pageSizeList: [20, 40, 60, 80],
pageSize: 20,
total: 0,
tableHeader: []
},
cardTableData: {
data: [],
currentPage: 1,
pageSizeList: [20, 40, 60, 80],
pageSize: 20,
total: 0,
tableHeader: [],
dictList: []
},
load: false
};
},
created() {
this.getTableHeader();
this.getCardChannelType();
// 触发第一个ecm子组件的change时候。触发自身的onChange
this.$nextTick(() => this.$refs.ecmRecord.change());
},
methods: {
async getTableData(tab, params) {
this.loading = true;
let res, name;
switch(tab) {
case "1":
res = await doFetchqs(ecmLogPage, params);
name = 'ecmTableData';
break;
case "2":
res = await doFetchqs(cardLogPage, params);
name = 'cardTableData';
break;
case "3":
res = await doFetchqs(messageLogPage, params);
name = 'messageTableData';
break;
case "4":
res = await doFetchqs(wechatLogPage, params);
name = 'wechatTableData';
break;
}
this.extendTable(this[name], res.data.result); // 赋值对应表格数据
this.loading = false;
},
getTableHeader() {
this.ecmTableData.TableHeader = [
{
label: "createDate",
prop: "提交时间",
minWidth: 120,
formatter(row) {
return `${this.dateformat(row.createDate, 'yyyy-MM-dd hh-mm-ss')}`;
}
},
{ label: "计划名称", prop: "ecmName", minWidth: 120 },
{ label: "营销方式", prop: "marketingType", minWidth: 120 },
{ label: "营销内容", prop: "", minWidth: 120 }
];
this.cardTableData.TableHeader = [
{
label: "投放时间",
prop: "createDate",
minWidth: 120,
formatter(row) {
return `${this.dateformat(row.createDate, 'yyyy-MM-dd hh-mm-ss')}`;
}
},
{
label: "领取时间",
prop: "receiveTime",
minWidth: 120,
formatter(row) {
return `${this.dateformat(row.receiveTime, 'yyyy-MM-dd hh-mm-ss')}`;
}
},
{ label: "卡券名称", prop: "cardName", minWidth: 120, },
{ label: "卡券代码", prop: "cardCode", minWidth: 120, },
{ label: "卡券类型", prop: "cardType", minWidth: 120,},
{ label: "投放途径", prop: "receiveCode", minWidth: 120, },
{ label: "来源明细", prop: "", minWidth: 120, }
];
this.messageTableData.TableHeader = [
{
label: "发送时间",
prop: "createDate",
minWidth: 120,
formatter(row) {
return `${this.dateformat(row.createDate, 'yyyy-MM-dd hh-mm-ss')}`;
}
},
{ label: "模板类型", prop: "smsType", minWidth: 120, },
{ label: "模板名称", prop: "smsTitle", minWidth: 120, },
{ label: "模板内容", prop: "smsContent", minWidth: 120, }
];
this.wechatTableData.TableHeader = [
{
label: "提交时间",
prop: "createDate",
minWidth: 120,
formatter(row) {
return `${this.dateformat(row.createDate, 'yyyy-MM-dd hh-mm-ss')}`;
}
},
{ label: "素材类型", prop: "contentType",minWidth: 120 },
{ label: "素材标题", prop: "title",minWidth: 120 }
];
},
// 设置表格数据
extendTable(tableObj, remoteData) {
tableObj['total'] = remoteData['totalCount'];
tableObj['currentPage'] = remoteData['currentPage'];
tableObj['data'] = remoteData['result'];
},
handleClick(tab) {
// 获取切换的组件实例重置数据, 注意不要改变el-tab-pane里的组件层级关系
const vm = tab.$children[0];
Object.assign(vm.$data, vm.$options.data());
vm.change();
},
// 获取卡券营销所有投放渠道
async getCardChannelType() {
const res = await doFetch(cardChannelType, { businessType: "CARD_RECEIVE_TYPE" })
this.cardTableData.dictList = res.data.result.dictList;
},
// 子组件任何筛选项发生改变, 包括tab切换、父组件初始化都会触发
onChange(params) {
console.log(params);
this.getTableData(this.tabsIndex, params);
}
}
};
</script>
<style lang="scss" scoped>
.marketing-container {
margin: 20px;
padding: 20px;
background-color: #fff;
}
.table {
.pager {
text-align: right;
padding: 20px 0;
}
}
.marketing-record {
height: 100%;
}
.w160 {
width: 160px;
}
.w260 {
width: 260px;
}
</style>
......@@ -327,6 +327,13 @@ export const constantRouterMap = [
title: '黑名单列表',
},
},
{
path: '/marketing-record',
component: _import('marketingRecord', 'index'),
meta: {
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