Commit 0594de51 by crushh

Merge branch 'feature/ai营销二期' into master

parents e66d473a f464a6da
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e){var c=window.webpackJsonp;window.webpackJsonp=function(a,t,o){for(var f,d,i,u=0,s=[];u<a.length;u++)d=a[u],n[d]&&s.push(n[d][0]),n[d]=0;for(f in t)Object.prototype.hasOwnProperty.call(t,f)&&(e[f]=t[f]);for(c&&c(a,t,o);s.length;)s.shift()();if(o)for(u=0;u<o.length;u++)i=r(r.s=o[u]);return i};var a={},n={24:0};function r(c){if(a[c])return a[c].exports;var n=a[c]={i:c,l:!1,exports:{}};return e[c].call(n.exports,n,n.exports,r),n.l=!0,n.exports}r.e=function(e){var c=n[e];if(0===c)return new Promise(function(e){e()});if(c)return c[2];var a=new Promise(function(a,r){c=n[e]=[a,r]});c[2]=a;var t=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.timeout=12e4,r.nc&&o.setAttribute("nonce",r.nc),o.src=r.p+"static/js/"+({0:"card",1:"game",2:"wechat",3:"ecm",4:"message",5:"ewash",6:"scan",7:"recharge",8:"activity",9:"signIn",10:"evaluation",11:"cdKey",12:"popup",13:"msg",15:"calllog"}[e]||e)+"."+{0:"d845bd264200e2fe023e",1:"8360686d83100ba44c76",2:"5a7e6c9a558f266c7762",3:"61e1d1a00a605488f348",4:"0ed5feeccec606921a9e",5:"dd66a6012402ab8d374f",6:"118cffd4fc4aec6165ca",7:"1870a11c1c7a62882285",8:"388da8ce361aad466b33",9:"fe11d8b6fea8a8e19249",10:"6750cbc0a759ff4c6458",11:"204e0fccedfb8f4e13ed",12:"561607517a3cf8bfd1d1",13:"fcdcee9f6a8e702a7712",14:"e974b9f88ac8f6dcb990",15:"cf0b09aa556f363809e3",16:"fd4b67a6e4b93d8b97e0",17:"c6caec60e9035b136c02",18:"cff1460eee064990b166",19:"59b5d962bdb9dfd78bda",20:"66d6821411e0e42afe9e",21:"3b70c426e7cb5384d238"}[e]+".js";var f=setTimeout(d,12e4);function d(){o.onerror=o.onload=null,clearTimeout(f);var c=n[e];0!==c&&(c&&c[1](new Error("Loading chunk "+e+" failed.")),n[e]=void 0)}return o.onerror=o.onload=d,t.appendChild(o),a},r.m=e,r.c=a,r.d=function(e,c,a){r.o(e,c)||Object.defineProperty(e,c,{configurable:!1,enumerable:!0,get:a})},r.n=function(e){var c=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(c,"a",c),c},r.o=function(e,c){return Object.prototype.hasOwnProperty.call(e,c)},r.p="/marketing/",r.oe=function(e){throw console.error(e),e}}([]);
\ No newline at end of file
......@@ -256,6 +256,9 @@ a:hover {
.w250{
width: 250px!important;
}
.w260{
width: 260px!important;
}
.w280{
width: 280px!important;
}
......@@ -585,4 +588,67 @@ img::after {
font-weight: 400;
color: #909399;
line-height: 17px;
}
\ No newline at end of file
}
.el-radio-group.customize{
.el-radio-button{
border-top: 1px solid #EBEFFE;
border-bottom: 1px solid #EBEFFE;
padding: 4px 0 4px 4px;
}
.el-radio-button:first-child{
border-left: 1px solid #EBEFFE;
border-radius: 2px 0 0 2px;
.el-radio-button__inner{
border:none;
}
}
.el-radio-button:last-child{
border-radius: 0 2px 2px 0;
border-right: 1px solid #EBEFFE;
padding: 4px;
}
.el-radio-button__inner{
padding: 2px 8px;
line-height: 20px;
border:none;
color: #303133;
font-weight: 400px;
}
.el-radio-button__orig-radio:checked+.el-radio-button__inner{
background: #EBEFFE;
border:none;
color: #2F54EB;
border-radius: 2px;
box-shadow:none;
font-weight: 500px;
}
}
.rank-icon {
display: block;
line-height: 20px;
// height: 20px;
text-align: center;
position: relative;
span {
font-weight: 700;
z-index: 1;
color: white;
font-size: 12px;
background: #98a3d9;
border-radius: 2px;
padding: 0 3px;
min-width: 14px;
min-height: 13px;
display: inline-block;
}
span.rank1 {
background: #2F54EB;
}
span.rank2 {
color: #303133;
background: #F2F3F5;
}
}
\ No newline at end of file
......@@ -58,7 +58,7 @@ export default {
{
path: 'ai-data-detail/:id',
name: '详情',
component: () => import('../../views/ai/ai-data-detail.vue')
component: () => import('../../views/ai/ai-data-index.vue')
}
]
};
......@@ -73,3 +73,50 @@ export const getPlanStatistics = params => requests('/api-marketing/statistics/p
//ai营销单价
export const getAiCalcSingle = params => requests(PREFIX + '/ai-calc-single', params, true, false, 'get');
// ai外呼门店维度分页列表接口
export const aiStoreOutboundRank = (params, urlData) => requests('/api-marketing/statistics/ai-store-outbound-rank', params, true, false, 'post', urlData);
// ai外呼门店维度无归属
export const aiStoreOutboundRankNoBelong = params => requests('/api-marketing/statistics/ai-store-outbound-rank-no-belong', params, true);
//
// ai外呼门店维度统计
export const aiStoreOutboundStatistics = params => requests('/api-marketing/statistics/ai-store-outbound-statistics', params, true);
// ai外呼门店维度导出
export const exportAiStoreOutbound = '/api-marketing/statistics/export-ai-store-outbound';
// ai外呼分组维度分页列表
export const aiStoreGroupOutboundRank = (params, urlData) => requests('/api-marketing/statistics/ai-store-group-outbound-rank', params, true, false, 'post', urlData);
// ai外呼分组维度统计
export const aiStoreGroupOutboundStatistics = params => requests('/api-marketing/statistics/ai-store-group-outbound-Statistics', params, true);
// ai外呼分组维度导出
export const exportAiStoreGroupOutbound = '/api-marketing/statistics/export-ai-store-group-outbound';
// 活动转换门店维度分页列表接口
export const aiStoreTransfer = (params, urlData) => requests('/api-marketing/statistics/ai-store-transfer', params, true, false, 'post', urlData);
// 活动转换门店维度无归属
export const aiStoreTransferNoBelong = params => requests('/api-marketing/statistics/ai-store-transfer-no-belong', params, true);
// 活动转换门店维度统计接口
export const aiStoreTransferStatistics = params => requests('/api-marketing/statistics/ai-store-transfer-statistics', params, true);
// 活动转换门店维度导出
export const exportAiStoreTransfer = '/api-marketing/statistics/export-ai-store-transfer';
// 活动转换分组维度分页列表接口
export const aiStoreGroupTransfer = (params, urlData) => requests('/api-marketing/statistics/ai-store-group-transfer', params, true, false, 'post', urlData);
// 活动转换分组维度统计
export const aiStoreGroupTransferStatistics = params => requests('/api-marketing/statistics/ai-store-group-transfer-Statistics', params, true);
// 活动转换分组维度导出
export const exportAiStoreGroupTransfer = '/api-marketing/statistics/export-ai-store-group-transfer';
// AI外呼门店分组数据
export const aiOutboundStoreGroupSplit = params => requests('/api-marketing/statistics/ai-outbound-store-group-split', params, true, false, 'get');
// 活动转换门店分组数据
export const aiTransformStoreGroupSplit = params => requests('/api-marketing/statistics/ai-transform-store-group-split', params, true, false, 'get');
......@@ -4,7 +4,7 @@ import config from '@/config';
export const url = config.api + PREFIX;
//卡券营销--卡券库--卡券分页列表
export const cardPageList = params => requests(PREFIX + 'card-page', params, false, false, 'POST', false);
export const cardPageList = params => requests(PREFIX + 'card-page', params);
//卡券营销--卡券库--卡券分页列表
export const updateCardStock = params => requests(PREFIX + 'update-card-stock', params);
......
......@@ -54,7 +54,7 @@ function popRequest(config) {
* @param {*} code
* @param {string} [message='请求错误']
*/
function handlerErr(code, message = '请求错误', alertError = true) {
function handlerErr(code, message = '请求错误') {
switch (code) {
// case 404:
// message = '404,错误请求';
......@@ -87,10 +87,8 @@ function handlerErr(code, message = '请求错误', alertError = true) {
message = '网关错误';
break;
}
if (alertError) {
// eslint-disable-next-line
Vue.prototype.$tips({ type: 'warning', message: message });
}
Vue.prototype.$tips({ type: 'warning', message: message });
}
const pendingRequest = new Map();
......@@ -98,7 +96,7 @@ const pendingRequest = new Map();
/**
* 请求地址,请求数据,是否静默,请求方法
*/
const requests = (url, data = {}, contentTypeIsJSON = false, isSilence = false, method = 'POST', alertError = true) => {
const requests = (url, data = {}, contentTypeIsJSON = false, isSilence = false, method = 'POST', urlData) => {
let _opts = { method, url };
let _timer = null;
// 如果用户没有开启权限 从参数中删除showSelfFlag
......@@ -109,7 +107,7 @@ const requests = (url, data = {}, contentTypeIsJSON = false, isSilence = false,
if (contentTypeIsJSON) {
_opts.data = data;
_opts.headers = { 'Content-Type': 'application/json' };
_opts.url += '?requestProject=marketing';
_opts.url += urlData ? '?requestProject=marketing' + urlData : '?requestProject=marketing';
} else {
_opts.data = qs.stringify(Object.assign({ requestProject: 'gic-web' }, data));
}
......@@ -143,7 +141,7 @@ const requests = (url, data = {}, contentTypeIsJSON = false, isSilence = false,
if (res.data.message.indexOf('抱歉') >= 0) {
Vue.prototype.$alert(res.data.message, '提示');
} else {
handlerErr(res.data.errorCode, res.data.message, alertError);
handlerErr(res.data.errorCode, res.data.message);
}
} else {
resolve(res.data);
......@@ -154,7 +152,7 @@ const requests = (url, data = {}, contentTypeIsJSON = false, isSilence = false,
popRequest(_random);
pendingRequest.delete(key);
if (res) {
handlerErr(res.response.status, '接口异常', alertError);
handlerErr(res.response.status, '接口异常');
}
reject(res);
});
......
<template>
<div class="data-detail">
<dm-sub-title style="margin-bottom: 5px">客户明细</dm-sub-title>
<div v-if="analyseFlag" class="page-tip">活动转化金额每天更新 1 次</div>
<el-input v-model="search.search" @change="handleCurrentChange(1)" type="text" placeholder="请输入姓名/昵称/手机号/会员卡号" prefix-icon="el-icon-search" clearable class="search-bar"></el-input>
<el-table :data="tableData" v-loading="loading">
......@@ -12,9 +11,14 @@
</svg>
<div>
<p class="member-name">
{{ row.memberNick || '--' }}<span class="member-subname" v-if="row.memberName">({{ row.memberName }})</span>
<template v-if="row.memberNick">
{{ row.memberNick || '--' }}
</template>
<span class="member-subname" v-if="row.memberName">
{{ row.memberNick ? `(${row.memberName})` : row.memberName }}
</span>
</p>
<p class="member-name">{{ row.memberMobile || '--' }}</p>
<p class="member-name" v-if="row.memberMobile">{{ row.memberMobile }}</p>
</div>
</div>
</el-table-column>
......@@ -69,6 +73,7 @@ export default {
this.showSendTime = this.$route.query.flag == 1;
this.analyseFlag = this.$route.query.analyseFlag == 1;
this.getData();
console.log('mounted3');
},
methods: {
getData() {
......@@ -117,7 +122,7 @@ export default {
<style lang="scss" scoped>
.data-detail {
padding: 20px;
padding: 5px 20px 20px 20px;
.search-bar {
margin-bottom: 20px;
width: 290px;
......
<template>
<div class="aiDataIndex">
<el-tabs v-model="activeName">
<el-tab-pane label="AI外呼数据" name="1">
<aiDataAiCall v-if="activeName == '1'" />
</el-tab-pane>
<el-tab-pane label="活动转化数据" name="2" v-if="this.$route.query.analyseFlag == 1">
<aiDataActiveDetail v-if="activeName == '2'" />
</el-tab-pane>
<el-tab-pane label="客户明细" name="3">
<aiDataDetail v-if="activeName == '3'" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import aiDataActiveDetail from './ai-data-active-data.vue';
import aiDataAiCall from './ai-data-aiCall.vue';
import aiDataDetail from './ai-data-detail.vue';
export default {
data() {
return {
activeName: '1'
};
},
components: {
aiDataActiveDetail,
aiDataAiCall,
aiDataDetail
},
created() {
console.log(this.$route.query.name);
this.activeName = this.$route.query.name;
}
};
</script>
<style lang="scss" scoped>
.aiDataIndex {
/deep/ .el-tabs__nav-wrap {
padding-left: 20px;
}
}
</style>
......@@ -8,7 +8,7 @@
<dm-sub-title title-align="space-between">
<div>
AI外呼数据<span class="title-tip">(数据实时更新)</span>
<el-button class="check-detail" @click="toDetailPage">查看详情</el-button>
<el-button class="check-detail" @click="toDetailPage(1)">查看详情</el-button>
</div>
</dm-sub-title>
<ai-data :ai-data-show="flags"></ai-data>
......@@ -17,7 +17,7 @@
<dm-sub-title title-align="space-between">
<div>
活动转化数据<span class="title-tip">(数据每天更新 1 次)</span>
<el-button class="check-detail" @click="toDetailPage">查看详情</el-button>
<el-button class="check-detail" @click="toDetailPage(2)">查看详情</el-button>
</div>
</dm-sub-title>
<conversion :member-type="flags.memberType" :filter-json="filterJson"></conversion>
......@@ -49,8 +49,8 @@ export default {
getFilterJson(filterJson) {
this.filterJson = filterJson;
},
toDetailPage() {
this.$router.push(`/ai/ai-data-detail/${this.$route.params.id}?flag=${this.flags.smsFlag}&analyseFlag=${this.flags.analyseFlag}`);
toDetailPage(name) {
this.$router.push(`/ai/ai-data-detail/${this.$route.params.id}?flag=${this.flags.smsFlag}&analyseFlag=${this.flags.analyseFlag}&name=${name}`);
}
}
};
......
......@@ -174,7 +174,8 @@ export default {
label: '平均通话时长',
value: '',
key: 'averageCallDuration',
type: 'time'
type: 'number',
unit: 'S'
}
],
[
......@@ -207,9 +208,13 @@ export default {
case 'time':
item.value = '--';
if (typeof value == 'number') {
const h = Math.floor(value / 3600);
const m = Math.floor((value % 3600) / 60);
const s = value % 60;
const h = Math.floor(value / 3600)
.toString()
.padStart(2, '0');
const m = Math.floor((value % 3600) / 60)
.toString()
.padStart(2, '0');
const s = (value % 60).toString().padStart(2, '0');
item.value = `${h}:${m}:${s}`;
}
break;
......@@ -261,26 +266,21 @@ export default {
const chart = new G2.Chart({
container: 'chart-tag',
forceFit: true,
height: 220,
padding: 10
height: 260,
padding: 0
});
chart.source(data.filter(el => el.count > 0));
chart.coord('theta');
chart.legend(false);
chart.coord('theta', {
radius: 0.75,
innerRadius: 0.6
});
chart.legend(null);
chart
.intervalStack()
.position('count')
.color('percent', ['#2d4dd1', '#239EFF', '#14C9C9', '#05B770', '#F69F3E', '#7D59E0'])
.color('label', ['#2d4dd1', '#239EFF', '#14C9C9', '#05B770', '#F69F3E', '#7D59E0'])
.label('percent', val => {
return {
offset: -40,
textStyle: {
fontSize: 10,
textAlign: 'center',
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
fill: '#fff'
},
formatter: (text, item) => {
return item.point.label + '\n' + item.point.percent;
}
......@@ -313,26 +313,22 @@ export default {
const chart = new G2.Chart({
container: 'chart-record',
forceFit: true,
height: 220,
padding: 10
height: 260,
padding: 0
});
chart.source(data.filter(el => el.count > 0));
chart.coord('theta');
chart.legend(false);
console.log(data);
chart.coord('theta', {
radius: 0.75,
innerRadius: 0.6
});
chart.legend(null);
chart
.intervalStack()
.position('count')
.color('percent', ['#2d4dd1', '#239EFF', '#14C9C9'])
.color('label', ['#2d4dd1', '#239EFF', '#14C9C9'])
.label('percent', () => {
return {
offset: -40,
textStyle: {
fontSize: 10,
textAlign: 'center',
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
fill: '#fff'
},
formatter: (text, item) => {
return item.point.label + '\n' + item.point.percent;
}
......@@ -409,6 +405,7 @@ export default {
<style lang="scss" scoped>
.chart-group {
width: 100%;
min-height: 300px;
.chart-title {
margin-top: 30px;
font-size: 14px;
......
......@@ -69,8 +69,3 @@ export default {
}
}
</style>
<style lang="scss">
.el-tooltip__popper.target-tooltip {
max-width: 475px !important;
}
</style>
<template>
<div>
<div class="aiList">
<el-tabs v-model="activeName">
<el-tab-pane label="外呼任务" name="first">
<task />
......@@ -28,4 +28,10 @@ export default {
};
</script>
<style></style>
<style lang="scss" scoped>
.aiList {
/deep/ .el-tabs__nav-wrap {
padding-left: 20px;
}
}
</style>
......@@ -207,6 +207,7 @@ export default {
case 'brithday_day':
if (!this.form.brithday_day) {
this.form.brithday_type = 1;
this.form.brithday_day = 1;
}
this.form.attention_day = 0;
this.form.lastCost_day = 0;
......
......@@ -351,7 +351,7 @@
<dm-sub-title type="line" v-else>
有效时间
</dm-sub-title>
<el-form-item label="有效时间" prop="endDate" :show-message="showMessage" v-if="form.consum_type == 1">
<el-form-item label="有效时间" prop="endDate" :show-message="showMessage" v-if="form.consum_type == 1" class="mt20">
<el-radio :disabled="isEdit && onlineStatus != 0 && form.expireType == 1" v-model="form.expireType" :label="0">长期有效</el-radio>
<el-radio :disabled="isEdit && onlineStatus != 0 && form.expireType == 0" v-model="form.expireType" :label="1">固定期限</el-radio>
<template v-if="form.effectType == 1">
......@@ -364,7 +364,7 @@
<el-date-picker v-if="form.expireType" v-model="form.endDate" :disabled="isEdit && onlineStatus == 2" type="datetime" :picker-options="endDatePickerOptions" placeholder="结束时间" @change="checkBeginEndTime"> </el-date-picker>
</template>
</el-form-item>
<el-form-item label="固定期限" prop="endDate" :show-message="showMessage" v-else>
<el-form-item label="固定期限" prop="endDate" :show-message="showMessage" v-else class="mt20">
<el-date-picker class="ml10" v-model="form.beginDate" :disabled="isEdit && onlineStatus != 0" type="datetime" placeholder="开始时间" @change="checkBeginEndTime" :picker-options="endDatePickerOptions"> </el-date-picker>
<el-date-picker v-model="form.endDate" :disabled="isEdit && onlineStatus == 2" type="datetime" :picker-options="isEdit ? endDatePickerOptions2 : endDatePickerOptions3" placeholder="结束时间" @change="checkBeginEndTime"> </el-date-picker>
</el-form-item>
......
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