Commit a02200c9 by zhangmeng

Merge branch 'dd2' into dev

* dd2:
  营销事件组件
  提交迭代
  营销事件组件
  营销事件组件
parents b01936ad a3da492c
......@@ -3979,6 +3979,7 @@
color: #606266;
font-size: 14px; }
.el-dialog__footer {
border-top:1px solid #DCDFE6;
padding: 20px;
padding-top: 10px;
text-align: right;
......@@ -25,7 +25,7 @@
</template>
<script>
import imgItemRadio from './imgItemRadio'
import {loadImgList,updateGroupName,addGroupService,deleteGroupService,deleteImageService,changeGroupService} from '@/service/api/wechatApi.js';
import {loadImgList,addGroupService} from '@/service/api/wechatApi.js';
export default {
name:'dm-ilib',
props:{
......
......@@ -5,8 +5,9 @@
<a @click="addLinkDialogShow" class="iconfont icon-lianjie" title="添加链接"></a>
<a @click="removeLink" class="iconfont icon-duankailianjie" :class="{'no-link':!cancelLink}" title="取消链接"></a>
</div>
<div class="text-content">
<div @click="selectLeft" v-html="textarea" class="my-textarea" @blur="getAllMytext" ref="myTextarea" contentEditable=true></div>
<div class="text-content"> <!-- @keyup="textInput(textarea,$event)" -->
<div @click="selectLeft" @keyup="textInput(textarea,$event)" v-html="textarea" class="my-textarea" @blur="getAllMytext" ref="myTextarea" contentEditable=true></div>
<!-- <p class="text-tip">{{textNum}}/{{textLength}}</p> -->
</div>
</div>
<!-- 添加回复链接的dialog -->
......@@ -19,10 +20,10 @@
<div class="add-replay-content">
<el-form label-position="right" ref="form" :model="form" label-width="100px">
<el-form-item class="text-content" label="文本内容">
<dm-input v-model="form.text" @keyup.native="toInput(form.text,$event)" :maxlength="20"></dm-input>
<!-- <span class="text-num">{{inputNum}}/{{inputLength}}</span> -->
<el-input v-model="form.text" @keyup.native="toInput(form.text,$event)"></el-input>
<span class="text-num">{{inputNum}}/{{inputLength}}</span>
</el-form-item>
<el-form-item label="文本链接" v-show="false">
<el-form-item label="文本链接" v-show="false">
<el-radio-group v-model="form.linkStyle">
<el-radio label="0">网址</el-radio>
<el-radio label="1">小程序</el-radio>
......@@ -53,8 +54,8 @@
</div>
</el-form-item>
<el-form-item label="链接类型" style="margin-bottom: 0">
<el-select @change="changePreValue" class="w200" v-model="preValue" placeholder="请选择" style="margin-right: 6px;">
<el-form-item label="链接类型" class="url-link-wrap" style="margin-bottom: 0">
<el-select @change="changePreValue" v-model="preValue" placeholder="请选择" style="margin-right: 6px;">
<el-option
v-for="item in options1"
:key="item.value"
......@@ -62,7 +63,7 @@
:value="item.value">
</el-option>
</el-select>
<el-select v-if="preValue" class="w200" @change="changeNextValue" v-model="nextValue" placeholder="请选择">
<el-select v-if="preValue" @change="changeNextValue" v-model="nextValue" placeholder="请选择">
<el-option
v-for="item in options2"
:key="item.linkId"
......@@ -135,6 +136,9 @@
preValue: '',
nextValue:'',
textNum:0,
textLength:10
}
},
watch:{
......@@ -176,20 +180,28 @@
if(reg.exec(this.textarea)!=null){
this.cancelLink=true;
}
},
},
textInput(v,e){ // 控制微信文本的文字
var textContent = this.$refs.myTextarea.innerText;
textContent = strLength.getByteVal2(e.target.innerText);
this.textNum = strLength.getZhLen(textContent);
if(this.textNum>=300){
this.$message.error('最多300个字符哦');
}
},
toInput(value,e) { // 控制字符数量
var that = this;
that.form.text = strLength.getByteVal(e.target.value,that.inputLength);
that.inputNum = strLength.getZhLen(that.form.text);
},
beforeAvatarUpload(file) {
console.log(file)
// console.log(file)
let fd = new FormData();
fd.append('file', file);
fd.append('requestProject', 'gic-web');
this.axios.post('/api-admin/marketing-wechat-image-save', fd).then((res)=> {//成功后回调
var data = res.data;
console.log(data);
// console.log(data);
if(data.errorCode==0){
this.imageUrl = data.result.qcloudImageUrl;
this.imageMediaId = data.result.imageMediaId;
......@@ -239,6 +251,11 @@
this.cancelLink=false;
this.textarea = this.$refs.myTextarea.innerText;
this.xcxUrlObj.textarea = this.textarea;
var textContent = strLength.getByteVal2(this.textarea);
this.textNum = strLength.getZhLen(textContent);
this.xcxUrlObj.textNum = this.textNum;
if(this.form.linkStyle==0){ // 文本链接
this.xcxUrlObj.imageMediaId = ''
} else if(this.form.linkStyle==1){ // 小程序链接
......@@ -246,11 +263,17 @@
}
this.$emit('listenWxText',this.xcxUrlObj)
},
getAllMytext(){ // 文本框输入失去焦点时获得输入框的内容
console.log(this.$refs.myTextarea.innerHTML);
this.xcxUrlObj.textarea = this.$refs.myTextarea.innerHTML.replace(/<\/div>|&nbsp;/g,'').replace(/<div>/g,'<br>').split('<br>').filter(v => v).join('<br>').replace(/<br>/g,'\n').replace('undefined','');
getAllMytext(){ // 文本框输入失去焦点时获得输入框的内容
this.textarea = this.$refs.myTextarea.innerHTML;
this.xcxUrlObj.textarea = this.textarea;
var textContent = strLength.getByteVal2(this.$refs.myTextarea.innerText);
this.textNum = strLength.getZhLen(textContent);
this.xcxUrlObj.textNum = this.textNum;
if(this.textNum>=300){
this.$message.error('最多300个字符哦');
// return;
}
var reg = /<a\s+href=['"]([^"]*)['"].*?[^>]*>(.*?)<\/a>/g;
if(reg.exec(this.textarea)!=null){
this.cancelLink=true;
......@@ -274,6 +297,10 @@
this.textarea = this.textarea + '<a href="'+this.form.url+'">'+this.form.text+'</a>';
this.xcxUrlObj.textarea = this.textarea;
var textContent = strLength.getByteVal2(this.$refs.myTextarea.innerText);
this.textNum = strLength.getZhLen(textContent);
this.xcxUrlObj.textNum = this.textNum;
var xcxObj = {};
xcxObj.type=val.id;
xcxObj.title=val.name;
......@@ -360,9 +387,17 @@
self.form.url='http://' + self.form.url;
}
self.textarea = self.textarea + '<a href="'+self.form.url+'">'+self.form.text+'</a>';
self.xcxUrlObj.textarea = self.textarea.replace('undefined','');
self.xcxUrlObj.textarea = self.textarea;
self.xcxUrlObj.imageMediaId = '';
var textContent = strLength.getByteVal2(self.$refs.myTextarea.innerText);
self.textNum = strLength.getZhLen(textContent);
self.xcxUrlObj.textNum = self.textNum;
// self.xcxUrlObj.wechatContent = null;
self.xcxUrlObj.textarea = self.xcxUrlObj.textarea.replace(/undefined/g,'')
self.$emit('listenWxText',self.xcxUrlObj)
}
} else if (self.form.linkStyle==1){ // 小程序链接
......@@ -382,9 +417,14 @@
}
self.addReplayDialog = false;
self.textarea = self.textarea + '<a href="" >'+ self.form.text +'</a>';
self.xcxToolObj.textarea = self.textarea.replace('undefined','');
self.xcxToolObj.textarea = self.textarea;
self.xcxToolObj.imageMediaId = self.imageMediaId;
self.xcxToolObj.wechatContent = self.wechatContent;
var textContent = strLength.getByteVal2(self.$refs.myTextarea.innerText);
self.textNum = strLength.getZhLen(textContent);
self.xcxUrlObj.textNum = self.textNum;
self.$emit('listenWxText',self.xcxToolObj)
// self.$refs.childlinktoolspage.confirmLinkSelect();
}
......@@ -400,14 +440,14 @@
.attention-text-wrap{
.attention-text-content{
width: 100%;
border: 1px solid #e7e7eb;
/* border: 1px solid #e7e7eb; */
.text-title{
width: 100%;
height: 40px;
line-height: 44px;
position: relative;
z-index: 1;
border-bottom: 1px solid #e7e7eb;
border: 1px solid #e7e7eb;
padding-left: 10px;
box-sizing: border-box;
a{
......@@ -428,10 +468,18 @@
}
}
.text-content{
.text-tip{
color: #c0c4cc;
font-size: 12px;
height: 30px;
line-height: 32px;
text-align:right;
}
.my-textarea{
border: 1px solid #e7e7eb;
border-top: none;
width: 100%;
min-height: 110px;
border: none;
outline: none;
resize: none;
padding: 15px;
......@@ -439,10 +487,10 @@
box-sizing: border-box;
line-height: 1.3;
a{
color: #409EFF
color: #1890ff
}
.selectLinkText{
color: #409EFF
color: #1890ff
}
}
}
......@@ -467,8 +515,8 @@
color: #c0c4cc;
font-size: 12px;
background: #fff;
height: 34px;
line-height: 34px;
height: 30px;
line-height: 32px;
padding-left: 10px;
}
}
......@@ -506,7 +554,7 @@
position: relative;
overflow: hidden;
&:hover {
border-color: #409EFF;
border-color: #1890ff;
}
}
.avatar-uploader-icon {
......@@ -530,7 +578,7 @@
overflow: hidden;
}
.upload-content /deep/ .avatar-uploader .el-upload:hover{
border-color: #409EFF;
border-color: #1890ff;
}
/*.add-replay-link /deep/ .replay-link-dialog > .el-dialog{
max-height: 600px;
......@@ -542,4 +590,7 @@
/*.add-replay-content::-webkit-scrollbar-track-piece{
background-color:transparent;
}*/
.url-link-wrap /deep/ .el-select{
width: 217px;
}
</style>
......@@ -2,6 +2,9 @@
import ecm from '../../views/ecm'
import ecmList from '../../views/ecm/list'
import addEdit from '../../views/ecm/addEdit'
import batchList from '../../views/ecm/batch-list'
import currentList from '../../views/ecm/current-list'
export default {
......@@ -35,7 +38,23 @@ export default {
type: 'add',
path:'/ecm/list'
}
}
},
{
path: 'batchlist',
name: '批次记录',
component: batchList,
meta: {
path:'/ecm/list'
}
},
{
path: 'currentlist',
name: '实时发送记录',
component: currentList,
meta: {
path:'/ecm/list'
}
},
]
}
......
......@@ -11,7 +11,7 @@ const _vm = new Vue();
//获取营销场景
export const sceneSettingList = (params) => requests(MARKET_PREFIX + 'scene-setting-list', params);
//获取营销场景
//获取卡券列表
export const getCardList = (params) => requests(PLUG_PREFIX + 'get-coupon-list', params);
//所有门店分组
......
......@@ -5,8 +5,8 @@ const PREFIX = 'api-marketing/';
import config from '@/config';
export const url = config.api + PREFIX;
//智能营销--ECM营销引擎分页列表
export const loadEcmList = (params) => requests(PREFIX + 'page-ecm-list', params);
//智能营销--ECM营销引擎分页列表 TODO
export const loadEcmList = (params) => requests(PREFIX + 'deprecate/page-ecm-list', params);
//智能营销--ECM营销引擎-- 删除
export const deleteEcm = (params) => requests(PREFIX + 'delete-ecm', params);
......@@ -14,5 +14,20 @@ export const deleteEcm = (params) => requests(PREFIX + 'delete-ecm', params);
//智能营销--ECM营销引擎-- 新建/修改 回显智能引擎计划信息
export const getEcmInfo = (params) => requests(PREFIX + 'get-ecm-info', params);
//智能营销--ECM营销引擎-- 新建/修改 [保存]计划信息
// 智能营销--ECM营销引擎-- 新建/修改 [保存]计划信息
export const saveEcmInfo = (params) => requests(PREFIX + 'save-update-ecm', params);
// 智能营销--下线 TODO
export const offlineEcmPlan = (params) => requests(PREFIX + 'deprecate/ecm-plan-offline', params);
// 智能营销--记录--批次记录页面 TODO
export const ecmBatchSendInfos = (params) => requests(PREFIX + 'deprecate/ecm-batch-send-infos', params);
// 智能营销--记录--批次人员列表 TODO
export const ecmBatchSendDetails = (params) => requests(PREFIX + 'deprecate/ecm-batch-send-details', params);
// 智能营销--实时发送记录类型 TODO
export const ecmCurrentSendInfos = (params) => requests(PREFIX + 'deprecate/ecm-current-send-infos', params);
// 智能营销--实时发送人员列表 TODO
export const ecmCurrentSendDetails = (params) => requests(PREFIX + 'deprecate/ecm-current-send-details', params);
......@@ -3,6 +3,9 @@ import qs from 'qs';
import axios from 'axios';
const host = window.location.origin;
const PREFIX = '/api-marketing/'
const PLUG_PREFIX = '/api-plug/'
// 加载最小时间
const MINI_TIME = 300
// 超时时间
......@@ -127,31 +130,32 @@ const requests = (url, data = {},contentTypeIsJSON = false, isSilence = false, m
})
})
}
// 获取营销事件配置信息(类型, 最大条数)
export const getMarketingEvent = (params) => requests( PREFIX + 'get-marketing-event', params);
//素材库 图文 图文分页列表
export const loadImgTextList = (params) => requests( PREFIX + 'page-marketing-wechat-image-text', params);
//获取卡券列表
export const getCardList = (params) => requests(PLUG_PREFIX + 'get-coupon-list', params);
//素材库--图片--图片分页列表
export const loadImgList = (params) => requests( PREFIX + 'page-marketing-wechat-image', params);
//素材库--图片--编辑图片名称
export const updateImgName = (params) => requests( PREFIX + 'update-marketing-wechat-image-titlename', params);
//素材库--图片--修改图片分组
export const updateGroupName = (params) => requests( PREFIX + 'update-marketing-wechat-image-group', params);
//素材库--图片--新建图片分组
export const addGroupService = (params) => requests( PREFIX + 'save-marketing-wechat-image-group', params);
//素材库--图片--删除图片分组
export const deleteGroupService = (params) => requests( PREFIX + 'delete-marketing-wechat-image-group', params);
//素材库--图片--批量删除图片
export const deleteImageService = (params) => requests( PREFIX + 'delete-batch-marketing-wechat-image', params);
//素材库--图片--移动分组
export const changeGroupService = (params) => requests( PREFIX + 'marketing-wechat-image-changeGroup', params);
// 智能营销--ECM营销引擎-- 新建/修改 --回显营销事件类型详情 TODO
export const getMarketingTypeDetails = (params) => requests( PREFIX + 'deprecate/get-marketing-type-details', params);
// 智能营销--新增/修改营销事件类型
export const saveUpdateMarketingType = (params) => requests( PREFIX + 'deprecate/save-update-marketing-type', params);
// 智能营销 --删除营销事件
export const deleteMarketingType = (params) => requests( PREFIX + 'deprecate/delete-marketing-Type', params);
//模板库--分页列表 (有效)
export const LoadTempList = (params) => requests(PREFIX + 'load-message-templateList', params);
"use strict";
exports.__esModule = true;
function _broadcast(componentName, eventName, params) {
this.$children.forEach(function (child) {
var name = child.$options.componentName;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
_broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
exports.default = {
methods: {
dispatch: function dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast: function broadcast(componentName, eventName, params) {
_broadcast.call(this, componentName, eventName, params);
}
}
};
......@@ -8,6 +8,9 @@
.w240 {
width: 240px;
}
.w400{
width: 400px;
}
.w500{
width: 500px;
}
......@@ -18,9 +21,24 @@
.block{
display: block!important;
}
.gray-color{
.gray-color,.gray{
color:#909399;
}
.pb22 {
padding-bottom:22px;
}
.pb10 {
padding-bottom:10px;
}
.pl20 {
padding-left: 20px;
}
.pl10{
padding-left:10px;
}
.pr10{
padding-right:10px;
}
.fz13 {
font-size: 13px;
}
......@@ -33,6 +51,7 @@
.fr {
float: right;
}
.clearfix:after{
display: block;
clear: both;
......@@ -79,13 +98,22 @@
-webkit-line-clamp:3;
-webkit-box-orient:vertical
}
.pb22{
padding-bottom: 22px;
}
.blue {
color: #1890ff;
cursor: pointer;
}
.label-hidden .el-checkbox__label, .label-hidden .el-radio__label {
opacity: 0;
position: absolute;
}
.dm-pagination {
text-align: right;
margin: 24px 0 10px 0;
}
.text-left {
text-align: left;
}
/* reset样式end */
.dm-marketing__opt{
......@@ -114,8 +142,13 @@
color:#1890ff;
border:1px solid #1890ff;
}
.dm-marketing__opt--icon.el-icon-delete:hover {
color:#f56c6c;
border:1px solid #f56c6c;
}
.dm-marketing__opt--icon--bottom{
margin:20px 0 0 70px;
margin:20px 0 0 60px;
}
.dm-marketing__opt__item {
display:inline-block;
......@@ -151,6 +184,7 @@
}
.dm-marketing__content__item{
padding: 20px 0 0 0;
min-width: 950px;
}
.dm-marketing__content__item::after {
......@@ -183,7 +217,7 @@
display: inline-block;
position: relative;
width: 320px;
height: 290px;
/* height: 290px; */
margin:0 10px;
vertical-align: middle;
background: #fff;
......@@ -196,12 +230,31 @@
top: 0;
left: 0;
width: 320px;
height: 290px;
height: 100%;
border-radius:4px;
background: rgba(0,0,0,0.5);
text-align: center;
color: #fff;
line-height: 290px;
cursor: pointer;
z-index:9;
}
.dm-imgText__item__other__mask {
position: absolute;
top: 0;
left: 0;
width: 320px;
height: 100%;
border-radius:4px;
background: rgba(0,0,0,0.5);
text-align: center;
color: #fff;
line-height: 290px;
cursor: pointer;
z-index:9;
}
.dm-imgText__item{
position: relative;
padding:15px;
}
.dm-imgText__item > img{
......@@ -210,6 +263,21 @@
height: 163px;
margin: 20px 0 20px;
}
.dm-imgText__item__other{
position: relative;
border-top: 1px solid #E4E7ED;
padding:15px;
}
.dm-imgText__item__other > img{
float: left;
width: 60px;
height: 60px;
margin-right: 10px;
}
.dm-imgText__item__other > p{
float: left;
width: 210px;
}
.dm-text__wrap{
......@@ -217,23 +285,23 @@
margin-left: 10px;
}
.dm-img__item__wrap{
.dm-image__item__wrap{
display: inline-block;
margin-left: 10px;
vertical-align: middle;
text-align: center;
width: 180px;
/* width: 180px; */
height: 122px;
border: 1px solid rgba(228,231,237,1);
border-radius: 2px;
}
.dm-img__item__wrap > img {
.dm-image__item__wrap > img {
width: auto;
height: 100%;
}
.dm-app__item__wrap{
.dm-wxa__item__wrap{
display: inline-block;
position: relative;
width: 320px;
......@@ -245,19 +313,19 @@
border-radius:4px;
box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.10);
}
.dm-app__item{
.dm-wxa__item{
padding:15px;
}
.dm-app__avatar {
.dm-wxa__avatar {
padding-bottom:15px;
}
.dm-app__avatar > img {
.dm-wxa__avatar > img {
width: 50px;
height: 50px;
border-radius: 50%;
vertical-align: middle;
}
.dm-app__avatar > span {
.dm-wxa__avatar > span {
vertical-align: middle;
max-width: 100px;
line-height: 20px;
......@@ -265,18 +333,18 @@
text-overflow: ellipsis;
}
.dm-app__item > img{
.dm-wxa__item > img{
display: inline-block;
width: 290px;
height: 163px;
margin: 20px 0 15px;
}
.dm-app__item--bottom > img {
.dm-wxa__item--bottom > img {
width: 14px;
height: 14px;
vertical-align: middle;
}
.dm-app__item--bottom > span {
.dm-wxa__item--bottom > span {
vertical-align: middle;
color: #909399;
font-size: 12px;
......@@ -341,9 +409,9 @@
}
/* tel msg */
.dm-msg__item__wrap,.dm-tel__item__wrap{
width: 860px;
/* teltask message */
.dm-message__item__wrap,.dm-teltask__item__wrap{
width: 760px;
height: 94px;
margin:0 10px;
border: 1px solid rgba(228,231,237,1);
......@@ -351,12 +419,12 @@
display: inline-block;
vertical-align: middle;
}
.dm-msg__item,.dm-tel__item{
.dm-message__item,.dm-teltask__item{
padding: 15px;
display: inline-block;
vertical-align: middle;
}
.dm-msg__item--title,.dm-tel__item--title {
.dm-message__item--title,.dm-teltask__item--title {
font-size: 16px;
max-height: 36px;
line-height: 36px;
......@@ -367,7 +435,7 @@
-webkit-box-orient: vertical;
}
.dm-msg__item--content,.dm-tel__item--content {
.dm-message__item--content,.dm-teltask__item--content {
font-size: 14px;
color: #606266;
max-height: 30px;
......
/**
* 补零
* @param {String/Number} num
*/
export const fillZero = (num) => {
num = num * 1;
if (num < 10) {
return '0' + num;
} else {
return num;
}
}
/**
* @param {*时间} date
* @param {*转换的格式} type
*/
export const formateDateTimeByType = (date, type = 'yyyy-MM-dd-HH-mm-ss') => {
if (!date){return ''}
if (typeof date === 'number') {
date = new Date(date);
}
if (typeof date === 'string') {
return date
} else {
var year = type.indexOf('yyyy') >= 0 ? (fillZero(date.getFullYear())) : '';
var month = type.indexOf('MM') >= 0 ? ('-' + fillZero(date.getMonth() + 1)) : '';
var day = type.indexOf('dd') >= 0 ? ('-' + fillZero(date.getDate())+'') : '';
var hours = type.indexOf('HH') >= 0 ? (' ' + fillZero(date.getHours())) : '';
var min = type.indexOf('mm') >= 0 ? (':' + fillZero(date.getMinutes())) : '';
var sec = type.indexOf('ss') >= 0 ? (':' + fillZero(date.getSeconds())) : '';
// console.log(year+month+day+hours+min+sec);
return year + month + day + hours + min + sec;
}
}
/*
* 限制字数用, 一个汉字算一个字,两个英文/字母算一个字
*/
export const getByteVal = (val, max) =>{
var returnValue = '';
var byteValLen = 0;
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
if (byteValLen > max)
break;
returnValue += val[i];
}
return returnValue;
};
/*
* 一个汉字算一个字,一个英文字母或数字算半个字
*/
export const getZhLen = (val) => {
var len = 0;
for (var i = 0; i < val.length; i++) {
var a = val.charAt(i);
if (a.match(/[^\x00-\xff]/ig) != null) {
len += 1;
}
else {
len += 0.5;
}
}
return Math.ceil(len);
};
/*
* 一个汉字算一个字,两个英文/字母算一个字
*/
export const getByteVal2 = (val) => {
var returnValue = '';
var byteValLen = 0;
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
returnValue += val[i];
}
return returnValue;
}
<template>
<el-popover class="el-button el-button--text"
placement="top"
width="160"
v-model="visible">
<p style="line-height:1.5;padding:10px 10px 20px;color:#606266;">{{tips}}</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="cancel">取消</el-button>
<el-button type="primary" size="mini" @click="confirm">确定</el-button>
</div>
<span slot="reference"><slot></slot></span>
</el-popover>
</template>
<script>
export default {
name:'dm-delete',
props:{
tips:{
type:String,
default:'是否删除?'
},
},
data() {
return {
visible: false,
};
},
methods:{
cancel() {
this.visible = false
},
confirm() {
this.visible = false
this.$emit('confirm')
}
}
}
</script>
<template>
<div class="dm-input" :class="{'disabled':disabled}">
<el-input
ref="elInput"
v-model="currentValue"
:placeholder="placeholder"
:size="size"
:resize="resize"
:name="name"
:form="form"
:id="id"
:maxlength="maxlength"
:minlength="minlength"
:readonly="readonly"
:autofocus="autofocus"
:disabled="disabled"
:type="type"
:autosize="autosize"
:rows="rows"
:autoComplete="autoComplete"
:max="max"
:min="min"
:step="step"
:validateEvent="validateEvent"
:suffixIcon="suffixIcon"
:prefixIcon="prefixIcon"
:label="label"
:clearable="clearable"
:tabindex="tabindex"></el-input>
<div class="dm-input__counter" :class="{'text-area':type === 'textarea'}">
<div class="dm-input__counter--inner">
<span class="cur">{{currentValue.length}}</span>
<span class="split"> / </span>
<span class="max">{{maxlength||0}}</span>
</div>
</div>
</div>
</template>
<script>
import emitter from '../assets/emitter.js';
export default {
name: 'dm-input',
props: {
// 自定义属性
// maxLength: Number,
// 原生属性
value: [String, Number],
placeholder: String,
size: String,
resize: String,
name: String,
form: String,
id: String,
maxlength: Number,
minlength: Number,
readonly: Boolean,
autofocus: Boolean,
disabled: Boolean,
type: {
type: String,
default: 'text'
},
autosize: {
type: [Boolean, Object],
default: false
},
rows: {
type: Number,
default: 2
},
autoComplete: {
type: String,
default: 'off'
},
max: {},
min: {},
step: {},
validateEvent: {
type: Boolean,
default: true
},
suffixIcon: String,
prefixIcon: String,
label: String,
clearable: {
type: Boolean,
default: false
},
tabindex: String
},
data() {
return {
currentValue: this.value,
};
},
// computed: {
// readonly() {
// return this.currentValue.length >= this.maxlength;
// }
// },
watch: {
value(val) {
if (val !== this.currentValue) {
this.currentValue = val;
}
},
currentValue(val) {
let value = '';
if (val.length > this.maxlength) {
value = val.slice(0, maxlength - 1);
} else {
value = val;
}
this.$emit('input', value);
this.dispatch('ElFormItem', 'el.form.change', [val]);
}
},
mixins: [emitter],
}
</script>
<style lang="scss" scoped>
.dm-input {
display: inline-block;
background: #fff;
width: 100%;
position: relative;
&.disabled {
background: #f5f7fa;
}
&__counter {
position: absolute;
bottom: 1px;
right: 10px;
height: 28px;
line-height: 12px;
font-size: 12px;
background: inherit;
opacity: 0.9;
&--inner{
display: flex;
align-items: center;
height: 100%;
& > span {
color:#909399;
}
}
}
&__counter.text-area {
bottom: -20px;
height: 20px;
right: 0;
}
}
</style>
<template>
<div class="dm-card__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div class="dm-card__item">
<img class="dm-card__item__avatar" :src="loadErrorImg" alt="">
<div class="inline-block">
<p class="dm-card__item__title">20元代金券</p>
<p class="dm-card__item__desc">领取后0-7天</p>
</div>
</div>
<p class="dm-card__item--bottom"><span>适用所有门店</span></p>
</div>
<div class="dm-card__item__wrap">
<div class="dm-card__item" :style="{background:item.cardColor}">
<img class="dm-card__item__avatar" :src="item.brandLogo || loadErrorImg" alt="">
<div class="inline-block">
<p class="dm-card__item__title">{{item.cardName}}</p>
<p class="dm-card__item__desc" v-if="item.cardEffectiveMode !== 0">领取后第{{item.startDay+1}}-{{item.limitDay+item.startDay}}</p>
<p class="dm-card__item__desc" v-if="item.cardEffectiveMode === 0">{{formateDateTimeByType(item.beginDate,'yyyy-MM-dd')}}{{formateDateTimeByType(item.endDate,'yyyy-MM-dd')}}</p>
</div>
</div>
<p class="dm-card__item--bottom">
<span>适用{{item.storeMode === 0 ? "所有门店" : (item.storeMode === 1 ? "部分分组" : "部分门店")}}</span>
</p>
</div>
</template>
<script>
import {formateDateTimeByType} from '../assets/utils.js'
export default {
name:'com-card',
name:'item-card',
data(){
return {
maskShow:false,
formateDateTimeByType,
loadErrorImg:require('../img/loaderror.png')
}
},
......
<template>
<div class="dm-imgText__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div v-show="maskShow" class="dm-imgText__item__mask"></div>
<div class="dm-imgText__item" >
<p class="ellipsis-l2">测试测试测试测试测测试测试测试测试测试测试测试测试试测试测试测试测试测试测试测试测试测试测试测试</p>
<img :src="loadErrorImg" alt="" srcset="">
<p class="ellipsis fz13 gray-color">测试测试测试测试测测试测试测试测试测试测试测试测试试测试测试测试测试测试测试测试测试测试测试测试</p>
</div>
</div>
<span class="dm-image__item__wrap">
<img :src="item.qcloudImageUrl || loadErrorImg" alt="">
</span>
</template>
<script>
export default {
name:'com-img-text',
data(){
return {
maskShow:false,
loadErrorImg:require('../img/loaderror.png')
}
},
props:{
item:{
name: 'item-image',
props: {
item: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
loadErrorImg:require('../assets/img/loaderror.png')
}
}
}
</script>
<template>
<span class="dm-message__item__wrap">
<div class="dm-message__item">
<p class="dm-message__item--title">{{item.title}}</p>
<p class="dm-message__item--content">{{item.content}}</p>
</div>
</span>
</template>
<script>
export default {
name: 'item-message',
props: {
item: {
type: Object,
default() {
return {}
}
}
}
}
</script>
<template>
<div class="dm-imgText__item__wrap" >
<div v-for="(v,i) in item.itemList" :key="i">
<div class="dm-imgText__item" v-if="i === 0" @mouseover="v.maskShow = true" @mouseout="v.maskShow = false">
<div v-show="v.maskShow" class="dm-imgText__item__mask" @click="window.open(v.mediaUrl)">预览文章</div>
<p class="ellipsis-l2">{{v.titleName}}</p>
<img :src="v.qcloudImageUrl || loadErrorImg" alt="" srcset="">
<p class="ellipsis fz13 gray-color">{{v.remark}}</p>
</div>
<div class="dm-imgText__item__other clearfix" v-else @mouseover="v.maskShow = true" @mouseout="v.maskShow = false">
<div v-show="v.maskShow" class="dm-imgText__item__other__mask" @click="window.open(v.mediaUrl)">预览文章</div>
<img :src="v.qcloudImageUrl || loadErrorImg" alt="" srcset="">
<p class="ellipsis-l2">{{v.titleName}}</p>
</div>
</div>
</div>
</template>
<script>
export default {
name:'item-teletext',
data(){
return {
maskShow:false,
loadErrorImg:require('../assets/img/loaderror.png')
}
},
props:{
item:{
type: Object,
default() {
return {
itemList:[]
}
}
}
},
created() {
if (this.item.itemList instanceof Array) {
this.item.itemList.forEach(v => {
v.maskShow = false
})
}
}
}
</script>
<template>
<span class="dm-teltask__item__wrap">
<div class="dm-teltask__item">
<p class="dm-teltask__item--title">{{item.title}} <span class="fz13 gray-color">任务逾期判定{{item.lateDays}}天之后</span></p>
<p class="dm-teltask__item--content">{{item.content}}</p>
</div>
</span>
</template>
<script>
export default {
name: 'item-teltask',
props: {
item: {
type: Object,
default() {
return {}
}
}
}
}
</script>
<template>
<span class="dm-text__wrap ellipsis-l2 inline-block" v-html="item.content"></span>
</template>
<script>
export default {
name: 'item-text',
props: {
item: {
type: Object,
default() {
return {}
}
}
}
}
</script>
<template>
<div class="dm-app__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div class="dm-app__item">
<div class="dm-app__avatar">
<img :src="loadErrorImg" alt="">
<span>达摩公开课</span>
<div class="dm-wxa__item__wrap" @mouseover="maskShow = true" @mouseout="maskShow = false">
<div class="dm-wxa__item">
<div class="dm-wxa__avatar">
<img :src="item.brandLogo || loadErrorImg" alt="">
<span>{{item.brandName}}</span>
</div>
<p class="ellipsis-l2">{{item.title}}</p>
<img :src="item.imageUrl || loadErrorImg" alt="" srcset="">
<p class="dm-wxa__item--bottom"><img src="../img/wxa-circle.svg" alt="" srcset=""><span> 小程序</span></p>
</div>
<p class="ellipsis-l2">测试测试测试测试测测试测试测试测试测试测试测试测试试测试测试测试测试测试测试测试测试测试测试测试</p>
<img :src="loadErrorImg" alt="" srcset="">
<p class="dm-app__item--bottom"><img src="../img/app-circle.svg" alt="" srcset=""><span> 小程序</span></p>
</div>
</div>
</template>
<script>
export default {
name:'com-app',
name:'item-wxa',
data(){
return {
maskShow:false,
loadErrorImg:require('../img/loaderror.png')
loadErrorImg:require('../assets/img/loaderror.png')
}
},
props:{
......
<template>
<el-dialog title="选择卡券" :visible.sync="show" width="800px" :before-close="close">
<div class="clearfix pb22">
<div class="fl">
<span>{{total}}</span>
<el-input v-model="listParams.searchParam" class="w200" clearable placeholder="请输入卡券名称" @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<span class="fz12 gray pl20">{{limitTips}} </span>
</div>
<div class="fr">
<el-button type="primary" @click="add">新建卡券</el-button>
<el-button @click="refresh">刷新列表</el-button>
</div>
</div>
<el-table tooltipEffect="light" :data="tableList" :height="360" style="width: 100%" v-loading="loading" @row-click="rowClick">
<el-table-column :show-overflow-tooltip="false" width="60" align="center" prop="coupCardId">
<template slot-scope="scope">
<div class="sms-record_left label-hidden">
<el-radio v-model="selectedData" :label="scope.row" class="pr10"></el-radio>
</div>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="false" align="left" prop="cardName" label="卡券名称"></el-table-column>
<el-table-column :show-overflow-tooltip="false" :width="100" align="left" prop="cardLimit" label="领取限制"></el-table-column>
<el-table-column :show-overflow-tooltip="false" :width="120" align="left" prop="storeMode" label="适用门店">
<template slot-scope="scope">
{{scope.row.storeMode === 0?'所有门店':(scope.row.storeMode === 1?'部分分组':'部分门店')}}
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="false" :width="100" align="left" prop="couponStock" label="库存"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="left" prop="subName" label="描述"></el-table-column>
</el-table>
<el-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, prev, pager, next" :total="total"></el-pagination>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import {getCardList} from '../assets/api.js';
export default {
name:'lib-card',
props:{
show:{
type:Boolean,
default:false
},
cardLimitType:{
type:Number,
default:1
}
},
computed:{
limitTips() {
if (this.cardLimitType === 2) {
return '领取限制领取 1~100的卡券,系统已过滤。';
} else if (this.cardLimitType === 3) {
return '领取限制领取>=100 的卡券,系统已过滤。';
} else {
return '领取限制>1的卡券不支持选择,系统已过滤。';
}
}
},
data(){
return{
listParams:{
searchParam:'',
currentPage:1,
pageSize:10,
requestProject:'gic-web',
cardLimitType:this.cardLimitType,
cardType:''
},
total:0,
tableList:[],
selectedData:{}
}
},
created(){
this.getCardList();
},
methods:{
handleSizeChange(val) {
this.listParams.pageSize = val;
this.getCardList();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.getCardList();
},
getCardList() {
this.loading = true;
getCardList(this.listParams).then(res => {
if (res.errorCode === 0) {
this.tableList = res.result.result || [];
this.total = res.result.totalCount;
}
this.loading = false;
})
},
reset() {
this.listParams.searchParams = '';
this.getCardList();
},
close() {
this.$emit('update:show',false);
},
rowClick(row) {
row.comName = 'card';
this.selectedData = row;
},
addItem() {
if (!this.selectedData.comName) {
this.$message({type:'warning',message:'未选择卡券'});
return;
}
this.$emit('sendItem',this.selectedData);
this.close();
},
add() {
winodw.open('/marketing/#/card/add');
},
refresh() {
this.listParams.currentPage = 1;
this.getCardList();
}
}
}
</script>
<template>
<el-dialog title="选择图片" :visible.sync="show" width="800px" :before-close="close">
<div class="dm-ilib clearfix">
<nav class="fl">
<h4 class="text-left pl10" ><el-button style="font-weight: 500;" size="small" type="text" icon="el-icon-plus" @click.stop="addGroup">新建分组</el-button></h4>
<ul class="dm-ilib-category">
<li v-for="(v,i) in groupsList" :key="i" @click="changeGroup(v)" :class="{'active':listParams.wechatImageGroupId===v.wechatImageGroupId}"><span class="ellipsis-80">{{v.groupName}}</span><span class="fz10 gray"> ({{v.imageCount}})</span></li>
</ul>
</nav>
<article class="fr">
<div class="dm-ilib-header">
<h4>{{listParams.wechatImageGroupId?currentGroup.groupName:'所有图片'}}</h4>
<div class="dm-ilib-opt_right">
<span class="fz12 gray pr10">大小不超过2M</span>
<label class="el-button el-button--primary el-button--small" :class="{'is-disabled':upLoadDisabled}">{{upLoadDisabled?'上传中...':'上传图片'}}
<input type="file" style="display:none;" :disabled="upLoadDisabled" accept="image/gif, image/jpeg,image/png" ref="uploader" v-imglibupload='this'>
</label>
</div>
</div>
<el-checkbox-group v-loading="loading" v-model="selectedId" :max="1" class="clearfix img-item__wrap">
<label :name="v.imageId" class="dm-img-item" v-for="(v,i) in imgList" :key="i" :item="v" @click.stop.prevent="rowClick(v)">
<div class="dm-img-item_top">
<img :src="v.qcloudImageUrl" alt="" srcset="">
</div>
<div class="dm-img-item_center">
<el-checkbox :label="v.imageId">{{v.imageTitle}}</el-checkbox>
</div>
</label>
</el-checkbox-group>
<el-pagination v-show="imgList.length" background class="dm-pagination imglib-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></el-pagination>
</article>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import {loadImgList,addGroupService} from '../assets/api.js';
export default {
name:'lib-img',
props:{
show:{
type:Boolean,
default:false
},
},
created(){
this.loadImgList();
},
data() {
return {
upLoadDisabled:false,
imgList:[],
listParams:{
currentPage:1,
pageSize:12,
wechatImageGroupId:''
},
total:0,
selectedData:{},
selectedId:[],
groupsList:[],
groupsMoveList:[],
loading:true,
currentGroup:{groupName:''},
moveImgVal:'',
fileList: [],
}
},
methods:{
handleSizeChange(val) {
this.listParams.pageSize = val;
this.loadImgList();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.loadImgList();
},
loadImgList() {
this.loading = true;
loadImgList(this.listParams).then(res => {
if (res.errorCode === 0) {
this.total = res.result.page.totalCount;
this.currentGroup = res.result.currentGroup;
this.groupsMoveList = Object.assign([],res.result.groups);
this.groupsList = res.result.groups;
this.groupsList.unshift({wechatImageGroupId: "",groupName: "所有图片",imageCount: res.result.totalPicsCount })
this.imgList = [];
if (res.result.page.result) {
const resList = res.result.page.result;
this.$nextTick(_ => {
resList.map(v => {
this.imgList.push(v)
})
})
} else {
this.imgList = [];
}
}
this.loading = false;
}).catch(err => {
this.$message({type:'warning',message:'获取图片列表错误'});
})
},
changeGroup(v){
this.listParams.wechatImageGroupId = v.wechatImageGroupId;
this.loadImgList();
},
addGroup() {
this.$prompt('', '新增分组', {
confirmButtonText: '确定',
cancelBUttonText: '取消',
inputPattern: /\S/,
inputPlaceholder:'请输入分组名称',
inputErrorMessage: '名称不能为空',
inputValue:'新分组',
}).then(({ value }) => {
this.addGroupService(value);
}).catch(err => {
console.log(err)
});
},
addGroupService(val) {
addGroupService({groupName:val}).then(res => {
if (res.errorCode === 0) {
this.$message({type: 'success',message: '新增成功'});
this.loadImgList();
} else {
this.$message({type: 'error',message: '新增失败'});
}
}).catch(err => {
this.$message({type: 'error',message: '新增失败'});
})
},
close() {
this.$emit('update:show',false);
},
rowClick(row) {
row.comName = 'image';
row.title = row.imageTitle;
this.selectedId = [row.imageId];
this.selectedData = row;
},
addItem() {
if (!this.selectedData.comName) {
this.$message({type:'warning',message:'未选择图片'});
return;
}
this.$emit('sendItem',this.selectedData);
this.close();
}
}
}
</script>
<style lang="scss" scoped>
.dm-ilib{
border: 1px solid #DCDFE6;
border-radius: 2px;
&>article{
position:relative;
width: calc(80% - 1px);
border-left: 1px solid #DCDFE6;
border-radius:0 2px 2px 0;
height:400px;
.dm-ilib-header{
display: flex;
justify-content: space-between;
padding:0 16px;
align-items: center;
border-bottom: 1px solid #DCDFE6;
}
h4{
height: 50px;
line-height: 50px;
font-size: 16px;
i{
font-size: 20px;
padding-left:10px;
cursor: pointer;
}
}
}
.img-item__wrap {
height: 290px;
margin-left: 6px;
overflow-y:auto;
}
&>nav{
width: 20%;
h4{
height: 50px;
line-height: 50px;
padding-right: 20px;
font-size: 16px;
border-bottom: 1px solid #DCDFE6;
cursor: pointer;
}
.dm-ilib-category{
height: 348px;
overflow-y: scroll;
cursor: pointer;
li{
height: 40px;
line-height: 40px;
padding-left: 10px;
&.active{
background: #f4f5f9;
}
&:hover{
background: #f4f5f9;
}
}
}
}
&-opt{
background: #f4f5f9;
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
padding:0 20px;
}
}
.ellipsis-80{
text-overflow: ellipsis;
overflow: hidden;
max-width: 80px;
white-space: nowrap;
word-wrap: normal;
width: auto;
display: inline-block;
vertical-align: middle;
}
.dm-upload{
display: inline-block;
position: relative;
}
.imglib-pagination{
padding-right:20px;
}
.dm-img-item{
width: 168px;
margin: 20px 10px 0 10px;
float: left;
border: 1px solid #e4e7ed;
border-radius: 2px;
overflow: hidden;
cursor: pointer;
&:hover{
border: 1px solid #1890ff;
}
&_top{
width: 168px;
height: 168px;
position: relative;
img{
width: 100%;
height: 100%;
}
}
&_center{
border-top:1px solid #e4e7ed;
height: 32px;
display: flex;
justify-content: left;
align-items: center;
padding-left: 10px;
/deep/ .el-checkbox__label{
text-overflow: ellipsis;
overflow: hidden;
max-width: 130px;
white-space: nowrap;
word-wrap: normal;
vertical-align: middle;
padding-left: 10px;
}
}
&_bottom{
border-top:1px solid #e4e7ed;
height: 32px;
display: flex;
justify-content: space-around;
align-items: center;
background: #f4f5f9;
}
}
</style>
<template>
<el-dialog title="选择短信" :visible.sync="show" width="800px" :before-close="close">
<div class="pb22 clearfix">
<div class="fl"><span class="pr10">{{total}}</span><el-input clearable v-model="listParams.search" class="w200" placeholder="请输入标题/作者" @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input></div>
<div class="fr">
<el-button type="primary" @click="add">新建短信</el-button>
<el-button @click="refresh">刷新列表</el-button>
</div>
</div>
<el-table tooltipEffect="light" :data="smsTempList" style="width: 100%" v-loading="loading" @row-click="rowClick">
<el-table-column :show-overflow-tooltip="false" :width="60" align="center" prop="smsTemplateId">
<template slot-scope="scope">
<div class="label-hidden">
<el-radio v-model="selectedData" :label="scope.row" class="pr10"></el-radio>
</div>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="false" :width="200" :min-width="200" align="left" prop="title" label="模板名称"></el-table-column>
<el-table-column :show-overflow-tooltip="false" :width="200" :min-width="200" align="left" prop="content" label="模板类型">
<template slot-scope="scope">
<p class="gray">{{scope.row.type === 0 ?'普通短信' :(scope.row.type === 1?'营销短信':'验证码')}}</p>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="false" :min-width="200" align="left" prop="content" label="模板内容">
<template slot-scope="scope">
<div class="ellipsis-l3">{{scope.row.content}}</div>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="smsTempList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></el-pagination>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import { LoadTempList } from '../assets/api.js';
export default {
name:'lib-message',
props:{
show:{
type:Boolean,
default:false
}
},
data(){
return{
listParams:{
search:'',
currentPage:1,
pageSize:20
},
total:0,
smsTempList:[],
selectedData:{}
}
},
created(){
this.LoadTempList();
},
methods:{
handleSizeChange(val) {
this.listParams.pageSize = val;
this.LoadTempList();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.LoadTempList();
},
LoadTempList() {
this.loading = true;
LoadTempList(this.listParams).then(res => {
if (res.errorCode === 0) {
this.smsTempList = res.result && res.result.result || [];
this.total = res.result && res.result.totalCount;
}
this.loading = false;
})
},
rowClick(row) {
row.comName = 'message';
this.selectedData = row;
},
addItem() {
if (!this.selectedData.comName) {
this.$message({type:'warning',message:'未选择短信'});
return;
}
this.$emit('sendItem',this.selectedData);
this.close();
},
close() {
this.$emit('update:show',false);
},
add() {
winodw.open('/marketing/#/message/temp/add');
},
refresh() {
this.listParams.currentPage = 1;
this.LoadTempList();
}
}
}
</script>
<template>
<section>
<el-dialog title="选择图文" :visible.sync="show" width="800px" :before-close="close">
<div class="pb22 clearfix">
<div class="fl">图文消息(共{{total}}条) <el-input v-model="listParams.searchName" clearable class="w200 ml10" placeholder="请输入标题/作者" @change="loadImgTextList"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input></div>
<div class="fl">图文消息(共{{total}}条) <el-input v-model="listParams.searchName" clearable class="w200 ml10" placeholder="请输入标题/作者" @change="refresh"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input></div>
<div class="fr">
<el-button type="primary" @click="add">新建图文</el-button>
<el-button @click="check">刷新列表</el-button>
<el-button @click="refresh">刷新列表</el-button>
</div>
</div>
<el-radio-group class="dm-imgtext-list" v-model="selectedData" v-loading="loading">
......@@ -37,14 +37,24 @@
</el-table>
</el-radio-group>
<el-pagination v-show="textImgList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></el-pagination>
</section>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import {loadImgTextList} from '../api.js';
import {formateDateTimeByType} from '@/utils/index.js'
import {loadImgTextList} from '../assets/api.js';
import {formateDateTimeByType} from '../assets/utils.js'
export default {
name:'lib-teletext',
props:{
show:{
type:Boolean,
default:false
}
},
created(){
this.loadImgTextList();
},
......@@ -58,58 +68,48 @@ export default {
},
total:0,
loading:false,
syncImgTextShow:false,
selectedImgTextId:'',
selectedData:{}
}
},
watch:{
activeId(val) {
this.textImgList.map(v => {
console.log('111'+val)
if (this.activeId === v.imageTextWechatId) {
this.selectedData = v;
}
})
}
},
methods:{
async loadImgTextList() {
this.loading = true
try {
let res = await loadImgTextList(this.listParams);
loadImgTextList() {
this.loading = true;
loadImgTextList(this.listParams).then(res => {
if (res.errorCode === 0) {
this.textImgList = res.result.result || [];
this.total = res.result.totalCount;
this.textImgList.map(v => {
v.createTimeStr = formateDateTimeByType(v.createTime,'yyyy-MM-dd-HH-mm')
v.updateTimeStr = formateDateTimeByType(v.updateTime,'yyyy-MM-dd-HH-mm')
console.log(this.activeId)
if (this.activeId === v.imageTextWechatId) {
this.selectedData = v;
}
})
}
} catch(err) {
console.log(err)
this.$tips({type:'warning',message:'列表请求出错,请稍后再试'});
}
this.loading = false
},
reset() {
this.listParams.searchName = '';
this.loadImgTextList();
},
check() {
this.syncImgTextShow = true;
this.loading = false;
})
},
rowClick(row) {
row.comName = 'teletext';
row.title = row.itemList[0] && row.itemList[0].titleName;
console.log(row)
this.selectedData = row;
this.$emit('get-data',{imageTextId:row.imageTextId,imageTextWechatId:row.imageTextWechatId})
},
add () {
this.$router.push('/wechat/editor');
addItem() {
if (!this.selectedData.comName) {
this.$message({type:'warning',message:'未选择图文消息'});
return;
}
this.$emit('sendItem',this.selectedData);
this.close();
},
close() {
this.$emit('update:show',false);
},
add() {
window.open('/marketing/#/wechat/editor');
},
refresh() {
this.listParams.currentPage = 1;
this.loadImgTextList();
},
handleSizeChange(val) {
this.listParams.pageSize = val;
......@@ -120,37 +120,11 @@ export default {
this.loadImgTextList();
}
}
}
</script>
<style lang="scss" scoped>
.dm-imgtext_head{
display: flex;
justify-content: space-between;
}
.dm-imgtext-list {
width: 100%;
&>.dm-imgtext-item {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #e7e7eb;
}
.dm-imgtext_left{
&>img {
width: 100px;
height: 100px;
display:inline-block;
vertical-align: middle;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
&>div{
display: inline-block;
vertical-align: middle;
line-height: 26px;
padding-left: 10px;
}
}
}
</style>
<template>
<el-dialog title="编辑话务" :visible.sync="show" width="600px" :before-close="close">
<el-form ref="form" :model="form" label-width="110px" :rules="rules" v-loading="loading" >
<el-form-item label="话务任务标题" class="pb10" prop="title">
<dm-input class="w400" v-model="form.title" placeholder="限制20个字符" :maxlength="20"></dm-input>
</el-form-item>
<el-form-item label="话务任务内容" class="pb10" prop="content">
<dm-input class="w400" :rows="4" type="textarea" v-model="form.content" placeholder="限制200个字符" :maxlength="200"></dm-input>
</el-form-item>
<el-form-item label="任务逾期判定" prop="lateDays">
<el-input-number controls-position="right" class="w400" v-model="form.lateDays"></el-input-number> 天之后
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import {getCardList} from '../assets/api.js';
import dmInput from './dm-input.vue';
export default {
name:'lib-tel',
components:{
'dm-input':dmInput,
},
props:{
item:{
type:Object,
default(){
return {}
}
},
show:{
type:Boolean,
default:false
}
},
data(){
return{
form:{
title:'',
lateDays:'',
content:'',
},
rules:{
title:{required:true,type:'string',message:'话务任务标题不能为空',trigger:'blur'},
content:{required:true,type:'string',message:'话务任务内容不能为空',trigger:'blur'},
lateDays:{required:true,type:'number',message:'任务逾期判定不能为空',trigger:'blur'}
},
loading:false
}
},
watch:{
show(val) {
this.form = {
title:'',
lateDays:'',
content:'',
};
if (val && this.item.ecmMarketingTypeRelationId) {
console.log('编辑')
this.form = JSON.parse(JSON.stringify(this.item))
}
}
},
methods:{
close() {
this.$refs.form.resetFields();
this.$emit('update:show',false);
},
addItem() {
this.form.comName = 'teltask';
if (!this.form.title || !this.form.content) {
this.$message({type:'warning',message:'话务未填写完整'});
return;
}
this.$emit('sendItem',this.form);
this.close();
},
}
}
</script>
<template>
<el-dialog title="添加小程序链接" :visible.sync="show" width="600px" class="add-replay-content" :before-close="close">
<el-form label-position="right" ref="form" :model="form" label-width="100px">
<el-form-item class="text-content" label="文本内容">
<el-input v-model="form.title" @keyup.native="toInput(form.title,$event)" ></el-input>
<span class="text-num">{{inputNum}}/{{inputLength}}</span>
</el-form-item>
<div class="content-wrap">
<div class="xcx-content-wrap">
<el-form-item label="上传图片">
<div class="upload-content">
<el-upload
class="avatar-uploader"
action="123"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="form.imageUrl" :src="form.imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<p>请上传长宽比为5:4的图片</p>
</div>
</el-form-item>
<el-form-item label="链接类型" class="url-link-wrap" style="margin-bottom: 0">
<el-select @change="changePreValue" v-model="preValue" class="w200" placeholder="请选择" style="margin-right: 6px;">
<el-option
v-for="item in options1"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-select v-if="preValue" @change="changeNextValue" class="w200" v-model="nextValue" placeholder="请选择">
<el-option
v-for="item in options2"
:key="item.linkId"
:label="item.name"
:value="item.linkId">
</el-option>
</el-select>
</el-form-item>
</div>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="close">关 闭</el-button>
<el-button type="primary" @click="addItem">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import {getByteVal,getZhLen} from '../assets/utils.js';
let qs = require('qs');
export default {
name:'lib-wxa',
props:{
show:{
type:Boolean,
default:false
},
item:{
type:Object,
default(){
return {}
}
},
},
data(){
return {
form: {
title:'',
linkStyle:'0',
url:'',
imageUrl: '',
mediaId:''
},
inputNum: 0, // 输入文字个数(两个字母/汉字算一个字)
inputLength: 20, // 限制最大字数
preValue: '',
nextValue: '',
options1: [{
value: 3,
label: '导航tabber页'
}, {
value: 4,
label: '二级功能页'
}],
options2:[],
wechatContent:{},
}
},
watch:{
show(val) {
this.form = {
title:'',
linkStyle:'0',
url:'',
imageUrl: '',
mediaId:''
}
this.preValue = '';
this.nextValue = '';
this.wechatContent = {};
if (val && this.item.ecmMarketingTypeRelationId) {
this.form = JSON.parse(JSON.stringify(this.item));
console.log('编辑',this.item)
const pagePath = this.form.pagePath ? JSON.parse(this.form.pagePath) : {};
this.wechatContent.type = pagePath.type;
this.wechatContent.objId = '';
this.wechatContent.title = pagePath.title;
this.wechatContent.pageType = pagePath.pageType;
this.preValue = pagePath.pageType;
this.getNextList(this.preValue).then(res => {
this.nextValue = pagePath.type;
})
}
}
},
methods:{
toInput(value,e) { // 控制字符数量
var that = this;
that.form.title = getByteVal(e.target.value,that.inputLength);
that.inputNum = getZhLen(that.form.title);
},
handleAvatarSuccess(res, file) {
},
beforeAvatarUpload(file) {
// console.log(file)
let fd = new FormData();
fd.append('file', file);
fd.append('requestProject', 'gic-web');
this.axios.post('/api-admin/marketing-wechat-image-save', fd).then((res)=> {//成功后回调
var data = res.data;
// console.log(data);
if(data.errorCode==0){
this.form.imageUrl = data.result.qcloudImageUrl;
this.form.mediaId = data.result.imageMediaId;
this.$message({
type:'success',
message:'上传成功'
})
} else {
this.$message({
type:'error',
message:data.message
})
}
})
return true
},
// 链接类型
changePreValue(val){
// val==3 导航tabber页 val==4 二级功能页
if(val){
this.nextValue = '';
this.getNextList(val)
}
},
changeNextValue(val){
if(val){
let obj = {};
obj = this.options2.find((style) => {
return style.linkId === val;
})
this.wechatContent.type = obj.linkId;
this.wechatContent.objId = '';
this.wechatContent.title = obj.name;
this.wechatContent.pageType = this.preValue;
}
},
// 获得链接类型的二级
getNextList(type){
return new Promise((resolve,reject) => {
this.axios.post('/api-plug/list-link-data', qs.stringify({
requestProject: 'gic-web',
linkScene:parseInt(type)
})).then((res)=> {//成功后回调
var data = res.data;
console.log(data);
if(data.errorCode==0){
this.options2 = data.result;
resolve();
} else {
this.$message({
type:'error',
message:data.message
})
}
})
})
},
addItem() {
let sendData = {
ecmMarketingTypeRelationId:this.item.ecmMarketingTypeRelationId,
comName:'wxa',
title:this.form.title,
mediaId:this.form.mediaId,
pagePath:JSON.stringify(this.wechatContent),
}
if (!sendData.title) {
this.$message({type:'warning',message:'简介未填写'});
return;
}
if (!sendData.mediaId) {
this.$message({type:'warning',message:'图片未上传'});
return;
}
if (!sendData.pagePath) {
this.$message({type:'warning',message:'链接选择'});
return;
}
this.$emit('sendItem',sendData);
this.close();
},
close() {
this.$emit('update:show',false);
},
}
}
</script>
<style lang="scss" scoped>
.add-replay-content{
.text-content{
position: relative;
width: 400px;
.text-num{
position: absolute;
right: 10px;
bottom: 2px;
color: #c0c4cc;
font-size: 12px;
background: #fff;
height: 28px;
line-height: 28px;
padding-left: 10px;
}
}
.link-url-warning{
color: #909399;
font-size: 12px;
line-height: 1;
padding-top: 4px;
margin-top: 3px;
position: absolute;
top: 100%;
left: 0;
}
}
.xcx-content-wrap{
width: 100%;
.upload-content{
> p{
height: 36px;
line-height: 36px;
font-size: 12px;
color: #b8b8b8
}
}
}
.avatar-uploader{
width: 152px;
height: 152px;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
&:hover {
border-color: #1890ff;
}
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
line-height: 150px;
text-align: center;
}
.avatar {
width: 150px;
height: 150px;
display: block;
}
.upload-content /deep/ .avatar-uploader .el-upload{
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.upload-content /deep/ .avatar-uploader .el-upload:hover{
border-color: #1890ff;
}
</style>
<template>
<div class="inline-block dm-marketing__opt">
<span v-for="(v,i) in options" :key="i" class="dm-marketing__opt__item" @click="addItem(v)">
<img :src="v.img" alt="" srcset=""><span> {{v.name}}</span>
</span>
</div>
</template>
<script>
export default {
name:'opt',
props:{
options:{
type:Array,
default() {
return []
}
}
},
methods:{
addItem(item) {
this.$emit('addItem',item);
}
}
}
</script>
import axios from 'axios';
import qs from 'qs';
axios.defaults.timeout = 10000;
let base = "http://192.168.1.164:8282/gic/";
const timeout = 10000;
let token = ''//sessionStorage.getItem('user');
/*
*
* 统一 get 请求方法
* @url: 请求的 url
* @params: 请求带的参数
* @header: 带 token
*
*/
export const getRequest = (url, params) => {
return axios({
method: 'get',
url: `${base}${url}`,
data: {},
params: params,
headers: {'content-type': 'application/x-www-form-urlencoded'},// "token": token
});
}
File mode changed from 100644 to 100755
......@@ -146,7 +146,9 @@
display: block;
margin: 10px auto;
}
.dm-store__reference {
position: relative;
}
.dm-store__inputtag{
white-space: nowrap;
overflow: hidden;
......@@ -171,6 +173,9 @@
font-size: 12px;
padding: 0 6px;
}
.dm-store__total--tag{
margin:5px;
}
/* 公共样式END */
......
File mode changed from 100644 to 100755
......@@ -20,13 +20,29 @@ import optionArea from './options/option-area';
import optionTags from './options/option-tags';
import optionGroup from './options/option-group';
import optionPart from './options/option-part';
const leftList = [{label:'所有门店',value:0},
{label:'门店类型',value:1},
{label:'门店标签',value:2},
{label:'门店区域',value:3},
{label:'门店分组',value:4},
{label:'部分门店',value:5}
];
export default {
name:'vue-gic-store-new',
props:{
uuid:{
type:String,
default:''
},
options: {
type:Array,
default() {
return [0,1,2,3,4,5]
}
},
isAdd:{
type:Boolean,
default:true
}
},
components:{
......@@ -36,22 +52,38 @@ export default {
'option-group':optionGroup,
'option-part':optionPart
},
watch:{
storeType(val) {
if (!val) {
this.saveInit();
}
},
uuid(val) {
// 不是新增 编辑或者详情
if (val && !this.isAdd) {
this.init();
}
}
},
data() {
return {
storeType:0, // 一级选择值
leftList:[ // 一级列表
{label:'所有门店',value:0},
{label:'门店类型',value:1},
{label:'门店标签',value:2},
{label:'门店区域',value:3},
{label:'门店分组',value:4},
{label:'部分门店',value:5}
],
leftList:[],
newUuid:uuidv1().replace(/-/g,'') // 生成uuid
}
},
created() {
this.init();
if (this.options instanceof Array) {
leftList.map( v => {
if (this.options.indexOf(v.value) >= 0) {
this.leftList.push(v);
}
})
}
// 新增init
if (this.isAdd) {
this.init();
}
},
methods:{
// 获取配置项
......@@ -59,6 +91,7 @@ export default {
this.newUuid = this.uuid || this.newUuid;
if (!this.uuid) {
this.$emit('update:uuid',this.newUuid);
this.saveInit();
} else {
let params = {
key:this.newUuid,
......@@ -67,12 +100,28 @@ export default {
}
this.axios.post(baseUrl + '/api-plug/get-store-widget-config?requestProject=gic-web',qs.stringify(params)).then(res => {
this.comList = res.data.result.config.result || [];
this.leftValue = res.data.result.selectType || 0;
this.storeType = res.data.result.selectType || 0;
}).catch(err => {
console.log(err)
})
}
}
},
// 保存
saveInit() {
let params = {
selectType:0,
key:'',
isAll:0,
value:'',
removeItems:'',
};
params.key = this.uuid;
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(params)).then(res => {
}).catch(err => {
console.log(err)
})
},
}
}
</script>
......
......@@ -39,20 +39,28 @@
import qs from 'qs';
import { baseUrl } from '../config';
export default {
name:'options-area',
props:{
uuid:{
name: 'options-area',
props: {
uuid: {
type:String,
default:''
}
},
watch: {
areaList:{
/* indeterminate isCheck
* true true 半勾选
* false false 不选
* false true 全选
*/
handler(val) {
this.totalProvince = this.areaList.filter(v => (!v.isIndeterminate && v.isCheck)).length;
// 全选计算省数量
this.totalProvince = this.areaList.filter(v => (!v.indeterminate && v.isCheck)).length;
console.log(this.totalProvince)
this.totalCity = 0;
this.areaList.map(v => {
if (v.children.length) {
// 半勾选计算市数量
if (v.indeterminate && v.isCheck && v.children.length) {
v.children.map(w => {
if (w.isCheck) {
this.totalCity ++
......@@ -122,16 +130,23 @@ export default {
let list = (res.data.result && res.data.result.result) || [];
list.map(u => u.id).map(u => {
this.areaList.map(v => {
// 全选 市一起勾选
if (u === v.provinceId) {
v.isIndeterminate = false;
v.indeterminate = false;
v.isCheck = true;
} else {
v.children.map(w => {
if (u === w.cityId) {
w.isCheck = true;
}
w.isCheck = true;
})
}
// 半勾选
v.children.map(w => {
if (u === w.cityId) {
w.isCheck = true;
v.indeterminate = true;
v.isCheck = true;
}
})
})
})
}).catch(err => {
......@@ -156,7 +171,7 @@ export default {
item.indeterminate = !isAllCheck && isSomeCheck;
}
},
/* isIndeterminate isCheck
/* indeterminate isCheck
* true true 半勾选
* false false 不选
* false true 全选
......@@ -174,18 +189,26 @@ export default {
let valueList = [];
this.areaList.map(v => {
// 全选 只传省ID
if(!v.isIndeterminate && v.isCheck) {
if(!v.indeterminate && v.isCheck) {
valueList.push(v.provinceId)
} else if(v.isIndeterminate && v.isCheck && v.children.length) {
}
if(v.indeterminate && v.isCheck && v.children.length) {
// 半选 传市id
v.children.map(w => {
if (w.isCheck) {
valueList.push(v.cityId)
valueList.push(w.cityId)
}
})
}
});
this.params.value = valueList.filter(v => v).join(',');
if (!this.params.value) {
this.$message({type:"warning",message:"门店选择不能为空"});
return;
}
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(this.params)).then(res => {
if (res.data.errorCode === 0) {
this.popoverShow = false;
......
......@@ -42,7 +42,7 @@
<span class="gray-color dm-store__inputtag--tips" v-show="rightList.length === 0">请选择门店</span>
</div>
<el-popover placement="top-start" popper-class="select-shop__popper" width="300" trigger="hover">
<el-tag class="select-shop__popper--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
<el-tag class="dm-store__total--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
{{v.name}}
</el-tag>
<span slot="reference" class="dm-store__inputtag--total" v-show="rightList.length">{{params.isAll?rightTotal:rightList.length}}</span>
......@@ -125,7 +125,11 @@ export default {
return this.leftCheckList.indexOf(v.tagId) < 0;
}).map(v => v.tagId).join(',');
} else {
this.params.value = this.$refs.tree.getCheckedKeys(true).join(',');
this.params.value = this.$refs.tree.getCheckedKeys().join(',');
if (!this.params.value) {
this.$message({type:"warning",message:"门店选择不能为空"});
return;
}
}
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(this.params)).then(res => {
this.getRightList();
......
......@@ -46,7 +46,7 @@
<span class="gray-color dm-store__inputtag--tips" v-show="rightList.length === 0">请选择门店</span>
</div>
<el-popover placement="top-start" popper-class="select-shop__popper" width="300" trigger="hover">
<el-tag class="select-shop__popper--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
<el-tag class="dm-store__total--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
{{v.name}}
</el-tag>
<span slot="reference" class="dm-store__inputtag--total" v-show="rightList.length">{{params.isAll?rightTotal:rightList.length}}</span>
......@@ -125,6 +125,10 @@ export default {
}).map(v => v.storeId).join(',');
} else {
this.params.value = this.leftCheckList.join(',');
if (!this.params.value) {
this.$message({type:"warning",message:"门店选择不能为空"});
return;
}
}
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(this.params)).then(res => {
this.getRightList();
......
......@@ -43,7 +43,7 @@
<span class="gray-color dm-store__inputtag--tips" v-show="rightList.length === 0">请选择门店</span>
</div>
<el-popover placement="top-start" popper-class="select-shop__popper" width="300" trigger="hover">
<el-tag class="select-shop__popper--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
<el-tag class="dm-store__total--tag" v-for="v in rightList" :key="v.id" @close="deleteRightItems(v.id)" closable>
{{v.name}}
</el-tag>
<span slot="reference" class="dm-store__inputtag--total" v-show="rightList.length">{{params.isAll?rightTotal:rightList.length}}</span>
......@@ -124,6 +124,10 @@ export default {
}).map(v => v.tagId).join(',');
} else {
this.params.value = this.leftCheckList.join(',');
if (!this.params.value) {
this.$message({type:"warning",message:"门店选择不能为空"});
return;
}
}
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(this.params)).then(res => {
this.getRightList();
......
......@@ -15,7 +15,7 @@
<span class="gray-color dm-store__inputtag--tips" v-show="checkedList.length === 0">请选择门店</span>
</div>
<el-popover placement="top-start" popper-class="select-shop__popper" width="300" trigger="hover">
<el-tag v-for="(v,i) in checkedList" class="select-shop__popper--tag" size="small" :key="i" closable @close="delItem(i)">{{filterLabel(v)}}</el-tag>
<el-tag v-for="(v,i) in checkedList" class="dm-store__total--tag" size="small" :key="i" closable @close="delItem(i)">{{filterLabel(v)}}</el-tag>
<span slot="reference" class="dm-store__inputtag--total" v-show="checkedList.length">{{checkedList.length}}</span>
</el-popover>
</div>
......@@ -88,6 +88,10 @@ export default {
this.params.key = this.uuid;
let valueList = [];
this.params.value = this.checkedList.join(',');
if (!this.params.value) {
this.$message({type:"warning",message:"门店选择不能为空"});
return;
}
this.axios.post(baseUrl + '/api-plug/save-store-widget?requestProject=gic-web',qs.stringify(this.params)).then(res => {
this.popoverShow = false;
}).catch(err => {
......
File mode changed from 100644 to 100755
<template>
<div style="background:#fff;">
<div>
<!-- <div>
<p>新版门店</p>
<vue-gic-store-new style="padding:20px;" :uuid="uuid"></vue-gic-store-new>
</div>
<div>
<vue-gic-store-new style="padding:20px;" :uuid="uuid" :isAdd="false"></vue-gic-store-new>
</div> -->
<!-- <div>
<p>新版卡券门店</p>
<vue-gic-store-card style="padding:20px;" :uuid="uuid"></vue-gic-store-card>
</div>
<div>
<p>老版门店</p>
<vue-gic-store-linkage style="padding:20px;" :msg="sendChildData"></vue-gic-store-linkage>
</div>
</div> -->
<div style="padding:20px;">
<p>营销事件组件</p>
<dm-marketing></dm-marketing>
<dm-marketing ecmPlanId="ff80808166817b0501669f9546db011f"></dm-marketing>
</div>
</div>
</template>
......@@ -44,6 +44,11 @@ export default {
storeIds:[],
},
}
},
created() {
setTimeout(_ => {
this.uuid = 'asdfghjkasdfghjkasdfghjkasdfghjk'
},3000)
}
}
</script>
......
......@@ -55,6 +55,22 @@ export default {
}
}
},
/*
* 一个汉字算一个字,两个英文/字母算一个字
*/
getByteVal2: function(val) {
var returnValue = '';
var byteValLen = 0;
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/ig) != null)
byteValLen += 1;
else
byteValLen += 0.5;
returnValue += val[i];
}
return returnValue;
},
/*
* 限制字数用, 一个汉字算一个字,两个英文/字母算一个字
......
......@@ -116,7 +116,7 @@ export default {
data () {
return {
loading:false,
effectActionOptions:[{value:'subscribe',label:'关注触发'},{value:'authentication',label:'认证触发'}],
effectActionOptions:[{value:'subscribe',label:'关注触发'},{value:'authentication',label:'认证触发'},{value:'consume',label:'消费触发'}],
dayOptions:[
{value:0,label:'0:00'},
{value:1,label:'1:00'},
......
<template>
<section class="dm-wrap">
<div class="pb22 clearfix">
<el-select class="dm-select" clearable="" v-model="listParams.effectType" placeholder="选择时效" @change="search">
<el-option v-for="(v,i) in effectTypeOption" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.marketingType" placeholder="选择营销方式" @change="search">
<el-option v-for="(v,i) in marketingTypeOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.sceneSettingId" placeholder="选择营销场景" @change="search">
<el-option v-for="(v,i) in sceneSettingIdOptions" :key="v.sceneSettingId" :label="v.sceneName" :value="v.sceneSettingId"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.onlineStatus" placeholder="选择上线状态" @change="search">
<el-option v-for="(v,i) in onlineOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-input v-model="listParams.searchName" class="w200" placeholder="输入计划名称" clearable @change="search"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-button class="fr" type="primary" @click="$router.push('/ecm/add')">新建计划</el-button>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中" >
<el-table-column v-for="(v,i) in tableHeader" :fixed="v.fixed" :show-overflow-tooltip="v.tooltip" :width="v.width" :min-width="v.minWidth" :align="v.align" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter">
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{scope.row[v.prop]}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="left" width="220" fixed="right">
<template slot-scope="scope" >
<dm-delete v-if="scope.row.onlineStatus === 1" @confirm="offlineEcmPlan(scope.row)" tips="是否下线该计划?">
<el-button type="text">下线</el-button>
</dm-delete>
<el-button type="text" @click="editData(scope.row)">编辑</el-button>
<dm-delete v-if="scope.row.putonStatus !== 2" @confirm="delData(scope.row)" tips="是否删除该计划?">
<el-button type="text">删除</el-button>
</dm-delete>
<el-button type="text" @click="editData(scope.row)">记录</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></el-pagination>
</section>
</template>
<script>
import {ecmBatchSendInfos,ecmBatchSendDetails,deleteEcm,offlineEcmPlan} from '@/service/api/ecmApi.js';
import {sceneSettingList} from '@/service/api/commonApi.js';
const marketingTypeOptions = [{value:'',label:'所有发送类型'},{value:'card',label:'卡券营销'} ,{value:'message',label:'短信营销'} ,{value:'teletext',label:'图文营销'},{value:'text',label:'文本营销'},{value:'teltask',label:'话务'},{value:'image',label:'微信图片'}];
export default {
name: 'ecm',
data () {
return {
effectTypeOption:[{value:-1,label:'所有时效'},{value:0,label:'实时'},{value:1,label:'定时'}],
onlineOptions:[{value:'',label:'所有上线状态'},{value:0,label:'待上线'},{value:1,label:'已上线'},{value:2,label:'已下线'}],
sceneSettingIdOptions:[],
marketingTypeOptions,
infoParams:{
},
listParams:{
effectType:-1,
marketingType:'',
sceneSettingId:'',
onlineStatus:'',
searchName:'',
currentPage:1,
pageSize:20
},
total:0,
loading:false,
tableHeader:[
{label:'计划名称',prop:'ecmPlanName',minWidth:'120',align:'left',fixed:'left'},
{label:'时效',prop:'effectType',width:'120',align:'left',formatter(row){
return row.effectType?(`<span><i class="el-icon-time fz18 vertical-middle"></i><span class="vertical-middle"> 每天${row.effectTime}:00</span></span>`):'实时'
}},
{label:'营销方式',prop:'marketingType',minWidth:'120',align:'left'},
{label:'营销场景',prop:'sceneSettingName',width:'120',align:'left'},
{label:'营销人次',prop:'timesForPeople',width:'120',align:'left'},
{label:'上线状态',prop:'onlineStatus',width:'120',align:'left',formatter(row){
let result = '--';
switch (row.onlineStatus) {
case 0:
result = '<span class="dm-status--primary">待上线</span>';
break;
case 1:
result = '<span class="dm-status--success">已上线</span>';
break;
case 2:
result = '<span class="dm-status--info">已下线</span>';
break;
}
return result;
}},
],
tableList:[]
}
},
created() {
this.ecmBatchSendDetails();
this.sceneSettingList();
this.$store.commit('aside_handler',false);
this.$store.commit('mutations_breadcrumb',[{name:'营销管理',path:''},{name:'智能营销',path:'/ecm'}]);
},
methods: {
search() {
this.listParams.currentPage = 1;
this.ecmBatchSendDetails();
},
handleSizeChange(val) {
this.listParams.pageSize = val;
this.ecmBatchSendDetails();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.ecmBatchSendDetails();
},
async ecmBatchSendDetails() {
this.loading = true;
let res = await ecmBatchSendDetails(this.listParams);
console.log(res)
this.tableList = res.result.result || [];
this.total = res.result.totalCount;
this.loading = false;
},
//编辑
editData(row){
this.$router.push('/ecm/edit/'+row.ecmPlanId)
},
// 删除
async delData(row){
try {
let res = await deleteEcm({ecmPlanId:row.ecmPlanId});
if (res.errorCode === 0) {
this.$tips({type: 'success',message: '删除成功!'});
this.ecmBatchSendDetails();
} else {
this.$tips({type: 'error',message: res.message || '删除失败!'});
}
} catch (err) {
this.$tips({type: 'error',message: '删除失败!'});
}
},
// 下线
async offlineEcmPlan(row){
try {
let res = await offlineEcmPlan({ecmPlanId:row.ecmPlanId});
if (res.errorCode === 0) {
this.$tips({type: 'success',message: '下线成功!'});
this.ecmBatchSendDetails();
} else {
this.$tips({type: 'error',message: res.message || '下线失败!'});
}
} catch (err) {
this.$tips({type: 'error',message: '下线失败!'});
}
},
async sceneSettingList() {
let res = await sceneSettingList();
this.sceneSettingIdOptions = res.result;
this.sceneSettingIdOptions.unshift({sceneName:'所有营销场景',sceneSettingId:''})
}
}
}
</script>
<template>
<section class="dm-wrap">
<div class="pb22 clearfix">
<el-select class="dm-select" clearable="" v-model="listParams.effectType" placeholder="选择时效" @change="search">
<el-option v-for="(v,i) in effectTypeOption" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.marketingType" placeholder="选择营销方式" @change="search">
<el-option v-for="(v,i) in marketingTypeOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.sceneSettingId" placeholder="选择营销场景" @change="search">
<el-option v-for="(v,i) in sceneSettingIdOptions" :key="v.sceneSettingId" :label="v.sceneName" :value="v.sceneSettingId"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.onlineStatus" placeholder="选择上线状态" @change="search">
<el-option v-for="(v,i) in onlineOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-input v-model="listParams.searchName" class="w200" placeholder="输入计划名称" clearable @change="search"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-button class="fr" type="primary" @click="$router.push('/ecm/add')">新建计划</el-button>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中" >
<el-table-column v-for="(v,i) in tableHeader" :fixed="v.fixed" :show-overflow-tooltip="v.tooltip" :width="v.width" :min-width="v.minWidth" :align="v.align" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter">
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{scope.row[v.prop]}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="left" width="220" fixed="right">
<template slot-scope="scope" >
<dm-delete v-if="scope.row.onlineStatus === 1" @confirm="offlineEcmPlan(scope.row)" tips="是否下线该计划?">
<el-button type="text">下线</el-button>
</dm-delete>
<el-button type="text" @click="editData(scope.row)">编辑</el-button>
<dm-delete v-if="scope.row.putonStatus !== 2" @confirm="delData(scope.row)" tips="是否删除该计划?">
<el-button type="text">删除</el-button>
</dm-delete>
<el-button type="text" @click="editData(scope.row)">记录</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></el-pagination>
</section>
</template>
<script>
import {loadEcmList,deleteEcm,offlineEcmPlan} from '@/service/api/ecmApi.js';
import {sceneSettingList} from '@/service/api/commonApi.js';
const marketingTypeOptions = [{value:'',label:'所有发送类型'},{value:'card',label:'卡券营销'} ,{value:'message',label:'短信营销'} ,{value:'teletext',label:'图文营销'},{value:'text',label:'文本营销'},{value:'teltask',label:'话务'},{value:'image',label:'微信图片'}];
export default {
name: 'ecm',
data () {
return {
effectTypeOption:[{value:-1,label:'所有时效'},{value:0,label:'实时'},{value:1,label:'定时'}],
onlineOptions:[{value:'',label:'所有上线状态'},{value:0,label:'待上线'},{value:1,label:'已上线'},{value:2,label:'已下线'}],
sceneSettingIdOptions:[],
marketingTypeOptions,
listParams:{
effectType:-1,
marketingType:'',
sceneSettingId:'',
onlineStatus:'',
searchName:'',
currentPage:1,
pageSize:20
},
total:0,
loading:false,
tableHeader:[
{label:'计划名称',prop:'ecmPlanName',minWidth:'120',align:'left',fixed:'left'},
{label:'时效',prop:'effectType',width:'120',align:'left',formatter(row){
return row.effectType?(`<span><i class="el-icon-time fz18 vertical-middle"></i><span class="vertical-middle"> 每天${row.effectTime}:00</span></span>`):'实时'
}},
{label:'营销方式',prop:'marketingType',minWidth:'120',align:'left'},
{label:'营销场景',prop:'sceneSettingName',width:'120',align:'left'},
{label:'营销人次',prop:'timesForPeople',width:'120',align:'left'},
{label:'上线状态',prop:'onlineStatus',width:'120',align:'left',formatter(row){
let result = '--';
switch (row.onlineStatus) {
case 0:
result = '<span class="dm-status--primary">待上线</span>';
break;
case 1:
result = '<span class="dm-status--success">已上线</span>';
break;
case 2:
result = '<span class="dm-status--info">已下线</span>';
break;
}
return result;
}},
],
tableList:[]
}
},
created() {
this.loadEcmList();
this.sceneSettingList();
this.$store.commit('aside_handler',false);
this.$store.commit('mutations_breadcrumb',[{name:'营销管理',path:''},{name:'智能营销',path:'/ecm'}]);
},
methods: {
search() {
this.listParams.currentPage = 1;
this.loadEcmList();
},
handleSizeChange(val) {
this.listParams.pageSize = val;
this.loadEcmList();
},
handleCurrentChange(val) {
this.listParams.currentPage = val;
this.loadEcmList();
},
async loadEcmList() {
this.loading = true;
let res = await loadEcmList(this.listParams);
console.log(res)
this.tableList = res.result.result || [];
this.total = res.result.totalCount;
this.loading = false;
},
//编辑
editData(row){
this.$router.push('/ecm/edit/'+row.ecmPlanId)
},
// 删除
async delData(row){
try {
let res = await deleteEcm({ecmPlanId:row.ecmPlanId});
if (res.errorCode === 0) {
this.$tips({type: 'success',message: '删除成功!'});
this.loadEcmList();
} else {
this.$tips({type: 'error',message: res.message || '删除失败!'});
}
} catch (err) {
this.$tips({type: 'error',message: '删除失败!'});
}
},
// 下线
async offlineEcmPlan(row){
try {
let res = await offlineEcmPlan({ecmPlanId:row.ecmPlanId});
if (res.errorCode === 0) {
this.$tips({type: 'success',message: '下线成功!'});
this.loadEcmList();
} else {
this.$tips({type: 'error',message: res.message || '下线失败!'});
}
} catch (err) {
this.$tips({type: 'error',message: '下线失败!'});
}
},
async sceneSettingList() {
let res = await sceneSettingList();
this.sceneSettingIdOptions = res.result;
this.sceneSettingIdOptions.unshift({sceneName:'所有营销场景',sceneSettingId:''})
}
}
}
</script>
......@@ -8,24 +8,32 @@
<el-option v-for="(v,i) in marketingTypeOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.sceneSettingId" placeholder="选择营销场景" @change="search">
<el-option v-for="item in sceneSettingIdOptions" :key="item.sceneSettingId" :label="item.sceneName" :value="item.sceneSettingId"></el-option>
<el-option v-for="(v,i) in sceneSettingIdOptions" :key="v.sceneSettingId" :label="v.sceneName" :value="v.sceneSettingId"></el-option>
</el-select>
<el-select class="dm-select" clearable v-model="listParams.onlineStatus" placeholder="选择上线状态" @change="search">
<el-option v-for="(v,i) in onlineOptions" :key="i" :label="v.label" :value="v.value"></el-option>
</el-select>
<el-input v-model="listParams.searchName" class="w200" placeholder="输入计划名称" clearable @change="search"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-button class="fr" type="primary" @click="$router.push('/ecm/add')">新建计划</el-button>
</div>
<el-table tooltipEffect="light" :data="tableList" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中" >
<el-table-column v-for="(v,i) in tableHeader" :fixed="v.fixed" :show-overflow-tooltip="v.tooltip" :width="v.width" :min-width="v.minWidth" :align="v.align" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter"></el-table-column>
<el-table-column label="状态" align="left" min-width="150px" prop="planInfo">
<template slot-scope="scope" >
<span type="text" >{{scope.row.planInfo || '--'}}</span>
<el-table-column v-for="(v,i) in tableHeader" :fixed="v.fixed" :show-overflow-tooltip="v.tooltip" :width="v.width" :min-width="v.minWidth" :align="v.align" :key="i" :prop="v.prop" :label="v.label" :formatter="v.formatter">
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{scope.row[v.prop]}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="left" width="120px" fixed="right">
<el-table-column label="操作" align="left" width="220" fixed="right">
<template slot-scope="scope" >
<dm-delete v-if="scope.row.onlineStatus === 1" @confirm="offlineEcmPlan(scope.row)" tips="是否下线该计划?">
<el-button type="text">下线</el-button>
</dm-delete>
<el-button type="text" @click="editData(scope.row)">编辑</el-button>
<dm-delete v-if="scope.row.putonStatus !== 2" @confirm="delData(scope.row)" tips="是否删除该计划?">
<el-button type="text">删除</el-button>
</dm-delete>
<el-button type="text" @click="toRecord(scope.row)">记录</el-button>
</template>
</el-table-column>
......@@ -34,7 +42,7 @@
</section>
</template>
<script>
import {loadEcmList,deleteEcm} from '@/service/api/ecmApi.js';
import {loadEcmList,deleteEcm,offlineEcmPlan} from '@/service/api/ecmApi.js';
import {sceneSettingList} from '@/service/api/commonApi.js';
const marketingTypeOptions = [{value:'',label:'所有发送类型'},{value:'card',label:'卡券营销'} ,{value:'message',label:'短信营销'} ,{value:'teletext',label:'图文营销'},{value:'text',label:'文本营销'},{value:'teltask',label:'话务'},{value:'image',label:'微信图片'}];
export default {
......@@ -42,12 +50,14 @@ export default {
data () {
return {
effectTypeOption:[{value:-1,label:'所有时效'},{value:0,label:'实时'},{value:1,label:'定时'}],
onlineOptions:[{value:'',label:'所有上线状态'},{value:0,label:'待上线'},{value:1,label:'已上线'},{value:2,label:'已下线'}],
sceneSettingIdOptions:[],
marketingTypeOptions,
listParams:{
effectType:-1,
marketingType:'',
sceneSettingId:'',
onlineStatus:'',
searchName:'',
currentPage:1,
pageSize:20
......@@ -56,20 +66,27 @@ export default {
loading:false,
tableHeader:[
{label:'计划名称',prop:'ecmPlanName',minWidth:'120',align:'left',fixed:'left'},
{label:'时效',prop:'effectType',width:'120',align:'left',formatter:function(row){
return row.effectType?('每天'+row.effectTime+':00'):'实时'
{label:'时效',prop:'effectType',width:'120',align:'left',formatter(row){
return row.effectType?(`<span><i class="el-icon-time fz18 vertical-middle"></i><span class="vertical-middle"> 每天${row.effectTime}:00</span></span>`):'实时'
}},
{label:'营销方式',prop:'marketingType',width:'120',align:'left',formatter:function(row){
let result = '';
marketingTypeOptions.forEach(v => {
if (v.value === row.marketingType) {
result = v.label
}
})
{label:'营销方式',prop:'marketingType',minWidth:'120',align:'left'},
{label:'营销场景',prop:'sceneSettingName',width:'120',align:'left'},
{label:'营销人次',prop:'timesForPeople',width:'120',align:'left'},
{label:'上线状态',prop:'onlineStatus',width:'120',align:'left',formatter(row){
let result = '--';
switch (row.onlineStatus) {
case 0:
result = '<span class="dm-status--primary">待上线</span>';
break;
case 1:
result = '<span class="dm-status--success">已上线</span>';
break;
case 2:
result = '<span class="dm-status--info">已下线</span>';
break;
}
return result;
}},
{label:'营销场景',prop:'sceneSettingName',width:'100',align:'left'},
{label:'营销人次',prop:'timesForPeople',width:'80',align:'left'}
],
tableList:[]
}
......@@ -105,6 +122,11 @@ export default {
editData(row){
this.$router.push('/ecm/edit/'+row.ecmPlanId)
},
// 记录
toRecord(row){
this.$router.push('/ecm/'+ (row.effectType ? 'batchlist' : 'currentlist'));
},
// 删除
async delData(row){
try {
let res = await deleteEcm({ecmPlanId:row.ecmPlanId});
......@@ -118,6 +140,20 @@ export default {
this.$tips({type: 'error',message: '删除失败!'});
}
},
// 下线
async offlineEcmPlan(row){
try {
let res = await offlineEcmPlan({ecmPlanId:row.ecmPlanId});
if (res.errorCode === 0) {
this.$tips({type: 'success',message: '下线成功!'});
this.loadEcmList();
} else {
this.$tips({type: 'error',message: res.message || '下线失败!'});
}
} catch (err) {
this.$tips({type: 'error',message: '下线失败!'});
}
},
async sceneSettingList() {
let res = await sceneSettingList();
this.sceneSettingIdOptions = res.result;
......
......@@ -140,6 +140,7 @@ export default {
wechatTextId:'',
wechatTeletextId:'',
content:'',
textNum:0,
mediaId:'',
},
memberList:[],
......@@ -169,12 +170,7 @@ export default {
this.$store.commit('mutations_layoutTips','');
},
methods:{
// 微信文本
getWxText(data){
this.info.content = data.textarea;
console.log(data)
this.info.imageMediaId = data.imageMediaId;
},
async getWechatMarketingDetail() {
let res = await getWechatMarketingDetail({wechatTeletextId:this.$route.params.id});
if (res.errorCode === 0 && res.result.wechatDTO) {
......@@ -264,6 +260,13 @@ export default {
getImgData(val) {
this.info.mediaId = val
},
// 微信文本
getWxText(data){
console.log(data)
this.info.textNum = data.textNum;
this.info.content = data.textarea;
this.info.imageMediaId = data.imageMediaId;
},
submit() {
if (!this.info.memberType && this.toggleTag && !this.getSaveData ) {
......@@ -286,6 +289,16 @@ export default {
return;
}
}
if (this.info.contentType === 1 ) {
console.log(this.info.textNum)
if(this.info.textNum >= 300){
this.$tips({type:'warning',message:'最多300个字符哦'});
return
}
var reg = /<(?!(a|\/a|div|\/div)).*?>/img ; // 只保留a 和 div 标签
this.info.content = this.info.content.replace(reg,"").replace(/<\/div>|&nbsp;/g,"").replace(/<((div)).*?>/g,"<br>").split('<br>').filter(v=>v).join('<br>');
}
// } else {
// if (!this.info.memberSearchDTO) {
// this.$tips({type:'warning',message:'会员筛选不能为空'});
......
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