Browse Source

feat

- 添加群发
  - 增加 动态创建消息模板
- 群发列表
  - 修复 分页异常的问题
feat
JinxChen 2 years ago
parent
commit
f381e209bd
6 changed files with 265 additions and 78 deletions
  1. +11
    -2
      README.md
  2. +16
    -1
      src/api/wechat-fans.js
  3. +15
    -2
      src/components/TTable/TTable.vue
  4. +2
    -2
      src/utils/model.js
  5. +200
    -57
      src/views/message-manage/main/add-mass/index.vue
  6. +21
    -14
      src/views/message-manage/main/mass-list/index.vue

+ 11
- 2
README.md View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Date: 2021-11-29 11:14:13 * @Date: 2021-11-29 11:14:13
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-20 09:21:28
* @LastEditTime: 2022-09-21 14:40:58
* @FilePath: \TelpoUserManageAdmin\README.md * @FilePath: \TelpoUserManageAdmin\README.md
* @description: * @description:
--> -->
@@ -112,4 +112,13 @@ fix
- 添加群发 - 添加群发
- 群发列表 - 群发列表
- 修复 分页异常的问题 - 修复 分页异常的问题
- 修复 预览异常的问题
- 修复 预览异常的问题


## v1.0.7
`2022.9.21`
feat
- 添加群发
- 增加 动态创建消息模板
- 群发列表
- 修复 分页异常的问题

+ 16
- 1
src/api/wechat-fans.js View File

@@ -1,7 +1,7 @@
/* /*
* @Date: 2022-09-15 16:40:39 * @Date: 2022-09-15 16:40:39
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-19 16:50:23
* @LastEditTime: 2022-09-20 11:25:01
* @FilePath: \TelpoUserManageAdmin\src\api\wechat-fans.js * @FilePath: \TelpoUserManageAdmin\src\api\wechat-fans.js
* @description: * @description:
*/ */
@@ -13,6 +13,8 @@ export const APIWechatFans = {
getArticlesGroup, //获取文章列表 getArticlesGroup, //获取文章列表
getGroupSenderDetails, //获取单个群发记录 getGroupSenderDetails, //获取单个群发记录
deleteGroupSender, //删除单个群发记录 deleteGroupSender, //删除单个群发记录
getTemplateMsgList, //获取模板消息列表
getTemplateById, //根据模板id获取对应的模板消息
}; };
export default APIWechatFans; export default APIWechatFans;


@@ -64,3 +66,16 @@ function deleteGroupSender(id) {
method: 'delete', method: 'delete',
}); });
} }
function getTemplateMsgList(){
return requestWxFans({
url: `/GroupSender/TemplateMsgTemplates`,
method: 'get',
});
}

function getTemplateById(id){
return requestWxFans({
url: `/GroupSender/TemplateMsgTemplates/${id}`,
method: 'get',
});
}

+ 15
- 2
src/components/TTable/TTable.vue View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Date: 2021-11-30 17:19:51 * @Date: 2021-11-30 17:19:51
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-17 14:42:20
* @LastEditTime: 2022-09-21 14:31:06
* @FilePath: \TelpoUserManageAdmin\src\components\TTable\TTable.vue * @FilePath: \TelpoUserManageAdmin\src\components\TTable\TTable.vue
* @description: 封装通用的table组件 * @description: 封装通用的table组件
--> -->
@@ -16,9 +16,16 @@
:prop="column.prop" :prop="column.prop"
:label="column.title" :label="column.title"
:fixed="column.fixed" :fixed="column.fixed"
v-if="!column.action"
v-if="!column.action && !column.isCustom"
height="400" height="400"
/> />
<el-table-column :label="column.title" :show-overflow-tooltip="true" :key="column.prop" v-else-if="column.isCustom" fixed="right" min-width="100">
<template slot-scope="scope">
<div>
<p @click="handleClick(scope.row, column.fnName)" class="details-underline">{{scope.row.template_content}}</p>
</div>
</template>
</el-table-column>
<!-- 操作列 --> <!-- 操作列 -->
<el-table-column :label="column.title" :key="column.prop" v-else fixed="right" min-width="100"> <el-table-column :label="column.title" :key="column.prop" v-else fixed="right" min-width="100">
<template slot-scope="scope"> <template slot-scope="scope">
@@ -97,4 +104,10 @@ export default {
.el-table { .el-table {
font-size: 14px; font-size: 14px;
} }
.details-underline {
text-decoration: underline;
&:hover {
cursor: pointer;
}
}
</style> </style>

+ 2
- 2
src/utils/model.js View File

@@ -1,8 +1,8 @@
/* /*
* @Date: 2021-11-30 15:09:25 * @Date: 2021-11-30 15:09:25
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-20 09:20:40
* @LastEditTime: 2022-09-21 14:18:59
* @FilePath: \TelpoUserManageAdmin\src\utils\model.js * @FilePath: \TelpoUserManageAdmin\src\utils\model.js
* @description: 版本号 * @description: 版本号
*/ */
export const VersionModel = '1.0.6';
export const VersionModel = '1.0.7';

+ 200
- 57
src/views/message-manage/main/add-mass/index.vue View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Date: 2022-08-08 10:09:47 * @Date: 2022-08-08 10:09:47
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-20 09:20:19
* @LastEditTime: 2022-09-21 14:43:45
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\add-mass\index.vue * @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\add-mass\index.vue
* @description: 添加群发 * @description: 添加群发
--> -->
@@ -14,41 +14,26 @@
<el-form-item label="消息主题:" size="small" prop="subject"> <el-form-item label="消息主题:" size="small" prop="subject">
<el-input v-model="form.subject" class="input-width-400" clearable ></el-input> <el-input v-model="form.subject" class="input-width-400" clearable ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="内容模板:" size="small" required>


<el-form-item prop="first">
<p>{{firstData}}</p>
<el-input v-model="form.first" class="input-width-600" clearable prop="first"></el-input>
</el-form-item>

<el-form-item prop="keyword1">
<p>消息类别{{keyword1Data}}</p>
<el-input v-model="form.keyword1" class="input-width-400" clearable prop="keyword1"></el-input>
</el-form-item>

<el-form-item prop="keyword2">
<p>通知用户{{keyword2Data}}</p>
<el-input v-model="form.keyword2" class="input-width-400" clearable prop="keyword1"></el-input>
</el-form-item>

<el-form-item prop="keyword3">
<p>通知内容{{keyword3Data}}</p>
<el-input v-model="form.keyword3" class="input-width-600" clearable prop="keyword1"></el-input>
</el-form-item>

<el-form-item prop="remark">
<p>{{remarkData}}</p>
<el-input v-model="form.remark" class="input-width-600" clearable prop="remark"></el-input>
<el-form-item label="选择模板:" size="small" prop="tempId">
<!-- 动态生成模板 -->
<el-select v-model="form.tempId" placeholder="选择模板" clearable filterable @change="onSelectTemp">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in templateMsgList" :key="index"/>
</el-select>
</el-form-item>
<el-form-item label="模板内容:" size="small" required>
<el-form-item v-for="(item, index) in templateMsgCon" :key="index">
<el-form-item>
<p>{{item.name}}</p>
<el-input v-model="templateInput[item.key]" class="input-width-600" clearable type="textarea" autosize></el-input>
</el-form-item>
</el-form-item> </el-form-item>
</el-form-item> </el-form-item>

<el-form-item label="内容链接:" size="small" class="inline-form-item" prop="articleContent" required>
<el-form-item prop="remark">
<el-input v-model="form.articleContent" placeholder="请选择文章或者输入外部URL" class="input-width-400"></el-input>
<el-select v-model="article" placeholder="选择文章" clearable filterable @change="onSelectArt">
<el-form-item label="内容链接:" size="small" class="inline-form-item" required>
<el-form-item prop="articleContent" >
<el-input v-model="form.articleContent" placeholder="请输入正确的外部URL" class="input-width-400" clearable @clear="onClearUrl"></el-input>
<!-- <el-select v-model="article" placeholder="选择文章" clearable filterable @change="onSelectArt" @clear="onClearSelect">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in articleList" :key="index"/> <el-option :label="item.label" :value="item.value" v-for="(item, index) in articleList" :key="index"/>
</el-select>
</el-select> -->
</el-form-item> </el-form-item>
</el-form-item> </el-form-item>


@@ -68,14 +53,14 @@
<el-dialog title="预览信息" :visible.sync="previewShow"> <el-dialog title="预览信息" :visible.sync="previewShow">
<div class="preview-container"> <div class="preview-container">
<div class="top"> <div class="top">
<p>{{form.first}}</p>
<p>消息类别:{{form.keyword1}}</p>
<p>通知用户:{{form.keyword2}}</p>
<p>通知内容:{{form.keyword3}}</p>
<p>更新时间:{{initUpdateTime}}</p>
<p>{{templateInput.first}}</p>
<p v-for="(item, index) in previewData" :key="index">
{{item.name}}:{{item.value}}
</p>
<p>{{templateInput.remark}}</p>
</div> </div>
<div class="fot">
<p>{{form.remark}}</p>
<div class="fot" @click="onOpenUrl">
<p>详情</p>
<p>></p> <p>></p>
</div> </div>
</div> </div>
@@ -95,6 +80,16 @@ export default {
name: 'add-mass', name: 'add-mass',
components: { }, components: { },
data(){ data(){
const checkUrl = (rule, value, callback) => {
let urlrReg = /^((https?|ftp):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\/\w\.-]*)*\/?/;
if(urlrReg.test(value)) {
callback();
} else if (this.form.articleId !== '') {
callback();
} else {
callback(`请输入正确的网址`);
}
};
return { return {
buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}} buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
firstData: '{{first.DATA}}:', firstData: '{{first.DATA}}:',
@@ -113,6 +108,7 @@ export default {
keyword2: '' /* || '财商36问' */, //更新内容 keyword2: '' /* || '财商36问' */, //更新内容
keyword3: '', keyword3: '',
remark: '' /* || '请点击内容查看详情' */, //内容详情 remark: '' /* || '请点击内容查看详情' */, //内容详情
tempId: '' || '-kOhlwJPhO7v3WW3rAdmNs76lR7ZpQEJtbVSY8Y35Eg', //模板id
}, },
article: '', //选择文章 article: '', //选择文章
// 文章列表 // 文章列表
@@ -128,12 +124,17 @@ export default {
], ],
// 预览dialog // 预览dialog
previewShow: false, previewShow: false,
// 要预览的页面字段
previewData: [],
// form验证 // form验证
rules: { rules: {
subject: [ subject: [
{ required: true, message: '请输入消息主题', trigger: 'blur' }, { required: true, message: '请输入消息主题', trigger: 'blur' },
], ],
first: [
tempId: [
{ required: true, message: '请选择模板消息', trigger: 'blur' },
],
/* first: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' }, { required: true, message: '请填写消息模板内容', trigger: 'blur' },
], ],
keyword1: [ keyword1: [
@@ -145,35 +146,70 @@ export default {
keyword3: [ keyword3: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' }, { required: true, message: '请填写消息模板内容', trigger: 'blur' },
], ],
remark: [
keyword4: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' }, { required: true, message: '请填写消息模板内容', trigger: 'blur' },
], ],
messageSubject: [
keyword5: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' }, { required: true, message: '请填写消息模板内容', trigger: 'blur' },
], ],
remark: [
{ required: true, message: '请填写消息模板内容', trigger: 'blur' },
], */
articleContent: [ articleContent: [
{ required: true, message: '请选择文章获取填写外部URL', trigger: 'blur' },
{ required: true, validator: checkUrl, message: '请输入正确的外部URL', trigger: 'blur' },
], ],
selfGroupId: [ selfGroupId: [
{ required: true, message: '请选择群发分组', trigger: 'blur' }, { required: true, message: '请选择群发分组', trigger: 'blur' },
], ],
}
},
// 模板消息列表
templateMsgList: [],
// 模板内容
templateMsgCon: [
{
key: 'first',
name: '{{first.DATA}}',
},
{
key: 'keyword1',
name: '消息类别:{{keyword1.DATA}}',
},
{
key: 'keyword2',
name: '通知用户:{{keyword2.DATA}}',
},
{
key: 'keyword3',
name: '通知内容:{{keyword3.DATA}}',
},
{
key: 'remark',
name: '{{remark.DATA}}'
}
],
// 动态生成的input
templateInput: {},
tempData: [] || [
{first: 'first', remark: 'remark'}
]
} }
}, },
computed: { computed: {
// 初始弹窗更新时间 // 初始弹窗更新时间
initUpdateTime() { initUpdateTime() {
return initTime(new Date(), 'ymdhm'); //更新时间 return initTime(new Date(), 'ymdhm'); //更新时间
}
},
}, },
activated() { activated() {
this.getSelfGroups(); this.getSelfGroups();
this.getArticlesGroup(); this.getArticlesGroup();
this.getTemplateMsgList();
}, },
mounted() { mounted() {
}, },
created() { created() {
this.getSelfGroups(); this.getSelfGroups();
this.getTemplateMsgList();
this.getArticlesGroup(); this.getArticlesGroup();
}, },
methods: { methods: {
@@ -215,23 +251,39 @@ export default {
}, },
// 查看白名单 // 查看白名单
onCheckList() {}, onCheckList() {},
// objectToArray
objectToArray(obj) {
let arr = [];
for(let i in obj) {
// arr.push (obj[i] ) //返回属性值
arr.push(obj[i]) //返回键名
};
return arr;
},
// 立即发送 // 立即发送
onSend() { onSend() {
this.tempData.push(this.templateInput);
console.log("tempData", JSON.stringify(this.templateInput));
// 拼接所需字段 // 拼接所需字段
let keywordStr = this.form.keyword1 + ',' + this.form.keyword2 + ',' + this.form.keyword3;
let reqBody = { let reqBody = {
send_now: true, //true 是立即发送, false是保存 send_now: true, //true 是立即发送, false是保存
subject: this.form.subject, subject: this.form.subject,
template_id: '1',
template_id: this.form.tempId,
self_group_id: this.form.selfGroupId, self_group_id: this.form.selfGroupId,
article_id: this.form.articleId || 0, article_id: this.form.articleId || 0,
first: this.form.first,
remark: this.form.remark,
keyword: keywordStr,
mptemplate_content: JSON.stringify(this.templateInput),
url: this.form.articleContent url: this.form.articleContent
}; };
this.$refs['form'].validate((valid) => { this.$refs['form'].validate((valid) => {
if(valid) { if(valid) {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请完善模板内容"
});
return
}
this.addGroupSender(reqBody, '群发'); this.addGroupSender(reqBody, '群发');
} else { } else {
this.$message({ this.$message({
@@ -240,25 +292,43 @@ export default {
}); });
} }
}); });
//this.addGroupSender(reqBody);
},
// 验证动态模板
checkTemplateInput() {
console.log("templateInput", this.templateInput);
// 获取模板输入框的实际数量
let tempInputLen = this.templateMsgCon.length;
console.log("tempInputLen", tempInputLen);
// 计算模板输入的数量
let tempLen = (Object.keys(this.templateInput).length);
if( tempLen !== tempInputLen) {
return false
} else {
return true
}
}, },
// 保存 // 保存
onSave() { onSave() {
// 拼接所需字段 // 拼接所需字段
let keywordStr = this.form.keyword1 + ',' + this.form.keyword2 + ',' + this.form.keyword3;
let reqBody = { let reqBody = {
send_now: false, //true 是立即发送, false是保存 send_now: false, //true 是立即发送, false是保存
subject: this.form.subject, subject: this.form.subject,
template_id: '1',
template_id: this.form.tempId,
self_group_id: this.form.selfGroupId, self_group_id: this.form.selfGroupId,
article_id: this.form.articleId || 0, article_id: this.form.articleId || 0,
first: this.form.first,
remark: this.form.remark,
keyword: keywordStr,
mptemplate_content: JSON.stringify(this.templateInput),
url: this.form.articleContent url: this.form.articleContent
}; };
this.$refs['form'].validate((valid) => { this.$refs['form'].validate((valid) => {
if(valid) { if(valid) {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请完善模板内容"
});
return
}
this.addGroupSender(reqBody, '保存'); this.addGroupSender(reqBody, '保存');
} else { } else {
this.$message({ this.$message({
@@ -298,7 +368,20 @@ export default {
}, },
// 预览 // 预览
onPreview() { onPreview() {
const isCanSave = this.checkTemplateInput();
if(!isCanSave) {
this.$message({
type: "error",
message: "请先完善模板内容"
});
return
}
this.previewShow = true; this.previewShow = true;
this.tempData.push(this.templateInput);
let previewArr = this.objectToArray(this.tempData[0]).slice(1, this.objectToArray(this.tempData[0]).length - 1);
this.previewData.forEach((value, index) => {
value['value'] = previewArr[index];
});
}, },
// 预览确认 // 预览确认
onConfirm() { onConfirm() {
@@ -321,6 +404,62 @@ export default {
this.form.selfGroupId = value; this.form.selfGroupId = value;
console.log("选择的对象分组", value); console.log("选择的对象分组", value);
} }
},
// 获取消息模板列表
getTemplateMsgList() {
APIWechatFans.getTemplateMsgList()
.then(res => {
let telpData = res.data;
this.templateMsgList = telpData.map(item => {
return {
label: item.title,
value: item.template_id
}
});
// 默认显示第一个模板id
this.getTemplateMsgListById(this.templateMsgList[0].value);
})
},
// 根据模板id获取对应模板消息
getTemplateMsgListById(id) {
APIWechatFans.getTemplateById(id)
.then(res => {
console.log("res", res);
let data = res.data;
let splitData = data.content.split('\\n');
this.templateMsgCon = splitData.map(item => {
return {
name: item,
key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
}
});
this.previewData = splitData.splice(1, splitData.length - 2 ).map(item => {
return {
name: item.substring( 0, item.indexOf(":")),
key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
}
});
})
},
// 选择模板消息
onSelectTemp(value) {
console.log("选择了模板消息", value);
this.form.tempId = value;
this.templateInput = {};
this.getTemplateMsgListById(value);
},
// 清空url填写
onClearUrl() {
this.form.articleContent = '';
if( this.form.articleId ) {
this.form.articleId = '';
}
},
onClearSelect() {
this.form.articleContent = '';
},
onOpenUrl() {
window.open(this.form.articleContent);
} }
} }
} }
@@ -381,6 +520,7 @@ export default {
flex-direction: column; flex-direction: column;
padding: 0 40px; padding: 0 40px;
border-bottom: 1px solid #d8dce5 ; border-bottom: 1px solid #d8dce5 ;
overflow-y: scroll;
} }
.fot { .fot {
width: 100%; width: 100%;
@@ -388,6 +528,9 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
&:hover {
cursor: pointer;
}
} }
} }
.preview-btn { .preview-btn {


+ 21
- 14
src/views/message-manage/main/mass-list/index.vue View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Date: 2022-08-08 10:09:50 * @Date: 2022-08-08 10:09:50
* @LastEditors: JinxChen * @LastEditors: JinxChen
* @LastEditTime: 2022-09-20 09:16:28
* @LastEditTime: 2022-09-21 14:34:33
* @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\mass-list\index.vue * @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\mass-list\index.vue
* @description: 群发列表 * @description: 群发列表
--> -->
@@ -20,7 +20,7 @@
/> />
<el-button icon="el-icon-search" @click="onSearch" type="primary" class="search-btn">搜索</el-button> <el-button icon="el-icon-search" @click="onSearch" type="primary" class="search-btn">搜索</el-button>
</div> </div>
<TTable :tableData="dataList" :columns="columns" @details="onDetails" @delete="onDelete" @update="onUpdate"></TTable>
<TTable :tableData="dataList" :columns="columns" @details="onDetails" @delete="onDelete" @update="onUpdate" @openUrl="openUrl"></TTable>
<!-- 分页 --> <!-- 分页 -->
<pagination <pagination
v-show="total > 0" v-show="total > 0"
@@ -68,9 +68,7 @@ export default {
limit: 10 limit: 10
}, },
placeholder: "可输入消息主题", placeholder: "可输入消息主题",
dataList: [

],
dataList: [],
// 群发详细数组 // 群发详细数组
detailsDataList: [], detailsDataList: [],
detailsColumns: [ detailsColumns: [
@@ -80,7 +78,7 @@ export default {
], ],
columns: [ columns: [
{ prop: "subject", title: "消息主题", fixed: "left" }, { prop: "subject", title: "消息主题", fixed: "left" },
{ prop: "template_first", title: "内容摘要(链接详情)" },
{ prop: "template_content", isCustom: true, title: "内容摘要", fnName: 'openUrl' },
{ prop: "self_group_name", title: "发送分组" }, { prop: "self_group_name", title: "发送分组" },
{ prop: "create_time", title: "创建时间" }, { prop: "create_time", title: "创建时间" },
{ {
@@ -146,7 +144,10 @@ export default {
APIWechatFans.getGroupSender(reqBody) APIWechatFans.getGroupSender(reqBody)
.then(res => { .then(res => {
console.log("res::", res); console.log("res::", res);
this.dataList = res.data.rows;
this.dataList = res.data.rows.map(item => {
item.template_content = item.template_content ? JSON.parse(item.template_content).remark : '';
return item
});
this.total = res.data.totals; this.total = res.data.totals;
}) })
}, },
@@ -176,13 +177,14 @@ export default {
type: "success", type: "success",
message: "删除成功" message: "删除成功"
}); });
this.getGroupSender();
} else {
this.$message({
type: "error",
message: `删除失败!${error.message}`
});
} }
}) })
}).catch(error => {
this.$message({
type: "error",
message: `删除失败!${error.message}`
});
}) })
}, },
// 群发明细 // 群发明细
@@ -220,8 +222,8 @@ export default {
}, },
// 分页发生变化 // 分页发生变化
pageChange() { pageChange() {
if (this.searchParams.keywords !== "") {
this.searchParams.keywords = "";
if (this.searchParams.subject !== "") {
this.searchParams.subject = "";
this.getGroupSender(); this.getGroupSender();
} else { } else {
this.getGroupSender(); this.getGroupSender();
@@ -230,6 +232,11 @@ export default {
// 群发明细发生变化 // 群发明细发生变化
pageDetailsChange() { pageDetailsChange() {
this.getGroupSenderDetails(this.massDetailsId); this.getGroupSenderDetails(this.massDetailsId);
},
// 打开链接
openUrl(value) {
window.open(value.url);
console.log("value", value.url);
} }
} }
}; };


Loading…
Cancel
Save