Commit 2686951e by crushh

Merge branch 'feature/游戏营销' into master

parents 7baddbc9 c3b559f3
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e){var n=window.webpackJsonp_marketing;window.webpackJsonp_marketing=function(r,c,o){for(var d,f,i,u=0,b=[];u<r.length;u++)f=r[u],a[f]&&b.push(a[f][0]),a[f]=0;for(d in c)Object.prototype.hasOwnProperty.call(c,d)&&(e[d]=c[d]);for(n&&n(r,c,o);b.length;)b.shift()();if(o)for(u=0;u<o.length;u++)i=t(t.s=o[u]);return i};var r={},a={24:0};function t(n){if(r[n])return r[n].exports;var a=r[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,t),a.l=!0,a.exports}t.e=function(e){var n=a[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,t){n=a[e]=[r,t]});n[2]=r;var c=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.timeout=12e4,t.nc&&o.setAttribute("nonce",t.nc),o.src=t.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:"9df7b6d363b92e4c6a9e",1:"3920466424dd3d4adf12",2:"2d97ab8f38d7e456013e",3:"1721cf177828f3ef9e03",4:"2d43047a94aef3fd92d9",5:"8e2749d3a189eeb56c2e",6:"9a995f0cdff670d4f8d3",7:"e204eb08edf4921f88da",8:"8d9c6b1eac82ed1a52b3",9:"648d4e9bf392d8980030",10:"da8008653f8c25addc3a",11:"7c2067b1ba786ef1b65c",12:"f1227cca53669d779b78",13:"02b43cb717829d19e528",14:"be5aa3556f1c4057ad85",15:"410d734e15e42bc183a8",16:"91803d84dca88eb2778e",17:"6c7bfbfbe4bbd7a2633a",18:"3ba3b70d72322dd6fc56",19:"20596c2fde39ba8b7da1",20:"5dbfdb8d97c0910fdde0",21:"ad98c8e9d3749f2f1097"}[e]+".js";var d=setTimeout(f,12e4);function f(){o.onerror=o.onload=null,clearTimeout(d);var n=a[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),a[e]=void 0)}return o.onerror=o.onload=f,c.appendChild(o),r},t.m=e,t.c=r,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/marketing/",t.oe=function(e){throw console.error(e),e}}([]);
\ No newline at end of file
......@@ -5,16 +5,15 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="shortcut icon" href="./static/img/favicon.ico">
<title></title>
<!-- <title>GIC后台</title> -->
<link rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.options.BASE_URL %>static/fonts/iconfont.css">
<link rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.options.BASE_URL %>static/css/common.css">
<!-- <link rel="stylesheet" href="//at.alicdn.com/t/font_2996579_93aeeozj35q.css"> -->
<link rel="stylesheet" href="//at.alicdn.com/t/font_3229694_vfjtu9hqyrc.css"> <!--GIC3.0营销-->
<script src="//at.alicdn.com/t/font_3229694_vfjtu9hqyrc.js"></script> <!--GIC3.0营销-->
<link rel="stylesheet" href="//at.alicdn.com/t/font_2996579_875h3lycepk.css"> <!-- 3.0企业 -->
<script src="//at.alicdn.com/t/font_2996579_875h3lycepk.js"></script><!-- 3.0企业 -->
<link rel="stylesheet" href="//at.alicdn.com/t/font_688955_2dxzdzrb3a7.css"> <!--GIC后台3.0-->
<script src="//at.alicdn.com/t/font_688955_2dxzdzrb3a7.js"></script> <!--GIC后台3.0-->
<link rel="stylesheet" href="//at.alicdn.com/t/font_3229694_f4zx0uhc8y.css"> <!--GIC3.0营销-->
<script src="//at.alicdn.com/t/font_3229694_f4zx0uhc8y.js"></script> <!--GIC3.0营销-->
<link src="//at.alicdn.com/t/font_2859043_udehp133w1.css"><!--3.0组件库-->
<script src="//at.alicdn.com/t/font_2859043_udehp133w1.js"></script><!--3.0组件库-->
<link rel="stylesheet" href="//at.alicdn.com/t/font_2996579_dv9vctk5vdt.css"> <!-- 3.0企业 -->
<script src="//at.alicdn.com/t/font_2996579_dv9vctk5vdt.js"></script><!-- 3.0企业 -->
<!-- <link rel="stylesheet" href="//web-1251519181.file.myqcloud.com/components/element.2.12.0.css"> -->
<!-- element 皮肤 -->
<!-- <link rel="stylesheet" type="text/css" href="http://web-1251519181.file.myqcloud.com/lib/elementUI/theme.1.0.1/index.css"> -->
......@@ -41,6 +40,6 @@
<script src="//web-1251519181.file.myqcloud.com/components/pagination.1.0.8.js"></script><!-- 分页器 -->
<script src="//web-1251519181.file.myqcloud.com/components/track.1.0.4.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/upload-file.1.0.10.js"></script><!-- 文件上传 -->
<script src="//web-1251519181.file.myqcloud.com/components/steps.1.0.1.js"></script>
<!-- <script src="//web-1251519181.file.myqcloud.com/components/steps.1.0.1.js"></script> -->
</body>
</html>
......@@ -66,7 +66,6 @@ export default {
<style lang="scss">
@import './assets/theme/index.css';
@import './assets/style/base/index.scss';
@import './assets/iconfont/iconfont.css';
#app {
height: 100%;
background-color: #f0f2f5;
......
......@@ -144,12 +144,18 @@ a:hover {
.mt5{
margin-top: 5px!important;
}
.mt6{
margin-top: 6px!important;
}
.mt10{
margin-top: 10px!important;
}
.mt15{
margin-top: 15px!important;
}
.mt16{
margin-top: 16px!important;
}
.mt17{
margin-top: 17px!important;
}
......@@ -189,6 +195,9 @@ a:hover {
.mr10{
margin-right: 10px!important;
}
.mr16{
margin-right: 16px!important;
}
.mr20{
margin-right: 20px!important;
}
......@@ -250,15 +259,24 @@ a:hover {
.w160{
width: 160px!important;
}
.w180{
width: 180px !important;
}
.w200{
width: 200px!important;
}
.w250{
width: 250px!important;
}
.w256{
width: 256px!important;
}
.w260{
width: 260px!important;
}
.w270{
width: 270px!important;
}
.w280{
width: 280px!important;
}
......@@ -271,6 +289,9 @@ a:hover {
.w350{
width: 350px!important;
}
.w382{
width: 382px!important;
}
.w400{
width: 400px!important;
}
......@@ -592,12 +613,12 @@ img::after {
.el-radio-group.customize{
.el-radio-button{
border-top: 1px solid #EBEFFE;
border-bottom: 1px solid #EBEFFE;
border-top: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
padding: 4px 0 4px 4px;
}
.el-radio-button:first-child{
border-left: 1px solid #EBEFFE;
border-left: 1px solid #DCDFE6;
border-radius: 2px 0 0 2px;
.el-radio-button__inner{
border:none;
......@@ -605,7 +626,7 @@ img::after {
}
.el-radio-button:last-child{
border-radius: 0 2px 2px 0;
border-right: 1px solid #EBEFFE;
border-right: 1px solid #DCDFE6;
padding: 4px;
}
.el-radio-button__inner{
......@@ -614,6 +635,7 @@ img::after {
border:none;
color: #303133;
font-weight: 400px;
min-width: 60px;
}
.el-radio-button__orig-radio:checked+.el-radio-button__inner{
background: #EBEFFE;
......@@ -651,4 +673,36 @@ img::after {
}
}
.wxapp {
width: 253px;
height: 32px;
background: #f2f3f5;
border-radius: 2px;
border: 1px solid #dcdfe6;
padding: 0 11px;
box-sizing: border-box;
display: inline-flex;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.mini-sub-title {
width: 100%;
height: 40px;
display: flex;
box-sizing: border-box;
align-items: center;
margin-bottom: 5px;
.line {
width: 2px;
height: 14px;
background: #2f54eb;
margin-right: 8px;
}
.text {
font-size: 14px;
color: #303133;
width: 100%;
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
}
.dm-pagination {
text-align: right;
margin: 24px 0 10px 0;
margin: 20px 0 10px 0;
}
.label-hidden .el-checkbox__label,.label-hidden .el-radio__label{
......
<template>
<div class="member-group-container">
<div class="echo-member-group" v-if="echoFlag">
<div class="subTitle">
<div class="mini-sub-title">
<div class="line"></div>
<div class="text">
<div class="space-between">
......@@ -17,7 +17,7 @@
<span class="groupName"> {{ item.groupName }}</span>
</div>
</div>
<el-dialog title="选择客户分组" :visible.sync="visiable" width="1000px" :before-close="close">
<el-dialog title="选择客户分组" :visible.sync="visiable" width="1000px" :before-close="close" custom-class="member-group-dialog" class="member-group-dialog-wrap">
<div class="member-group">
<div class="left">
<el-tabs v-model="activeName">
......@@ -252,9 +252,29 @@ export default {
</script>
<style lang="scss" scoped>
.member-group-dialog-wrap {
display: flex;
align-items: center;
justify-content: center;
}
.member-group-dialog {
margin: 0 !important;
padding: 0 4px 4px;
max-height: 700px;
.el-dialog__header {
display: none;
}
.el-dialog__body {
padding-bottom: 0 !important;
}
.el-dialog__footer {
padding: 20px 0;
}
}
.delBtn {
color: #f5222d;
}
.space-between {
display: flex;
justify-content: space-between;
......
<template>
<div class="ruleFilter">
<div class="echo-member-group" v-if="echoFlag">
<div class="subTitle">
<div class="mini-sub-title">
<div class="line"></div>
<div class="text">
<div class="space-between">
......
......@@ -76,6 +76,10 @@ export default {
height: 100
};
}
},
imgType: {
type: String,
default: 'jpg/jpeg/png/gif'
}
},
data() {
......
<template>
<div>
<label class="el-button el-button--primary" :class="{ 'is-disabled': copyDisabled }" :style="labelStyle">
<label :class="[textType ? 'el-button el-button--text el-button--small' : 'el-button el-button--primary', copyDisabled ? 'is-disabled' : '']" :style="labelStyle">
<i v-if="icon" :class="icon"></i>
{{ label }}
<input type="file" style="display:none;" :disabled="copyDisabled" accept="image/gif, image/jpeg,image/png" ref="uploader" v-upload="this" />
......@@ -63,6 +63,10 @@ export default {
disabled: {
type: Boolean,
default: false
},
textType: {
type: Boolean,
default: false
}
},
watch: {
......
......@@ -44,12 +44,21 @@ export default {
el.addEventListener('change', function(e) {
if (e.target.value) binding.value.loading = true;
else return;
console.log(el.files);
const size = binding.value.limit && binding.value.limit.maxSize ? binding.value.limit.maxSize : 2; // 默认最大2M限制
if (el.files[0].size > size * 1024 * 1024) {
binding.value.$tips({ type: 'warning', message: `上传${binding.value.acceptType.indexOf('image') >= 0 ? '图片' : '文件'}不能大于${size}M` });
binding.value.$tips({ type: 'warning', message: `上传${binding.value.fileType.indexOf('image') >= 0 ? '图片' : '文件'}不能大于${size}M` });
binding.value.loading = false;
return;
}
if (binding.value.imgType) {
const arr = binding.value.imgType.split('/');
if (!arr.includes(el.files[0].type.split('/')[1])) {
binding.value.$tips({ type: 'warning', message: `请上传${binding.value.imgType}类型` });
binding.value.loading = false;
return;
}
}
// 启用限制图片宽高大小
if (binding.value.limit && binding.value.limit.type) {
const { width, height } = binding.value.limit;
......
......@@ -6,6 +6,7 @@ import { axios } from './service/api/index';
import directives from './directives';
import limit from '@/utils/limiting';
import scrollToError from '@/assets/common.js';
import '@/utils/sticky.js';
Vue.config.productionTip = false;
if (process.env.NODE_ENV == 'development') { // eslint-disable-line
......@@ -17,6 +18,7 @@ Vue.prototype.scrollToError = scrollToError;
Vue.prototype.axios = axios;
Vue.prototype.axios.withCredentials = true;
Vue.prototype.axios.defaults.timeout = 50000;
window.ELEMENT.Dialog.props.closeOnClickModal.default = false;
Object.keys(directives).map(item => Vue.directive(item, directives[item]));
window.$bus = new Vue();
let flag = false;
......
......@@ -2,8 +2,9 @@ import Vue from 'vue';
import Router from 'vue-router';
import routes from './routes';
import store from '@/store';
import axios from 'axios';
import axios from 'axios';
console.log(routes);
Vue.use(Router);
let router = new Router({
......@@ -25,9 +26,11 @@ let router = new Router({
router.beforeEach((to, from, next) => {
// 获取是否限流
let path = to.path;
Object.keys(to.params).map(key => {
path = path.replace(to.params[key], `:${key}`);
});
axios
.get(`/api-plug/rate-limit?requestPath=${path}&enterpriseId=${store.state.marketing.enterpriseId}`)
.then(limitRes => {
......@@ -47,15 +50,6 @@ router.beforeEach((to, from, next) => {
.catch(() => {
next();
});
// document.title = to.name;
// if (to.path != '/limit' && store.state.marketing.isLimit) {
// next({ path: '/limit' });
// } else if (to.path == '/limit' && !store.state.marketing.isLimit) {
// next({ path: '/' });
// } else {
// next();
// }
});
export default router;
......@@ -219,6 +219,61 @@ export default {
meta: {
path: '/game/klfl'
}
},
{
path: 'cmh',
name: '拆盲盒列表',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/index.vue'),
meta: {}
},
{
path: 'cmh/statistics/:id',
name: '拆盲盒数据统计',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/statistics/statistics.vue')
},
{
path: 'cmh/statistics/activityDetail/:id',
name: '拆盲盒数据统计-活动明细',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/statistics/activityDetail.vue')
},
{
path: 'cmh/statistics/prizeDetail/:id',
name: '拆盲盒数据统计-奖品明细',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/statistics/prizeDetail.vue')
}
]
};
export const cmhDetail = [
{
path: '/game/cmh/add',
name: '新建拆盲盒',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/form.vue'),
meta: {
type: 'add'
}
},
{
path: '/game/cmh/edit/:id',
name: '编辑拆盲盒',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/form.vue'),
meta: {
type: 'edit'
}
},
{
path: '/game/cmh/info/:id',
name: '查看拆盲盒',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/form.vue'),
meta: {
type: 'info'
}
},
{
path: '/game/cmh/copy/:id',
name: '复制拆盲盒',
component: () => import(/* webpackChunkName: "game" */ '../../views/game/cmh/form.vue'),
meta: {
type: 'copy'
}
}
];
......@@ -9,7 +9,7 @@ import activityCenter from '@/views/activityCenter';
//微信营销
import wechat from './modules/wechat';
//游戏营销
import game from './modules/game';
import game, { cmhDetail } from './modules/game';
//短信营销
import message from './modules/message';
//卡券营销
......@@ -66,6 +66,7 @@ export default [
ai
]
},
...cmhDetail,
{
path: '/401',
name: '未授权',
......
......@@ -156,5 +156,57 @@ export const getCouponStock = params => requests(PREFIX + 'get-coupon-stock', pa
// 游戏营销-- 获取游戏更新时间
export const getGameDataUpdatetime = params => requests(PREFIX + 'get-game-data-updatetime', params);
//游戏营销--拆盲盒游戏分页列表
export const cmhPage = params => requests(PREFIX + '/game-pro/page', params, true);
//游戏营销--拆盲盒游戏统计数据
export const cmhStatistics = params => requests(PREFIX + '/game-pro/page-statistics', params);
//游戏营销--拆盲盒游戏新建
export const initActivity = params => requests(PREFIX + '/game-pro/init-game', params, true);
//游戏营销--拆盲盒-获取游戏详细信息
export const getGameDetail = params => requests(PREFIX + '/game-pro/get-game-detail', params, true, false, 'get');
//游戏营销--拆盲盒-根据类型获取游戏模板信息
export const getGameTemplateByType = params => requests(PREFIX + '/game-pro/get-game-template-by-type', params, true, false, 'get');
// 生成小程序游戏链接
export const generateMiniProgramLink = params => requests('api-admin/get-page-link-game', params);
// 生成小程序游戏链接-拆盲盒
export const getQrGame = params => requests(PREFIX + '/game-pro/get-qr-game', params, true, false, 'get');
//卡券回显 key 卡券ID ; value 卡券状态 0正常 1删除 2失效
export const cardView = params => requests(PREFIX + '/game-pro/card-view', params, true);
// 停止游戏-拆盲盒
export const stopGame = params => requests(PREFIX + '/game-pro/stop-game', params, true, false, 'get');
// 转换成页面链接
export const getPageLink = params => requests('api-admin/get-page-link', params, true, false, 'get');
// 游戏活动分页查询-统计
export const pageStatistics = params => requests(PREFIX + 'game-pro/page-statistics', params, true, false, 'post');
// 活动基础数据-拆盲盒
export const getGameOverview = params => requests(PREFIX + '/game-data/get-game-overview', params, true, false, 'get');
// 游戏卡券核销数据-拆盲盒
export const getGameCardWriteOff = params => requests(PREFIX + '/game-data/get-game-card-write-off', params, true, false, 'get');
// 游戏奖品数据-拆盲盒
export const getGamePrize = params => requests(PREFIX + 'game-data/get-game-prize', params, true, false, 'get');
// 游戏活动基础数据明细-拆盲盒
export const getGameDataDetail = params => requests(PREFIX + '/game-data/get-game-detail', params, true, false, 'get');
//游戏活动基础明细导出-拆盲盒
export const exportGameDetail = config.api + PREFIX + '/game-data/export-game-detail';
// 奖品明细-拆盲盒
export const getPrizeDetail = params => requests(PREFIX + '/game-data/get-game-prize-detail', params, true, false, 'get');
//奖品明细导出-拆盲盒
export const exportGamePrizeDetail = config.api + PREFIX + '/game-data/export-game-prize-detail';
......@@ -18,7 +18,8 @@ const state = {
userId: '',
departAuth: 0,
superAdmin: 0,
keepAlive: []
keepAlive: [],
showLayout: true
};
// getters
......@@ -38,7 +39,8 @@ const getters = {
},
getLimitCodeList: state => state.limitCodeList,
// 查询是否被限制使用微信模版消息 true:被限制
getLimitWechatTemplateMessage: state => state.limitCodeList.some(el => el == 'wxTemplateMessage')
getLimitWechatTemplateMessage: state => state.limitCodeList.some(el => el == 'wxTemplateMessage'),
getShowLayout: state => state.showLayout
};
// actions
......@@ -109,6 +111,9 @@ const mutations = {
},
setKeepAlive(state, val) {
state.keepAlive = val;
},
mutations_Layout(state, val) {
state.showLayout = val;
}
};
......
......@@ -378,7 +378,7 @@ export const numFormat = function(num) {
// if (process.env.NODE_ENV == 'development') {
// console.error('numFormat Arguments TypeError: Arguments type is not number');
// }
return '--';
return '- -';
}
return num.toString().replace(/\d+/, n => {
// 先提取整数部分
......
import Vue from 'vue';
// 给固定头设置样式
function doFix(dom, top) {
dom.style.position = 'fixed';
dom.style.zIndex = '2001';
dom.style.top = top + 'px';
dom.style.display = 'block';
}
// 给固定头取消样式
function removeFix(dom) {
dom.style.position = 'static';
dom.style.top = '0';
dom.style.zIndex = '0';
dom.style.display = 'none';
}
// 给固定头添加class
function addClass(dom, fixtop) {
const old = dom.className;
if (!old.includes('fixed')) {
dom.setAttribute('class', old + ' fixed');
doFix(dom, fixtop);
}
}
// 给固定头移除class
function removeClass(dom) {
const old = dom.className;
const idx = old.indexOf('fixed');
if (idx !== -1) {
const newClass = old.substr(0, idx - 1);
dom.setAttribute('class', newClass);
removeFix(dom);
}
}
// 具体判断是否固定头的主函数
function fixHead(parent, el, top) {
/**
* myTop 当前元素距离滚动父容器的高度,
* fixtop 当前元素需要设置的绝对定位的高度
* parentHeight 滚动父容器的高度
*/
let myTop;
let fixtop;
let parentHeight;
// 表头DOM节点
const dom = el.lastChild;
// const dom = feakHead;
if (parent.tagName) {
// 如果是DOM内局部滚动
// 当前元素距离滚动父容器的高度= 当前元素距离父元素的高度-父容器的滚动距离-表头的高度
myTop = el.offsetTop - parent.scrollTop - dom.offsetHeight;
// 父元素高度
const height = getComputedStyle(parent).height;
parentHeight = Number(height.slice(0, height.length - 2));
// 绝对定位高度 = 滚动父容器相对于视口的高度 + 传入的吸顶高度
fixtop = top + parent.getBoundingClientRect().top;
// 如果自己距离顶部距离大于父元素的高度,也就是自己还没在父元素滚动出来,直接return
if (myTop > parentHeight) {
return;
}
} else {
// document节点滚动
// 当前元素距离滚动父容器的高度 = 当前元素距离视口顶端的距离
myTop = el.getBoundingClientRect().top;
// 父元素高度 = 视口的高度
parentHeight = window.innerHeight;
// 绝对定位高度 = 传入的吸顶高度
fixtop = top;
// 如果自己距离顶部距离大于父元素的高度,也就是自己还没在父元素滚动出来,直接return
if (myTop > document.documentElement.scrollTop + parentHeight) {
return;
}
}
// 如果 已经滚动的上去不在父容器显示了。直接return
if (Math.abs(myTop) > el.offsetHeight + 100) {
return;
}
if (myTop < 0 && Math.abs(myTop) > el.offsetHeight) {
// 如果当前表格已经完全滚动到父元素上面,也就是不在父元素显示了。则需要去除fixed定位
removeClass(dom);
} else if (myTop <= 0) {
// 如果表头滚动到 父容器顶部了。fixed定位
addClass(dom, fixtop);
} else if (myTop > 0) {
// 如果表格向上滚动 又滚动到父容器里。取消fixed定位
removeClass(dom);
} else if (Math.abs(myTop) < el.offsetHeight) {
// 如果滚动的距离的绝对值小于自身的高度,也就是说表格向上滚动,刚刚显示出表格的尾部是需要将表头fixed定位
addClass(dom, fixtop);
}
}
// 设置头部固定时表头外容器的宽度写死为表格body的宽度
function setHeadWidth(el) {
// 获取到当前表格个表格body的宽度
const width = getComputedStyle(el.getElementsByClassName('el-table__body-wrapper')[0]).width;
// 给表格设置宽度。这里默认一个页面中的多个表格宽度是一样的。所以直接遍历赋值,也可以根据自己需求,单独设置
const tableParent = el.getElementsByClassName('el-table__header-wrapper');
feakHead.style.width = width;
for (let i = 0; i < tableParent.length; i++) {
tableParent[i].style.width = width;
}
}
function cloneNode(el) {
// 复制一个表头且将固定的表头删除is-hidden样式
const tableParent = el.getElementsByClassName('el-table__header-wrapper');
feakHead = tableParent[0].cloneNode(true);
let thArr = feakHead.lastChild.lastChild.firstChild.childNodes;
if (thArr.length > 1 && !el.lastChild.className.includes('el-table__header-wrapper')) {
feakHead.style.display = 'none';
let len = thArr ? thArr.length : 0;
for (let i = 0; i < len; i++) {
let className = thArr[i].className;
if (className.includes('is-hidden')) {
thArr[i].className = className.replace('is-hidden', '');
}
}
el.appendChild(feakHead);
}
}
/**
* 这里有三个全局对象。用于存放监听事件。方便组件销毁后移除监听事件
*/
const fixFunObj = {}; // 用于存放滚动容器的监听scroll事件
const setWidthFunObj = {}; // 用于存放页面resize后重新计算head宽度事件
const autoMoveFunObj = {}; // 用户存放如果是DOM元素内局部滚动时,document滚动时,fix布局的表头也需要跟着document一起向上滚动
let feakHead = {}; // 滚动时吸顶的表头 暂时只支持当前页面只有一个表格
// 全局注册 自定义事件
Vue.directive('sticky', {
// 当被绑定的元素插入到 DOM 中时……
inserted(el, binding, vnode) {
// 获取当前vueComponent的ID。作为存放各种监听事件的key
const uid = vnode.componentInstance._uid;
// 当window resize时 重新计算设置表头宽度,并将监听函数存入 监听函数对象中,方便移除监听事件
window.addEventListener(
'resize',
(setWidthFunObj[uid] = () => {
setHeadWidth(el);
})
);
// 获取当前滚动的容器是什么。如果是document滚动。则可默认不传入parent参数
const scrollParent = document.querySelector(binding.value.parent) || document;
// 给滚动容器加scroll监听事件。并将监听函数存入 监听函数对象中,方便移除监听事件
scrollParent.addEventListener(
'scroll',
(fixFunObj[uid] = () => {
fixHead(scrollParent, el, binding.value.top);
}),
true
);
// 如果是局部DOM元素内滚动。则需要监听document滚动,document滚动是同步让表头一起滚动。并将监听函数存入 监听函数对象中,方便移除监听事件
if (binding.value.parent) {
document.addEventListener(
'scroll',
(autoMoveFunObj[uid] = () => {
// 获取到表头DOM节点
const dom = el.children[1];
// 如果当前表头是fixed定位。则跟着document滚动一起滚
if (getComputedStyle(dom).position === 'fixed') {
// 滚动的距离是: 滚动父容器距离视口顶端高度 + 传入的吸顶固定距离
const fixtop = binding.value.top + scrollParent.getBoundingClientRect().top;
doFix(dom, fixtop, 'fixed');
}
}),
true
);
}
},
// component 更新后。重新计算表头宽度
componentUpdated(el) {
cloneNode(el);
setHeadWidth(el);
},
// 节点取消绑定时 移除各项监听事件。
unbind(el, binding, vnode) {
const uid = vnode.componentInstance._uid;
window.removeEventListener('resize', setWidthFunObj[uid]);
const scrollParent = document.querySelector(binding.value.parent) || document;
scrollParent.removeEventListener('scroll', fixFunObj[uid]);
if (binding.value.parent) {
document.removeEventListener('scroll', autoMoveFunObj[uid]);
}
}
});
......@@ -61,7 +61,7 @@
<script>
import { aiTransformStoreGroupSplit, getActivityDetail, aiStoreTransfer, aiStoreTransferNoBelong, aiStoreTransferStatistics, exportAiStoreTransfer, aiStoreGroupTransfer, aiStoreGroupTransferStatistics, exportAiStoreGroupTransfer } from '@/service/api/aiApi.js';
import TargetGroup from '@/views/ai/ai-data-report/target-group.vue';
import TargetGroup from '@/components/target-group.vue';
import { numFormat } from '@/utils/index.js';
const tableHead = [
......
......@@ -61,7 +61,7 @@
<script>
import { aiOutboundStoreGroupSplit, getActivityDetail, aiStoreOutboundRank, aiStoreOutboundRankNoBelong, aiStoreOutboundStatistics, exportAiStoreOutbound, aiStoreGroupOutboundRank, aiStoreGroupOutboundStatistics, exportAiStoreGroupOutbound } from '@/service/api/aiApi.js';
import TargetGroup from '@/views/ai/ai-data-report/target-group.vue';
import TargetGroup from '@/components/target-group.vue';
import { numFormat } from '@/utils/index.js';
let tableHead = [
......
......@@ -71,7 +71,7 @@ import G2 from '@antv/g2';
import DataSet from '@antv/data-set';
import { numFormat } from '@/utils/index.js';
import { getOutBound, getIntentionLabel, getBillAnalysis, getCallDuration } from '@/service/api/aiApi.js';
import TargetGroup from './target-group.vue';
import TargetGroup from '@/components/target-group.vue';
export default {
name: 'AiData',
......
......@@ -71,7 +71,7 @@
<script>
import { getPlanStatistics, getMemberCrowd, getComparativeData } from '@/service/api/aiApi.js';
import { numFormat } from '@/utils/index.js';
import TargetGroup from './target-group.vue';
import TargetGroup from '@/components/target-group.vue';
export default {
name: 'Conversion',
......
......@@ -83,7 +83,7 @@
<el-table-column min-width="80" align="left" prop="receiveName" label="投放渠道"></el-table-column>
<el-table-column width="100" align="left" prop="receiveName" label="来源明细">
<template slot-scope="scope">
{{ scope.row.receiveCode == 'RECEIVE_003' || scope.row.receiveCode == 'RECEIVE_005' || scope.row.receiveCode == 'RECEIVE_007' || scope.row.receiveCode == 'RECEIVE_004' || scope.row.receiveCode == 'RECEIVE_010' || scope.row.receiveCode == 'RECEIVE_025' ? scope.row.receiveTypeExcel : '--' }}
{{ scope.row.receiveCode == 'RECEIVE_003' || scope.row.receiveCode == 'RECEIVE_005' || scope.row.receiveCode == 'RECEIVE_007' || scope.row.receiveCode == 'RECEIVE_004' || scope.row.receiveCode == 'RECEIVE_010' || scope.row.receiveCode == 'RECEIVE_025' || scope.row.receiveCode == 'RECEIVE_026' ? scope.row.receiveTypeExcel : '--' }}
</template>
</el-table-column>
<el-table-column width="100" align="left" prop="receiveName" label="核销来源">
......
......@@ -88,7 +88,10 @@ export default {
storedLowest: 0,
storedMax: 0,
consum_type: 1,
gift_flag: 0
gift_flag: 0,
consumer_store_type: 0,
store_filter_id: '',
store_filter_id_new: ''
},
template: {
headerColor: '#173177',
......@@ -176,6 +179,7 @@ export default {
payment: false
},
storedChecked: false,
storedShopChecked: false,
discount_limit: { type: 1, count: undefined, flag: false }, // 适用商品折扣
useStoredFlag: 0, //显示储值触发和会员卡升级事件
analyseConfig: {
......@@ -584,9 +588,17 @@ export default {
if (this.form.effectAction === 'stored' && result.effectTriggerJson) {
let cost = JSON.parse(result.effectTriggerJson) || {};
this.storedChecked = true;
this.form.storedLowest = cost.lowest_cost;
this.form.storedMax = cost.max_cost;
this.storedChecked = cost.hasOwnProperty('lowest_cost') || cost.hasOwnProperty('max_cost');
if (this.storedChecked) {
this.form.storedLowest = cost.lowest_cost;
this.form.storedMax = cost.max_cost;
}
this.storedShopChecked = cost.hasOwnProperty('consumer_store_type');
if (this.storedShopChecked) {
this.form.consumer_store_type = cost.consumer_store_type;
cost.consumer_store_type == 0 ? (this.form.store_filter_id = cost.store_filter_id) : (this.form.store_filter_id_new = cost.store_filter_id);
}
}
}
if (!this.form.templateUseEnable) return;
......@@ -806,7 +818,7 @@ export default {
// 如果是消费触发
if (this.form.effectAction === 'consume') {
// 这里判断部分门店是否为空
if (this.consumeChecked.store && this.consumeChecked.store) {
if (this.consumeChecked.store) {
let data = this.form.consumerStoreType === 1 ? await this.$refs.newStoreCard.getList() : await this.$refs.storeCard.getRightList();
if (!data.length) {
this.$tips({ type: 'warning', message: '门店不能为空' });
......@@ -870,6 +882,14 @@ export default {
this.$tips({ type: 'warning', message: '储值金额区间值填写错误' });
return;
}
// 这里判断部分门店是否为空
if (this.storedShopChecked) {
let data = this.form.consumer_store_type === 1 ? await this.$refs.newStoreCard2.getList() : await this.$refs.storeCard2.getRightList();
if (!data.length) {
this.$tips({ type: 'warning', message: '门店不能为空' });
return;
}
}
}
// 会员卡升降级
// if (this.form.effectAction === 'degrade' || this.form.effectAction === 'upgrade') {
......@@ -1005,8 +1025,17 @@ export default {
params = Object.assign(params, consumeObj); // 合并消费触发数据
}
if (this.form.effectAction === 'stored' && this.storedChecked) {
params.effectTriggerJson = JSON.stringify({ lowest_cost: this.form.storedLowest || undefined, max_cost: this.form.storedMax || undefined });
if (this.form.effectAction === 'stored') {
let storedObj = {};
if (this.storedChecked) {
storedObj.lowest_cost = this.form.storedLowest || undefined;
storedObj.max_cost = this.form.storedMax || undefined;
}
if (this.storedShopChecked) {
storedObj.consumer_store_type = this.form.consumer_store_type;
storedObj.store_filter_id = this.form.consumer_store_type === 0 ? this.form.store_filter_id : this.form.store_filter_id_new;
}
Object.keys(storedObj).length ? (params.effectTriggerJson = JSON.stringify(storedObj)) : '';
}
}
params.marketingActivityId = this.form.marketingActivityId || ''; // 否 String 营销场景
......
......@@ -297,6 +297,22 @@
</div>
</div>
</el-form-item>
<el-form-item>
<div class="no_label_form_label">
<el-checkbox class="el-form-item__label align-left" :disabled="!isAdd" v-model="storedShopChecked">储值门店</el-checkbox>
<div>
<span class="fz14 gray" v-show="!storedShopChecked">所有储值门店</span>
</div>
<div v-show="storedShopChecked">
<el-radio-group :disabled="!isAdd" v-model="form.consumer_store_type">
<el-radio :label="0">固定门店</el-radio>
<el-radio :label="1">按门店条件</el-radio>
</el-radio-group>
<vue-gic-store-card style="margin:20px 0 0 35px" v-if="form.consumer_store_type === 0" v-bind="storeParams" :readonly="!isAdd" ref="storeCard2" :uuid.sync="form.store_filter_id"></vue-gic-store-card>
<dm-store-selector style="margin:20px 0 0 35px" v-if="form.consumer_store_type === 1" ref="newStoreCard2" :readonly="!isAdd" :uuid.sync="form.store_filter_id_new"></dm-store-selector>
</div>
</div>
</el-form-item>
</section>
<!-- 营销次数配置 重复 -->
<section class="dm-form__wrap" v-if="form.effectType == 1">
......@@ -325,14 +341,6 @@
<span class="vertical-middle" v-if="form.marketingTimesType !== -1"><el-input-number :disabled="!isAdd" controls-position="right" class="w150" :min="1" v-model="form.marketingTimes"></el-input-number></span>
<span class="pl20 fz13 gray"> * 该营销计划{{ marketingTimesTips }}</span>
</el-form-item>
<!-- <div v-else>
<div class="ecm-title">消费触发配置</div>
<div class="ecm-content">
<el-radio :label="1">每天触发一次</el-radio>
<el-radio :label="-1">每天叠加触发</el-radio>
</div>
</div> -->
<el-form-item label="营销次数配置" prop="marketingTimesType" v-else>
<div class="ecm-content" style="margin:8px 0 0 0;">
<el-radio :disabled="!isAdd" :label="1" v-model="form.marketingTimesType">每天触发一次 <span class="gray fz13 ml58">客户当天累计消费金额满足所配置的消费金额就会触发,只触发一次</span></el-radio>
......
......@@ -109,7 +109,7 @@
.dm-pagination {
text-align: right;
margin: 24px 0 10px 0;
margin: 20px 0 10px 0;
}
.text-left {
text-align: left;
......
......@@ -7,7 +7,7 @@
<template>
<card-profit-sum :list="list" />
<div class="title" style="margin-top:20px">
<h2 class="subtitle">卡券核销</h2>
<h2 class="mini-sub-title">卡券核销</h2>
<span>卡券核销情况统计通过当前智能营销计划投放的卡券,顾客使用该卡券消费的情况。即使消费时间已经过了收益有效期也会统计在内</span>
</div>
<el-table :data="tableData" v-if="tableData.length > 0" style="margin:20px 0 20px" max-height="710" header-cell-class-name="card-profit-header">
......
<template>
<div class="template-game">
<img class="bg-image" :src="backImageUrl" />
<div class="content">
<div class="right-btn rule">游戏规则</div>
<div class="right-btn award">我的奖品</div>
<div class="music-icon iconfont icon-yinfu" v-show="backMusicFlag"></div>
<div class="integral" v-if="ruleData[0].type == 0">{{ ruleData[0].val }}积分/次</div>
<div class="swiper-item" v-if="bulletFlag == 1">
<img :src="bulletImageUrl" />
<span>恭喜 张* 获得 100元优惠券</span>
</div>
<div class="box-box">
<img v-for="item in boxList" :key="item" :src="boxImg" :style="{ left: item.left, top: item.top }" @click="showOpenBoxDialog = true" />
</div>
<div class="points-count-box">
<div class="text-box">
<div class="text">1500</div>
<img class="text-shadow" src="https://pic01-10001430.cos.ap-shanghai.myqcloud.com/game/template1/circle_bg.png" />
<img class="title-image" src="https://pic01-10001430.cos.ap-shanghai.myqcloud.com/game/template1/我的积分.png" />
</div>
<div class="game-name-box">
<img :src="noticeImageUrl" style="width: 224px;height: 22px;" />
</div>
<div class="text-box">
<div class="text">55</div>
<img class="text-shadow" src="https://pic01-10001430.cos.ap-shanghai.myqcloud.com/game/template1/circle_bg.png" />
<img class="title-image" src="https://pic01-10001430.cos.ap-shanghai.myqcloud.com/game/template1/剩余次数.png" />
</div>
</div>
<img class="ad-image" :src="adsImageUrl" mode="widthFix" v-if="adsImageUrl && adsFlag == 1" />
<!-- <div class="share-box" v-if="ruleData[1].type == 1">
<div class="icon">
<div class="iconfont icon-yaoqing"></div>
</div>
<img src="https://pic01-10001430.cos.ap-shanghai.myqcloud.com/game/template1/邀请助力.png" />
</div> -->
</div>
</div>
</template>
<script>
import img from '../../../components/upload/img.vue';
const box6 = [
{
left: '24%',
top: '202px'
},
{
left: '50%',
top: '266px'
},
{
left: '77%',
top: '202px'
},
{
left: '24%',
top: '338px'
},
{
left: '50%',
top: '393px'
},
{
left: '77%',
top: '338px'
}
];
const box9 = [
{
left: 'calc(51% - 0px)',
top: '204px'
},
{
left: 'calc(33% - 0px)',
top: '252px'
},
{
left: 'calc(66% - 0px)',
top: '248px'
},
{
left: 'calc(16% - 0px)',
top: '297px'
},
{
left: 'calc(52% - 0px)',
top: '296px'
},
{
left: 'calc(84% - 0px)',
top: '297px'
},
{
left: 'calc(35% - 0px)',
top: '341px'
},
{
left: 'calc(68% - 0px)',
top: '345px'
},
{
left: 'calc(53% - 0px)',
top: '390px'
}
];
export default {
components: { img },
data() {
return {
list: ['刷卡拉拉卡', '阿拉卡萨卢克和史莱克', '刷卡拉拉卡', '阿拉卡萨卢克和史莱克'],
boxList: box9,
backImageUrl: '',
noticeImageUrl: '',
boxImg: '',
adsImageUrl: '',
bulletFlag: 0,
bulletImageUrl: '',
backMusicFlag: 0,
adsFlag: 0
};
},
props: {
ruleData: {
type: Object,
default: () => {}
},
basicData: {
type: Object,
default: () => {}
},
templateId: String
},
watch: {
templateId(val) {
if (val == 1 || val == 2) {
this.boxList = box9;
}
if (val == 3) {
this.boxList = box6;
}
},
basicData: {
deep: true,
// immediate: true,
handler(val) {
console.log('basicData----->');
console.log(val);
if (!val) return;
const { backImageUrl, noticeImageUrl, boxImg, adsImageUrl, bulletFlag, bulletImageUrl, backMusicFlag, adsFlag } = val;
this.backImageUrl = backImageUrl;
this.noticeImageUrl = noticeImageUrl;
this.boxImg = boxImg;
this.adsImageUrl = adsImageUrl;
this.bulletFlag = bulletFlag;
this.bulletImageUrl = bulletImageUrl;
this.backMusicFlag = backMusicFlag;
this.adsFlag = adsFlag;
console.log(this.boxImg);
}
}
}
};
</script>
<style lang="scss" scoped>
img {
object-fit: fill;
}
.template-game {
height: 724px;
margin-top: 70px;
}
.bg-image {
width: 375px;
position: absolute;
height: 724px;
overflow-y: auto;
}
.content {
position: relative;
z-index: 1;
}
.music-icon {
text-align: center;
width: 30px;
height: 30px;
line-height: 30px;
border-radius: 50%;
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
background: rgba(39, 41, 43, 0.5);
color: white;
font-size: 20px;
/* transform: rotate(90deg); */
}
.right-btn {
writing-mode: tb-rl;
position: absolute;
right: 0;
top: 20px;
padding: 7px 5px;
border-radius: 8px 0 0 8px;
background: rgba(39, 41, 43, 0.5);
z-index: 10;
color: white;
font-size: 11px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
}
.right-btn.award {
top: 89px;
}
.integral {
position: absolute;
z-index: 2;
top: 98px;
left: 50%;
transform: translateX(-50%);
min-width: 120px;
height: 24px;
line-height: 24px;
text-align: center;
padding: 0 10px;
box-sizing: border-box;
border-radius: 8px;
border: 1px solid white;
background: rgba(255, 255, 255, 0.3);
color: #27292b;
font-size: 12px;
}
.swiper-item {
width: 300px;
height: 36px;
box-sizing: border-box;
border-radius: 17px 0 0 17px;
display: flex;
align-items: center;
position: absolute;
top: 129px;
left: 19px;
}
.swiper-item img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.swiper-item span {
padding-left: 30px;
color: white;
font-size: 12px;
z-index: 2;
}
.box-box {
height: 501px;
width: 100%;
position: relative;
}
@keyframes box-animation {
from {
transform: translate(-50%, -50%);
}
to {
transform: translate(-50%, 0);
}
}
.box-box img {
position: absolute;
transform: translate(-50%, 0);
width: 95px;
height: 95px;
animation-timing-function: ease-in;
/* animation-name: box-animation; */
animation-duration: 1s;
}
.box-box img:nth-child(1) {
animation-delay: 0s;
}
.box-box img:nth-child(2) {
animation-delay: 0.1s;
}
.box-box img:nth-child(3) {
animation-delay: 0.2s;
}
.box-box img:nth-child(4) {
animation-delay: 0.3s;
}
.box-box img:nth-child(5) {
animation-delay: 0.4s;
}
.box-box img:nth-child(6) {
animation-delay: 0.5s;
}
.box-box img:nth-child(7) {
animation-delay: 0.6s;
}
.box-box img:nth-child(8) {
animation-delay: 0.7s;
}
.box-box img:nth-child(9) {
animation-delay: 0.8s;
}
.points-count-box {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 18px;
}
.text-box {
position: relative;
text-align: center;
}
.text-box .text {
font-family: DINAlternate-Bold, DINAlternate;
font-size: 20px;
font-weight: 600;
color: #388ccc;
line-height: 23px;
position: relative;
z-index: 2;
}
.text-box .text-shadow {
position: absolute;
left: 50%;
top: 17px;
transform: translateX(-50%);
width: 41px;
height: 8px;
z-index: 1;
}
.text-box .title-image {
padding-top: 5px;
width: 48px;
height: 16px;
}
.ad-image {
margin: 39px 15px 15px;
border-radius: 8px;
height: 75px;
width: calc(100% - 30px);
position: absolute;
}
.share-box {
position: absolute;
z-index: 10;
right: 20px;
bottom: -75px;
padding: 10px;
}
.share-box .icon {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: 50%;
background: #27292b;
box-shadow: inset 0px 3px 2px 0px rgba(243, 243, 243, 0.4);
}
.share-box .icon .iconfont {
background-image: -webkit-linear-gradient(180deg, #01ffde 0%, #01fff4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 18px;
}
.share-box img {
position: absolute;
left: 50%;
bottom: 0;
width: 48px;
height: 16px;
transform: translateX(-50%);
z-index: 2;
}
</style>
<template>
<div>
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!this.$route.meta.keepAlive" />
</div>
</template>
<script>
export default {};
</script>
<style></style>
<template>
<section class="static-detial" v-loading="loading">
<dm-sub-title title-align="space-between">
活动数据详情
</dm-sub-title>
<div class="tips mt5 mb20">数据每天更新 1 次</div>
<div class="pb20 clearfix">
<el-input v-model="form.search" class="w270" placeholder="请输入姓名/昵称/手机号/会员卡号" clearable @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-date-picker class="w256" v-model="form.dateTime" value-format="yyyy-MM-dd" @change="refresh" type="daterange" range-separator="~" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
<el-button icon="iconfont fz14 icon-cp-xiazai" type="primary" class="fr" @click="handleExport" size="small" :disabled="!total"> 查询结果导出</el-button>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width:100%">
<el-table-column label="基本信息" min-width="214" fixed="left">
<div slot-scope="{ row }" class="member-info">
<img class="member-logo" v-if="row.memberImage" :src="row.memberImage" alt="" />
<svg aria-hidden="true" v-else class="member-logo">
<use xlink:href="#icon-user-default"></use>
</svg>
<div>
<p class="member-name">
<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" v-if="row.phoneNum">{{ row.phoneNum }}</p>
</div>
</div>
</el-table-column>
<el-table-column label="会员卡号" min-width="130px" prop="cardNum" show-overflow-tooltip :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="服务门店" min-width="210" prop="mainStoreName">
<template slot-scope="{ row }">
{{ row.mainStoreName || '--' }}
<br />
{{ row.mainStoreCode || '--' }}
</template>
</el-table-column>
<el-table-column label="中奖次数" min-width="96" prop="winCnt" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="分享次数" min-width="96" prop="shareCnt" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="裂变新用户" min-width="112" prop="invitationMemberCnt" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="消耗积分" min-width="96" prop="useIntegralCnt" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column min-width="88" prop="isNewMember" :formatter="(row, col, val) => val || '--'">
<template slot="header">
新会员
<el-tooltip placement="top" content="用户自身是否通过本次游戏注册成为会员">
<i class="iconfont icon-QuestionCircleOutlined"></i>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="首次参与时间" min-width="114" prop="firstPlayTime" fixed="right">
<template slot-scope="{ row }">
{{ formatDateTimeByType(row.firstPlayTime, 'yyyy-MM-dd') || '--' }}
<br />
{{ formatDateTimeByType(row.firstPlayTime, 'HH-mm-ss') || '--' }}
</template>
</el-table-column>
</el-table>
<dm-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="form.pageNum" :page-sizes="[20, 40, 60, 80]" :page-size="form.pageSize" layout="total, sizes, prev, pager, next" :total="total"></dm-pagination>
<vue-gic-export-excel :dialogVisible.sync="dialogVisible" :dataArr="tableList" :type="2" :excelUrl="exportGameDetail" :params="params" projectName="marketing"></vue-gic-export-excel>
</section>
</template>
<script>
import { formatDateTimeByType } from '@/utils/index.js';
import { getGameDataDetail, exportGameDetail } from '@/service/api/gameApi.js';
export default {
data() {
return {
tableList: [],
form: {
pageNum: 1,
pageSize: 20,
gameId: this.$route.params.id,
dateTime: [],
search: ''
},
formatDateTimeByType,
total: 0,
dialogVisible: false,
exportGameDetail,
params: {},
loading: false
};
},
mounted() {
this.getTableList();
},
methods: {
handleExport() {
this.dialogVisible = true;
this.params = {
gameId: this.$route.params.id,
search: this.form.search
};
if (this.form.dateTime && this.form.dateTime.length) {
this.params.startTime = this.form.dateTime[0];
this.params.endTime = this.form.dateTime[1];
}
},
refresh() {
this.form.pageNum = 1;
this.getTableList();
},
handleSizeChange(val) {
this.form.pageSize = val;
this.getTableList();
},
handleCurrentChange(val) {
this.form.pageNum = val;
this.getTableList();
},
getTableList() {
const params = { ...this.form };
this.loading = true;
if (params.dateTime && params.dateTime.length) {
params.startTime = params.dateTime[0];
params.endTime = params.dateTime[1];
}
delete params.dateTime;
getGameDataDetail(params)
.then(res => {
const { result } = res;
this.tableList = result.result;
this.total = result.totalCount;
})
.finally(() => {
this.loading = false;
});
}
}
};
</script>
<style lang="scss" scoped>
.static-detial {
padding: 16px 20px;
}
.tips {
font-weight: 400;
color: #606266;
line-height: 17px;
}
.member-info {
display: flex;
justify-content: flex-start;
align-items: center;
.member-logo {
flex-shrink: 0;
margin-right: 10px;
width: 40px;
height: 40px;
border-radius: 40px;
overflow: hidden;
}
.member-name {
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 20px;
.member-subname {
color: #909399;
}
}
}
</style>
<template>
<section class="static-detial" v-loading="loading">
<dm-sub-title title-align="space-between">
奖品数据详情
</dm-sub-title>
<div class="tips mt5 mb20">数据实时更新</div>
<div class="pb20 clearfix">
<el-input v-model="form.prizeName" class="w260" placeholder="请输入奖品名称" clearable @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-input v-model="form.search" class="w270" placeholder="请输入姓名/昵称/手机号/会员卡号" clearable @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-date-picker class="w256" v-model="form.dateTime" value-format="yyyy-MM-dd" @change="refresh" type="daterange" range-separator="~" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
<el-select class="w160" v-model="form.prizeType" placeholder="所有奖品类型" @change="refresh" clearable>
<el-option v-for="(v, i) in prizeTypeOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-button icon="iconfont fz14 icon-cp-xiazai" type="primary" class="fr" @click="handleExport" size="small" :disabled="!total"> 查询结果导出</el-button>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width:100%">
<el-table-column label="奖品名称" min-width="204" prop="prizeName" fixed="left" show-overflow-tooltip :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="奖品类型" min-width="183" prop="prizeType" :formatter="(row, col, val) => prizeTypeObj[val]"></el-table-column>
<el-table-column label="中奖会员" min-width="214">
<div slot-scope="{ row }" class="member-info">
<img class="member-logo" v-if="row.memberImage" :src="row.memberImage" alt="" />
<svg aria-hidden="true" v-else class="member-logo">
<use xlink:href="#icon-user-default"></use>
</svg>
<div>
<p class="member-name">
<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" v-if="row.memberMobile">{{ row.memberMobile }}</p>
</div>
</div>
</el-table-column>
<el-table-column label="会员卡号" min-width="184" prop="memberCard" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="兑奖方式" min-width="184" prop="exchangeTypeName" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="中奖时间" min-width="183" prop="prizeTime" fixed="right">
<template slot-scope="{ row }">
{{ formatDateTimeByType(row.prizeTime, 'yyyy-MM-dd') || '--' }}
<br />
{{ formatDateTimeByType(row.prizeTime, 'HH-mm-ss') || '--' }}
</template>
</el-table-column>
</el-table>
<dm-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="form.pageNum" :page-sizes="[20, 40, 60, 80]" :page-size="form.pageSize" layout="total, sizes, prev, pager, next" :total="total"></dm-pagination>
<vue-gic-export-excel :dialogVisible.sync="dialogVisible" :dataArr="tableList" :type="2" :excelUrl="exportGamePrizeDetail" :params="params" projectName="marketing"></vue-gic-export-excel>
</section>
</template>
<script>
import { formatDateTimeByType } from '@/utils/index.js';
import { getPrizeDetail, exportGamePrizeDetail } from '@/service/api/gameApi.js';
export default {
data() {
return {
tableList: [],
form: {
prizeName: '',
pageNum: 1,
pageSize: 20,
gameId: this.$route.params.id,
dateTime: [],
search: '',
prizeType: ''
},
prizeTypeOptions: [
{
label: '积分',
value: 1
},
{
label: '卡券',
value: 2
}
],
prizeTypeObj: {
1: '积分',
2: '卡券'
},
formatDateTimeByType,
total: 0,
dialogVisible: false,
exportGamePrizeDetail,
params: {},
loading: false
};
},
mounted() {
this.getTableList();
},
methods: {
handleExport() {
this.dialogVisible = true;
const { search, prizeType, prizeName } = this.form;
this.params = {
gameId: this.$route.params.id,
search,
prizeType,
prizeName
};
if (this.form.dateTime && this.form.dateTime.length) {
this.params.startTime = this.form.dateTime[0];
this.params.endTime = this.form.dateTime[1];
}
},
refresh() {
this.form.pageNum = 1;
this.getTableList();
},
handleSizeChange(val) {
this.form.pageSize = val;
this.getTableList();
},
handleCurrentChange(val) {
this.form.pageNum = val;
this.getTableList();
},
getTableList() {
const params = { ...this.form };
this.loading = true;
if (params.dateTime && params.dateTime.length) {
params.startTime = params.dateTime[0];
params.endTime = params.dateTime[1];
}
delete params.dateTime;
getPrizeDetail(params)
.then(res => {
const { result } = res;
this.tableList = result.result;
this.total = result.totalCount;
})
.finally(() => {
this.loading = false;
});
}
}
};
</script>
<style lang="scss" scoped>
.static-detial {
padding: 16px 20px;
}
.tips {
font-weight: 400;
color: #606266;
line-height: 17px;
}
.member-info {
display: flex;
justify-content: flex-start;
align-items: center;
.member-logo {
flex-shrink: 0;
margin-right: 10px;
width: 40px;
height: 40px;
border-radius: 40px;
overflow: hidden;
}
.member-name {
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 20px;
.member-subname {
color: #909399;
}
}
}
</style>
<template>
<div>
<label class="el-button el-button--primary" :class="{ 'is-disabled': disabled }">
{{ disabled ? '上传中...' : label }}
<label :class="[textType ? 'el-button el-button--text el-button--small' : 'el-button el-button--primary', disabled ? 'is-disabled' : '']">
<span style="font-weight: 400;"> {{ disabled ? '上传中...' : label }}</span>
<input type="file" style="display:none;" :disabled="disabled" accept="audio/mpeg" ref="uploader" v-audioUpload="this" />
</label>
<p class="fz12 gray">{{ tips }}</p>
......@@ -29,6 +29,10 @@ export default {
url: {
type: String,
default: '/api-marketing/upload-game-music'
},
textType: {
type: Boolean,
default: false
}
},
data() {
......
<template>
<el-dialog title="游戏链接下载" :visible.sync="show" width="600px" :before-close="close">
<el-dialog title="游戏预览" :visible.sync="show" width="600px" :before-close="close" :show-close="showClose">
<div class="link-tip">
<i class="el-icon-info"></i>
非会员进入游戏页面将提示进行认证注册成为会员,认证成功后自动返回游戏页面
</div>
<div class="link-title">
<i class="icon iconfont icon-xiaochengxu4" style="color:#6457AD"></i>
小程序链接
</div>
<div v-loading="loading" class="links-block">
<div class="links__body">
<div class="links__body--url">{{ miniprogram.link }}</div>
<div class="links__body--btn">
<el-button v-clipboard:text="miniprogram.link" type="text" icon="iconfont icon-lianjie fz14"> 复制链接</el-button>
<el-button v-clipboard:text="miniprogram.link" v-show="miniprogram.link" type="text" icon="iconfont icon-lianjie fz14"> 复制链接</el-button>
</div>
</div>
<div class="link__divider"></div>
<div class="links__qr">
<!-- <vue-qr id="qrWrap" :text="qcText" :size="102" :margin="0" :logoMargin="10"></vue-qr> -->
<div style="text-align:center">
<el-image style="width:102px;height:102px;border:4px solid #fff;cursor: pointer;" lazy :src="miniprogram.url" @click.native="onView(false)">
<!-- <img slot="placeholder" style="width:102px;height:102px" src="@/assets/img/loaderror.png" /> -->
<div slot="placeholder" style="display:flex;align-items:center;justify-content:center;width:100%;height:100%;">
<i class="el-icon-picture-outline" style="font-size:30px"></i>
</div>
</el-image>
</div>
<el-button class="links__qr--btn" type="text" icon="iconfont icon-icon_yunxiazai fz14 mr4" @click="downloadMiniporgramImg">小程序二维码下载</el-button>
<el-button class="links__qr--btn" v-show="miniprogram.url" type="text" icon="iconfont icon-icon_yunxiazai fz14 mr4" @click="downloadMiniporgramImg">小程序二维码下载</el-button>
</div>
</div>
<!-- <div class="link-title">
<i class="icon iconfont icon-fuwuhao2" style="color:#2CBB64"></i>
H5链接
<div slot="footer" v-if="showFooter">
<slot></slot>
</div>
<div class="links-block" style="margin-bottom:25px">
<div class="links__body">
<div class="links__body--url">{{ obj.gameUrl }}</div>
<div class="links__body--btn">
<el-button v-clipboard:text="obj.gameUrl" type="text" icon="iconfont icon-lianjie fz14"> 复制链接</el-button>
</div>
</div>
<div class="link__divider"></div>
<div class="links__qr">
<vue-qr id="qrWrap" :text="qcText" :size="510" :logoScale="5" :margin="0" :logoMargin="10" @click.native="onView(true)"></vue-qr>
<el-button class="links__qr--btn" type="text" icon="iconfont icon-icon_yunxiazai fz14 mr4" @click="downloadImg">服务号二维码下载</el-button>
</div>
</div> -->
</el-dialog>
</template>
<script>
import VueQr from 'vue-qr';
import { formatDateTimeByType, downloadFile } from '@/utils/index.js';
import { generateMiniProgramLink } from '@/service/api/gameApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
import { generateMiniProgramLink, getQrGame } from '@/service/api/gameApi.js';
import { api as viewerApi } from 'v-viewer';
import 'viewerjs/dist/viewer.css';
export default {
......@@ -73,6 +53,18 @@ export default {
gameTypeName: {
type: String,
default: ''
},
showFooter: {
type: Boolean,
default: false
},
isNew: {
type: Boolean,
default: false
},
showClose: {
type: Boolean,
default: true
}
},
data() {
......@@ -93,7 +85,11 @@ export default {
url: ''
};
this.createQrImg();
this.generateMiniProgramLink();
if (this.isNew) {
this.getQrGame();
} else {
this.generateMiniProgramLink();
}
}
}
},
......@@ -120,23 +116,35 @@ export default {
case '口令福利':
gameType = 'GAME_LINK_KLFL';
break;
case '拆盲盒':
gameType = 'GAME_LINK_CMH';
break;
}
generateMiniProgramLink({
id: this.obj.gameId || this.obj.gameActivityId,
linkId: gameType
}).then(res => {
this.miniprogram = res.result;
this.loading = false;
});
})
.then(res => {
this.miniprogram = res.result;
})
.finally(() => {
this.loading = false;
});
},
getQrGame() {
this.loading = true;
getQrGame({ id: this.obj.gameId })
.then(res => {
this.miniprogram = res.result;
})
.finally(() => {
this.loading = false;
});
},
close() {
this.$emit('update:show', false);
},
downloadImg() {
const qrWrap = document.getElementById('qrWrap');
const qrImg = qrWrap && qrWrap.childNodes[0].src;
downloadFile(this.gameTypeName + '-' + this.obj.gameName + '-服务号-' + formatDateTimeByType(this.obj.gameStartTime, 'yyyy-MM-dd-HH-mm-ss') || '游戏链接', qrImg);
},
onView(isH5) {
let qrImg = '';
if (isH5) {
......@@ -185,7 +193,7 @@ export default {
overflow-x: hidden;
overflow-y: auto;
word-break: break-all;
padding: 13px 22px 0 16px;
padding: 13px 16px 0 16px;
line-height: 17px;
font-size: 12px;
}
......@@ -209,7 +217,7 @@ export default {
}
.links-block {
display: flex;
background: #f5f7fa;
background: #f7f8fa;
margin: 10px 0 10px;
}
.link-title {
......@@ -225,21 +233,20 @@ export default {
}
}
.link-tip {
width: 544px;
background: #e6f7ff;
border-radius: 4px;
border: 1px solid #91d5ff;
font-size: 13px;
color: #606266;
line-height: 1.2;
padding: 10px 0;
margin-bottom: 10px;
width: 552px;
background: #fcf6f1;
border-radius: 2px;
font-size: 12px;
color: #303133;
line-height: 22px;
padding: 5px 0;
margin-bottom: 16px;
display: flex;
.el-icon-info {
line-height: 18px;
margin-left: 16px;
margin-right: 4px;
color: #2f54eb;
line-height: 22px;
margin-left: 17px;
margin-right: 9px;
color: #fa8c16;
font-size: 12px;
}
}
......
......@@ -28,6 +28,9 @@ export default {
case 5:
this.gameTypeName = '口令福利';
break;
case 6:
this.gameTypeName = '拆盲盒';
break;
}
this.linksObj = row;
this.linkShow = true;
......
import Step from './step';
/* istanbul ignore next */
const GicStep = {
install(Vue) {
Vue.component(Step.name, Step);
}
};
if (typeof window.Vue !== 'undefined' && window.Vue) {
window.Vue.use(GicStep);
}
export default GicStep;
# 副标题
```
<h2>simple样式</h2>
<dm-steps :active="active" simple>
<dm-step title="步骤一" @click.native="active=0"></dm-step>
<dm-step title="步骤二" @click.native="active=1"></dm-step>
<dm-step title="步骤三" @click.native="active=2"></dm-step>
</dm-steps>
<h2>线性样式</h2>
<dm-steps :active="active">
<dm-step title="步骤一" ></dm-step>
<dm-step title="步骤二" ></dm-step>
<dm-step title="步骤三" ></dm-step>
```
### props
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 是否必传 |
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
| space | 每个 step 的间距,不填写将自适应间距。支持百分比 | [Number, String] | -- | -- | 否 |
| active | 设置当前激活步骤 | Number | -- | 0 | 否 |
| simple | 样式 | Boolean | | false| 否 |
### 更新日志:
1.1.0 重写完成 by黄冷
\ No newline at end of file
<template>
<div class="steps" v-if="$parent.simple">
<div :class="['step-item', $parent.active === index ? 'active' : '']" :style="canClick ? 'cursor: pointer;' : ''">
<slot name="title"> {{ title }} </slot>
</div>
<span class="triangle" :class="[$parent.active === index ? 'triangle-active' : '', $parent.active == index + 1 ? 'triangle-bg' : 'triangle-default']"></span>
</div>
<div v-else class="dm-step" :style="style" :class="[isLast && !space && 'is-flex']">
<div class="dm-step-box" :class="`is-${currentStatus}`">
<!-- 线 -->
<div class="dm-step__line" :style="isLast ? '' : { marginRight: $parent.stepOffset + 'px' }">
<i class="dm-step__line-inner" :style="lineStyle"></i>
</div>
<div class="dm-step__head">
<!-- icon -->
<div class="dm-step__icon">
<slot v-if="currentStatus !== 'success' && currentStatus !== 'error'" name="icon">
<i v-if="icon" class="dm-step__icon-inner" :class="[icon]"></i>
<div class="dm-step__icon-inner" v-if="!icon">{{ index + 1 }}</div>
</slot>
<i v-else :class="['el-icon-' + (currentStatus === 'success' ? 'check' : 'close')]" class="dm-step__icon-inner is-status"> </i>
</div>
<!-- 内容区 -->
<div class="dm-step__content" :class="`is-${currentStatus}`">
<slot name="title">{{ title }}</slot>
</div>
</div>
</div>
<!-- 文字描述 -->
<div class="dm-step__description" :class="`is-${currentStatus}`">
<slot name="description">{{ description }}</slot>
</div>
</div>
</template>
<script>
export default {
name: 'vue-gic-step',
props: {
title: String,
icon: String,
description: String,
status: String,
canClick: {
type: Boolean,
default: false
}
},
data() {
return {
index: -1,
lineStyle: {},
internalStatus: ''
};
},
beforeCreate() {
this.$parent.steps.push(this);
},
beforeDestroy() {
const steps = this.$parent.steps;
const index = steps.indexOf(this);
if (index >= 0) {
steps.splice(index, 1);
}
},
computed: {
currentStatus() {
return this.status || this.internalStatus;
},
prevStatus() {
const prevStep = this.$parent.steps[this.index - 1];
return prevStep ? prevStep.currentStatus : 'wait';
},
isLast() {
const parent = this.$parent;
return parent.steps[parent.steps.length - 1] === this;
},
space() {
const {
$parent: { space }
} = this;
return space;
},
style: function() {
const style = {};
const parent = this.$parent;
const len = parent.steps.length;
const space = typeof this.space === 'number' ? this.space + 'px' : this.space ? this.space : 100 / (len - 1) + '%';
style.flexBasis = space;
return style;
}
},
methods: {
updateStatus(val) {
const prevChild = this.$parent.$children[this.index - 1];
if (val > this.index) {
this.internalStatus = this.$parent.finishStatus;
} else if (val === this.index && this.prevStatus !== 'error') {
this.internalStatus = this.$parent.processStatus;
} else {
this.internalStatus = 'wait';
}
if (prevChild) prevChild.calcProgress(this.internalStatus);
},
calcProgress(status) {
let step = 100;
const style = {};
style.transitionDelay = 150 * this.index + 'ms';
if (status === 'wait') {
step = 0;
style.transitionDelay = -150 * this.index + 'ms';
}
style.borderWidth = step ? '1px' : 0;
style.width = step + '%';
this.lineStyle = style;
}
},
mounted() {
const unwatch = this.$watch('index', val => {
this.$watch('$parent.active', this.updateStatus, { immediate: true });
this.$watch(
'$parent.processStatus',
() => {
const activeIndex = this.$parent.active;
this.updateStatus(activeIndex);
},
{ immediate: true }
);
unwatch();
});
}
};
</script>
<style lang="scss">
.dm-step {
position: relative;
flex-shrink: 1;
&:last-of-type.is-flex {
flex-basis: 80px !important;
flex-shrink: 1;
flex-grow: 0;
.dm-step__content {
flex: 1;
}
}
.dm-step-box {
position: relative;
width: 100%;
&.is-process {
color: #fff;
border-color: #2f54eb;
.dm-step__icon {
background-color: #2f54eb;
}
}
&.is-finish,
&.is-success {
color: #2f54eb;
border-color: #2f54eb;
}
&.is-wait {
color: #c0c4cc;
border-color: #c0c4cc;
}
}
.dm-step__line {
position: absolute;
height: 1px;
top: 11px;
left: 0;
right: 0;
border-color: inherit;
background-color: #dcdfe6;
}
.dm-step__line-inner {
display: block;
border-top: 1px solid;
border-color: inherit;
transition: 0.15s ease-out;
box-sizing: border-box;
width: 0;
height: 0;
}
.dm-step__head {
display: flex;
width: 100%;
border-color: inherit;
}
.dm-step__icon {
position: relative;
z-index: 1;
display: inline-flex;
justify-content: center;
align-items: center;
width: 24px;
height: 24px;
font-size: 14px;
box-sizing: border-box;
background-color: #fff;
transition: 0.15s ease-out;
border-radius: 50%;
border: 1px solid;
border-color: inherit;
&-inner {
display: inline-block;
user-select: none;
text-align: center;
line-height: 1;
color: inherit;
&.is-status {
font-size: 16px;
transform: translateY(1px);
}
}
}
.dm-step__content {
position: relative;
z-index: 2;
line-height: 25px;
padding-left: 8px;
padding-right: 18px;
background-color: #fff;
font-size: 16px;
&.is-process {
color: #303133;
font-weight: 500;
}
&.is-finish,
&.is-success {
color: #303133;
}
&.is-wait {
color: #909399;
}
}
.dm-step__description {
padding-left: 32px;
line-height: 24px;
color: #909399;
&.is-process {
color: #606266;
}
}
}
.steps {
display: flex;
width: 100%;
position: relative;
// margin-right: 10px;
.active {
background: #2f54eb;
color: #fff;
}
}
.step-item {
flex: 1;
background: #eef4ff;
color: #303133;
height: 44px;
text-align: center;
line-height: 44px;
font-size: 14px;
}
.triangle {
width: 0;
height: 0;
display: inline-block;
z-index: 2;
border-style: solid;
border-width: 22px 0 22px 12px;
position: relative;
background: #eef4ff;
border-color: transparent transparent transparent #eef4ff;
}
.steps:last-of-type {
.triangle {
background: #fff;
}
}
.triangle::before {
content: '';
position: absolute;
height: 28px;
width: 4px;
background: #fff;
transform: rotate(-209deg) translateY(-0.7px);
transform-origin: 0 0;
top: 0;
right: -6px;
}
.triangle::after {
content: '';
bottom: 0;
position: absolute;
height: 27px;
width: 4px;
background: #fff;
transform: rotate(209deg) translateY(1.3px);
transform-origin: 100% 100%;
right: 1.8px;
}
.triangle-active {
border-left-color: #2f54eb;
}
.triangle-bg {
background: #2f54eb;
}
.triangle-default {
background: #eef4ff;
}
</style>
import Steps from './steps';
/* istanbul ignore next */
const GicSteps = {
install(Vue) {
Vue.component(Steps.name, Steps);
}
};
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(GicSteps);
}
export default GicSteps;
<template>
<div class="dm-steps">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'vue-gic-steps',
props: {
space: [Number, String], // 间距
active: {
// 设置步骤
type: Number,
default: 0
},
finishStatus: {
// 成功状态
type: String,
default: 'success'
},
processStatus: {
// 当前步骤状态
type: String,
default: 'process'
},
simple: {
type: Boolean,
default: false
}
},
data() {
return {
steps: [],
stepOffset: 20
};
},
watch: {
active(newVal, oldVal) {
this.$emit('change', newVal, oldVal);
},
steps(steps) {
steps.forEach((child, index) => {
child.index = index;
});
}
}
};
</script>
<style lang="scss">
.dm-steps {
display: flex;
white-space: nowrap;
background-color: #fff;
}
</style>
......@@ -120,7 +120,7 @@ import { numberToChinese, formatDateTimeByType } from '@/utils/index.js';
import dmUploadAvatar from '@/components/upload/avatar';
import editAlertMixin from '../common/editAlertMixin.js';
import ptyxSkin from './partials/ptyx-skin.vue';
import audioUpload from './partials/audio-upload.vue';
import audioUpload from '../common/audio-upload.vue';
import dmShare from '../common/share.vue';
import dmThreshold from '../common/threshold.vue';
let gameInfo = {};
......
......@@ -140,6 +140,12 @@ export default {
created() {
this.getTableList();
this.getQuantity();
document.addEventListener('scroll', () => {
console.log('--->scroll');
// console.log(document.body.scrollHeight);
// console.log(window.screen.height);
// console.log(document.body.scrollTop);
});
},
methods: {
formatDateTimeByType,
......
......@@ -45,7 +45,7 @@
<el-col :span="6" style="padding: 0 2px">
<div class="recharge-today-item border2">
<div class="text-center">
<div class="icon-box">
<div class="box-icon">
<i class="iconfont icon-wenbenmsg fz36"></i>
</div>
</div>
......@@ -68,7 +68,7 @@
<el-col :span="6" style="padding: 0 2px">
<div class="recharge-today-item border2">
<div class="text-center">
<div class="icon-box color2">
<div class="box-icon color2">
<i class="iconfont icon-duanxinyzm fz28"></i>
</div>
</div>
......@@ -91,7 +91,7 @@
<el-col :span="6" style="padding: 0 2px">
<div class="recharge-today-item border2">
<div class="text-center">
<div class="icon-box color3">
<div class="box-icon color3">
<i class="iconfont icon-shuangxianghujiao1 fz36"></i>
</div>
</div>
......@@ -114,7 +114,7 @@
<el-col :span="6" style="padding: 0 2px">
<div class="recharge-today-item border2">
<div class="text-center">
<div class="icon-box color4">
<div class="box-icon color4">
<i class="iconfont icon-AIdianhua fz36"></i>
</div>
</div>
......@@ -134,6 +134,29 @@
</div>
</div>
</el-col>
<el-col :span="6" style="padding: 0 2px">
<div class="recharge-today-item border2">
<div class="text-center">
<div class="box-icon color3">
<i class="iconfont icon-xitongtongzhi fz36"></i>
</div>
</div>
<div class="consume-info">
<div class="consume-item">
<p class="consume-item-label">系统通知</p>
<p class="consume-item-value">
<span class="value-content">{{ recharge.sysSmsCount || 0 }}</span>
</p>
</div>
<div class="consume-item">
<p class="consume-item-label">合计费用</p>
<p class="consume-item-value">
<span class="value-content">{{ (recharge.sysSmsFee / 1000) | amount }}</span>
</p>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
<div class="dm-wrap" style="padding-top: 0">
......@@ -325,7 +348,8 @@ export default {
{ name: '短信营销', type: 'marketing', fee: result.messageFee, count: result.messageCount },
{ name: '短信验证码', type: 'sms', fee: result.smsFee, count: result.smsCount },
{ name: '双向呼叫', type: 'call', fee: result.callFee, count: result.callTime },
{ name: 'AI电话', type: 'ai-call', fee: result.aiFee, count: result.aiCount }
{ name: 'AI电话', type: 'ai-call', fee: result.aiFee, count: result.aiCount },
{ name: '系统通知', type: 'sys-sms', fee: result.sysSmsFee, count: result.sysSmsCount }
];
} else {
this.recharge = res.result;
......@@ -488,7 +512,7 @@ export default {
}
}
}
.icon-box {
.box-icon {
width: 60px;
height: 60px;
background: rgba(253, 107, 56, 0.15);
......
......@@ -87,6 +87,15 @@
<el-radio :label="3">活动创建人</el-radio>
</el-radio-group>
</el-form-item>
<h2 class="icon-type-title mb20">
<div class="mark" />
系统通知
</h2>
<el-form-item label="系统通知:" class="mb20">
<el-radio-group v-model="num">
<el-radio :label="1">活动创建人</el-radio>
</el-radio-group>
</el-form-item>
</template>
</el-form>
<div class="footer">
......@@ -123,6 +132,7 @@ export default {
aiRule: 3 // AI营销
},
isMoreAccount: false,
num: 1,
rules: {
smsPlanRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [2, 3]) }],
smsEcmRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [2, 3]) }],
......
......@@ -118,29 +118,6 @@
</template>
</el-table-column>
</el-table>
<!-- 语音验证码 -->
<!-- <el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'voice'">
<el-table-column align="left" prop="createTime" label="发送时间" width="170px">
<template slot-scope="scope">
<p class="cell-time">
{{ formatDateTimeByType(scope.row.createTime, 'yyyy-MM-dd-HH-mm-ss', true).y }}<br />
<span>{{ formatDateTimeByType(scope.row.createTime, 'yyyy-MM-dd-HH-mm-ss', true).h }}</span>
</p>
</template>
</el-table-column>
<el-table-column align="left" prop="receivePhone" label="接收号码"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="left" width="320" prop="storeName" label="门店">
<template slot-scope="scope">
<p>{{ scope.row.storeName }}</p>
<p class="fz13 gray">{{ scope.row.storeGroupName }}</p>
</template>
</el-table-column>
<el-table-column label="状态" align="left" prop="status">
<template slot-scope="scope">
{{ scope.row.status === 1 ? '成功' : '失败' }}
</template>
</el-table-column>
</el-table> -->
<!-- 双向呼叫 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'call'">
<el-table-column align="left" min-width="110" prop="createTime" label="呼叫时间">
......@@ -286,6 +263,40 @@
<el-table-column label="时间(秒)" prop="aiDurationSec" min-width="90" :formatter="(row, col, val) => val || '--'"></el-table-column>
<el-table-column label="消费金额(元)" prop="aiFeeYuan" min-width="90" :formatter="(row, col, val) => val || '--'"></el-table-column>
</el-table>
<!-- 系统通知 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'sys-sms'">
<el-table-column align="left" min-width="90" prop="sendTime" label="发送时间">
<template slot-scope="scope">
<p class="cell-time">
{{ formatDateTimeByType(scope.row.sendTime, 'yyyy-MM-dd-HH-mm-ss', true).y }}<br />
<span>{{ formatDateTimeByType(scope.row.sendTime, 'yyyy-MM-dd-HH-mm-ss', true).h }}</span>
</p>
</template>
</el-table-column>
<el-table-column align="left" min-width="100" prop="telephone" label="接收号码"></el-table-column>
<el-table-column align="left" min-width="100" label="通知方式">
<template>
短信
</template>
</el-table-column>
<el-table-column align="left" min-width="100" prop="telephone" label="通知类型">
<template>
游戏
</template>
</el-table-column>
<el-table-column align="left" min-width="100" prop="sourceName" label="活动名称"></el-table-column>
<el-table-column align="left" min-width="100" prop="sourceCreatorName" label="活动创建人"></el-table-column>
<template v-if="isMoreAccount">
<el-table-column label="扣费规则" prop="accountRule" min-width="120">
<template slot-scope="{ row }">{{ accountRule(row.accountRule) }}</template>
</el-table-column>
<el-table-column label="费用归属部门" prop="accountDepartName" min-width="120" :formatter="(row, col, val) => val || '--'"></el-table-column>
</template>
<el-table-column align="left" min-width="100" prop="countNum" label="计费条数"></el-table-column>
<el-table-column align="left" min-width="100" prop="fee" label="消费金额(元)">
<template slot-scope="scope">{{ Number(scope.row.fee / 1000).toFixed(2) }}</template>
</el-table-column>
</el-table>
<!-- 视频资费 -->
<!-- <el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'video'">
<el-table-column align="left" prop="createTime" label="时间">
......@@ -524,27 +535,38 @@ export default {
this.placeholder = '请输入手机号/活动名称/活动创建人';
this.aiCallData();
}
if (this.$route.params.type == 'sys-sms') {
this.placeholder = '请输入手机号/活动名称/活动创建人';
if (!onlyList) {
this.marketingCharts(102);
}
this.marketingList(102);
}
},
onSearch() {
this.listParams.currentPage = 1;
this.loadAll();
},
async marketingList() {
async marketingList(source) {
this.loading = true;
try {
let resList = await messageMarketingPage(this.listParams);
const data = source ? Object.assign(this.listParams, { source }) : this.listParams;
let resList = await messageMarketingPage(data);
if (resList.errorCode === 0 && resList.result.result) {
this.tableList = resList.result.result || [];
this.total = resList.result.totalCount;
} else {
this.tableList = [];
}
console.log(this.tableList);
} catch (err) {}
this.loading = false;
},
async marketingCharts() {
async marketingCharts(source) {
try {
let res = await messageMarketingChart(this.listParams);
console.log(source);
const data = source ? Object.assign(this.listParams, { source }) : this.listParams;
let res = await messageMarketingChart(data);
this.sumCount = res.result.sumCount;
this.sumFee = res.result.sumFee;
this.sumInternational = res.result.sumInternational;
......@@ -764,6 +786,9 @@ export default {
case 'ai-call':
meth = '/api-marketing/excel/recharge-ai-excel';
break;
case 'sys-sms':
meth = '/api-marketing/excel/recharge-sms-marketing-excel';
break;
}
const params = {
requestProject: 'marketing',
......@@ -774,6 +799,7 @@ export default {
accountDepartId: this.deparment.departId
};
if (type == 'sms') params.channelType = this.listParams.channelType;
if (type == 'sys-sms') params.source = 102;
exportData.excelUrl = meth;
exportData.params = params;
},
......
......@@ -111,7 +111,7 @@
.dm-pagination {
text-align: right;
margin: 24px 0 10px 0;
margin: 20px 0 10px 0;
}
.text-left {
text-align: left;
......
......@@ -72,10 +72,17 @@ export default {
cardLimitType: {
type: Number,
default: 3
},
cardLimit: {
type: Number,
default: 0
}
},
computed: {
limitTips() {
if (this.cardLimit == 1) {
return '该选择器可筛选所有限制领取张数>1的卡券,共';
}
if (this.cardLimitType === 2) {
return '领取限制领取1~100的卡券,系统已过滤,符合条件共';
} else if (this.cardLimitType === 3) {
......@@ -102,8 +109,9 @@ export default {
pageSize: 5,
cardTypes: '', // 0:抵金券,1:折扣券,2:兑换券
requestProject: 'gic-web',
cardLimitType: 3,
cardType: ''
cardLimitType: this.cardLimitType,
cardType: '',
cardLimit: this.cardLimit
},
total: 0,
tableList: [],
......
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
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