天波用户运营管理后台系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
16KB

  1. <!--
  2. * @Date: 2022-08-08 10:09:47
  3. * @LastEditors: JinxChen
  4. * @LastEditTime: 2023-01-05 14:49:38
  5. * @FilePath: \TelpoUserManageAdmin\src\views\message-manage\main\add-mass\index.vue
  6. * @description: 添加群发
  7. -->
  8. <template>
  9. <div class="home-container">
  10. <div class="form-container">
  11. <!-- 表单 -->
  12. <el-form ref="form" :model="form" label-width="100px" :rules="rules">
  13. <el-form-item label="消息主题:" size="small" prop="subject">
  14. <el-input v-model="form.subject" class="input-width-400" clearable ></el-input>
  15. </el-form-item>
  16. <el-form-item label="选择模板:" size="small" prop="tempId">
  17. <!-- 动态生成模板 -->
  18. <el-select v-model="form.tempId" placeholder="选择模板" clearable filterable @change="onSelectTemp">
  19. <el-option :label="item.label" :value="item.value" v-for="(item, index) in templateMsgList" :key="index"/>
  20. </el-select>
  21. </el-form-item>
  22. <el-form-item label="模板内容:" size="small" required>
  23. <el-form-item v-for="(item, index) in templateMsgCon" :key="index">
  24. <el-form-item>
  25. <p>{{item.name}}</p>
  26. <el-input v-model="templateInput[item.key]" class="input-width-600" clearable type="textarea" autosize></el-input>
  27. </el-form-item>
  28. </el-form-item>
  29. </el-form-item>
  30. <el-form-item label="内容链接:" size="small" class="inline-form-item" required>
  31. <el-form-item prop="articleContent" >
  32. <el-input v-model="form.articleContent" placeholder="请输入正确的外部URL" class="input-width-400" clearable @clear="onClearUrl"></el-input>
  33. <!-- <el-select v-model="article" placeholder="选择文章" clearable filterable @change="onSelectArt" @clear="onClearSelect">
  34. <el-option :label="item.label" :value="item.value" v-for="(item, index) in articleList" :key="index"/>
  35. </el-select> -->
  36. </el-form-item>
  37. </el-form-item>
  38. <el-form-item label="群发对象:" size="small" prop="selfGroupId">
  39. <el-select v-model="form.selfGroupId" placeholder="选择分组" clearable filterable @change="onSelectMass">
  40. <el-option :label="item.label" :value="item.value" v-for="(item, index) in groupList" :key="index"/>
  41. </el-select>
  42. <el-button type="primary" @click="onCheckList" disabled>查看名单</el-button>
  43. </el-form-item>
  44. <div class="footer-container">
  45. <el-button type="primary" v-for="(item, index) in footBtnList" :key="index" @click="item.click">{{item.name}}</el-button>
  46. </div>
  47. </el-form>
  48. </div>
  49. <!-- 底部操作按钮 -->
  50. <!-- 预览dialog -->
  51. <el-dialog title="预览信息" :visible.sync="previewShow">
  52. <div class="preview-container">
  53. <div class="top">
  54. <p>{{templateInput.first}}</p>
  55. <p v-for="(item, index) in previewData" :key="index">
  56. {{item.name}}:{{item.value}}
  57. </p>
  58. <p>{{templateInput.remark}}</p>
  59. </div>
  60. <div class="fot" @click="onOpenUrl">
  61. <p>详情</p>
  62. <p>></p>
  63. </div>
  64. </div>
  65. <div class="preview-btn">
  66. <el-button type="primary" @click="onConfirm">确认</el-button>
  67. </div>
  68. </el-dialog>
  69. </div>
  70. </template>
  71. <script>
  72. import { initTime } from '@/utils/index.js';
  73. import { APIWechatFans } from '@/api/wechat-fans.js';
  74. export default {
  75. name: 'add-mass',
  76. components: { },
  77. data(){
  78. const checkUrl = (rule, value, callback) => {
  79. let urlrReg = /^((https?|ftp):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\/\w\.-]*)*\/?/;
  80. if(urlrReg.test(value)) {
  81. callback();
  82. } else if (this.form.articleId !== '') {
  83. callback();
  84. } else {
  85. callback(`请输入正确的网址`);
  86. }
  87. };
  88. return {
  89. buttonList: [], // 头部按钮,例子: {name: '添加', type: 'primary', icon: 'el-icon-circle-plus', click: () => { this.AddDialog();}}
  90. firstData: '{{first.DATA}}:',
  91. keyword1Data: '{{keyword1DATA}}:',
  92. keyword2Data: '{{keyword2DATA}}:',
  93. keyword3Data: '{{keyword3DATA}}:',
  94. remarkData: '{{remarkDATA}}:',
  95. form: {
  96. subject: '' /* || '财商学习通知' */, //发送主题
  97. templateId: 1, //模板id
  98. articleContent: '', //文章内容
  99. articleId: '', //文章id
  100. selfGroupId: '', //分组id
  101. first: '' /* || '家长您好,您孩子所需的财商学习内容有更新' */, //内容模板标题
  102. keyword1: '' /* || 'csds' */, //学习账号
  103. keyword2: '' /* || '财商36问' */, //更新内容
  104. keyword3: '',
  105. remark: '' /* || '请点击内容查看详情' */, //内容详情
  106. tempId: '' /* || '-kOhlwJPhO7v3WW3rAdmNs76lR7ZpQEJtbVSY8Y35Eg' */, //模板id
  107. },
  108. article: '', //选择文章
  109. // 文章列表
  110. articleList: [],
  111. // 用户分组
  112. groupList: [],
  113. // 底部操作按钮
  114. footBtnList: [
  115. { name: '立即发送', click: () => { this.onSend()}},
  116. { name: '保存', click: () => { this.onSave()} },
  117. { name: '预览', click: () => { this.onPreview()} },
  118. ],
  119. // 预览dialog
  120. previewShow: false,
  121. // 要预览的页面字段
  122. previewData: [],
  123. // form验证
  124. rules: {
  125. subject: [
  126. { required: true, message: '请输入消息主题', trigger: 'blur' },
  127. ],
  128. tempId: [
  129. { required: true, message: '请选择模板消息', trigger: 'blur' },
  130. ],
  131. /* first: [
  132. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  133. ],
  134. keyword1: [
  135. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  136. ],
  137. keyword2: [
  138. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  139. ],
  140. keyword3: [
  141. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  142. ],
  143. keyword4: [
  144. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  145. ],
  146. keyword5: [
  147. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  148. ],
  149. remark: [
  150. { required: true, message: '请填写消息模板内容', trigger: 'blur' },
  151. ], */
  152. articleContent: [
  153. { required: true, validator: checkUrl, message: '请输入正确的外部URL', trigger: 'blur' },
  154. ],
  155. selfGroupId: [
  156. { required: true, message: '请选择群发分组', trigger: 'blur' },
  157. ],
  158. },
  159. // 模板消息列表
  160. templateMsgList: [],
  161. // 模板内容
  162. templateMsgCon: [
  163. {
  164. key: 'first',
  165. name: '{{first.DATA}}',
  166. },
  167. {
  168. key: 'keyword1',
  169. name: '消息类别:{{keyword1.DATA}}',
  170. },
  171. {
  172. key: 'keyword2',
  173. name: '通知用户:{{keyword2.DATA}}',
  174. },
  175. {
  176. key: 'keyword3',
  177. name: '通知内容:{{keyword3.DATA}}',
  178. },
  179. {
  180. key: 'remark',
  181. name: '{{remark.DATA}}'
  182. }
  183. ],
  184. // 动态生成的input
  185. templateInput: {},
  186. tempData: [] || [
  187. {first: 'first', remark: 'remark'}
  188. ]
  189. }
  190. },
  191. computed: {
  192. // 初始弹窗更新时间
  193. initUpdateTime() {
  194. return initTime(new Date(), 'ymdhm'); //更新时间
  195. },
  196. },
  197. activated() {
  198. this.getSelfGroups();
  199. this.getArticlesGroup();
  200. this.getTemplateMsgList();
  201. },
  202. mounted() {
  203. },
  204. created() {
  205. this.getSelfGroups();
  206. this.getTemplateMsgList();
  207. this.getArticlesGroup();
  208. },
  209. methods: {
  210. // 获取自定义分组
  211. getSelfGroups() {
  212. let reqBody = {
  213. page_index: 1,
  214. page_size: 10
  215. }
  216. APIWechatFans.SelfGroups(reqBody)
  217. .then(res => {
  218. let data = res.data;
  219. this.groupList = data.rows.map(item => {
  220. return {
  221. label: item.name,
  222. value: item.id
  223. }
  224. })
  225. console.log("data", data);
  226. })
  227. },
  228. // 获取文章列表
  229. getArticlesGroup() {
  230. let reqBody = {
  231. page_index: 1,
  232. page_size: 100000
  233. }
  234. APIWechatFans.getArticlesGroup(reqBody)
  235. .then(res => {
  236. let data = res.data;
  237. console.log("文章data", data);
  238. this.articleList = data.rows.map(item => {
  239. return {
  240. label: item.title,
  241. value: item.id
  242. }
  243. })
  244. })
  245. },
  246. // 查看白名单
  247. onCheckList() {},
  248. // objectToArray
  249. objectToArray(obj) {
  250. let arr = [];
  251. for(let i in obj) {
  252. // arr.push (obj[i] ) //返回属性值
  253. arr.push(obj[i]) //返回键名
  254. };
  255. return arr;
  256. },
  257. // 立即发送
  258. onSend() {
  259. this.tempData.push(this.templateInput);
  260. console.log("tempData", JSON.stringify(this.templateInput));
  261. // 拼接所需字段
  262. let reqBody = {
  263. send_now: true, //true 是立即发送, false是保存
  264. subject: this.form.subject,
  265. template_id: this.form.tempId,
  266. self_group_id: this.form.selfGroupId,
  267. article_id: this.form.articleId || 0,
  268. mptemplate_content: JSON.stringify(this.templateInput),
  269. url: this.form.articleContent
  270. };
  271. this.$refs['form'].validate((valid) => {
  272. if(valid) {
  273. const isCanSave = this.checkTemplateInput();
  274. if(!isCanSave) {
  275. this.$message({
  276. type: "error",
  277. message: "请完善模板内容"
  278. });
  279. return
  280. }
  281. this.addGroupSender(reqBody, '群发');
  282. } else {
  283. this.$message({
  284. type: "error",
  285. message: "请完善群发消息"
  286. });
  287. }
  288. });
  289. },
  290. // 验证动态模板
  291. checkTemplateInput() {
  292. console.log("templateInput", this.templateInput);
  293. // 获取模板输入框的实际数量
  294. let tempInputLen = this.templateMsgCon.length;
  295. console.log("tempInputLen", tempInputLen);
  296. // 计算模板输入的数量
  297. let tempLen = (Object.keys(this.templateInput).length);
  298. if( tempLen !== tempInputLen) {
  299. return false
  300. } else {
  301. return true
  302. }
  303. },
  304. // 保存
  305. onSave() {
  306. // 定义接口所需字段
  307. let reqBody = {
  308. send_now: false, //true 是立即发送, false是保存
  309. subject: this.form.subject, //消息主题
  310. template_id: this.form.tempId, // 模板id
  311. self_group_id: this.form.selfGroupId, //发送分组id
  312. article_id: this.form.articleId || 0, //文章id
  313. mptemplate_content: JSON.stringify(this.templateInput), //模板消息内容
  314. url: this.form.articleContent, //文章链接
  315. };
  316. this.$refs['form'].validate((valid) => {
  317. if(valid) {
  318. const isCanSave = this.checkTemplateInput();
  319. if(!isCanSave) {
  320. this.$message({
  321. type: "error",
  322. message: "请完善模板内容"
  323. });
  324. return
  325. }
  326. this.addGroupSender(reqBody, '保存');
  327. } else {
  328. this.$message({
  329. type: "error",
  330. message: "请完善群发消息"
  331. });
  332. }
  333. });
  334. },
  335. // 添加群发
  336. addGroupSender(reqBody, action) {
  337. const loading = this.$loading({
  338. text: `${action}中`
  339. });
  340. APIWechatFans.addGroupSender(reqBody)
  341. .then(res => {
  342. console.log("res", res);
  343. if(res.code === 20000) {
  344. this.$message({
  345. type: "success",
  346. message: `${action}成功!`
  347. });
  348. } else {
  349. this.$message({
  350. type: "error",
  351. message: `${action}失败!${res.message}`
  352. });
  353. }
  354. }).catch(error => {
  355. this.$message({
  356. type: "error",
  357. message: '出错了!请联系管理员。'
  358. });
  359. }).finally(() => {
  360. loading.close();
  361. })
  362. },
  363. // 预览
  364. onPreview() {
  365. const isCanSave = this.checkTemplateInput();
  366. if(!isCanSave) {
  367. this.$message({
  368. type: "error",
  369. message: "请先完善模板内容"
  370. });
  371. return
  372. }
  373. this.previewShow = true;
  374. this.tempData.push(this.templateInput);
  375. let previewArr = this.objectToArray(this.tempData[0]).slice(1, this.objectToArray(this.tempData[0]).length - 1);
  376. this.previewData.forEach((value, index) => {
  377. value['value'] = previewArr[index];
  378. });
  379. },
  380. // 预览确认
  381. onConfirm() {
  382. this.previewShow = false;
  383. },
  384. // 选择文章
  385. onSelectArt(value) {
  386. if(value) {
  387. this.form.articleId = value;
  388. this.form.articleContent = this.articleList.filter(item => {
  389. return item.value === value;
  390. }).map(item => {
  391. return item.label;
  392. }).toString();
  393. }
  394. },
  395. // 选择群发对象分组
  396. onSelectMass(value) {
  397. if(value) {
  398. this.form.selfGroupId = value;
  399. console.log("选择的对象分组", value);
  400. }
  401. },
  402. // 获取消息模板列表
  403. getTemplateMsgList() {
  404. APIWechatFans.getTemplateMsgList()
  405. .then(res => {
  406. let telpData = res.data;
  407. this.templateMsgList = telpData.map(item => {
  408. return {
  409. label: item.title,
  410. value: item.template_id
  411. }
  412. });
  413. // 默认显示第一个模板id
  414. this.getTemplateMsgListById(this.templateMsgList[0].value);
  415. })
  416. },
  417. // 根据模板id获取对应模板消息
  418. getTemplateMsgListById(id) {
  419. APIWechatFans.getTemplateById(id)
  420. .then(res => {
  421. console.log("res", res);
  422. let data = res.data;
  423. let splitData = data.content.split('\\n');
  424. this.templateMsgCon = splitData.map(item => {
  425. return {
  426. name: item,
  427. key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
  428. }
  429. });
  430. this.previewData = splitData.splice(1, splitData.length - 2 ).map(item => {
  431. return {
  432. name: item.substring( 0, item.indexOf(":")),
  433. key: item.substring( item.indexOf("{{") + 2, item.indexOf(".DATA"))
  434. }
  435. });
  436. })
  437. },
  438. // 选择模板消息
  439. onSelectTemp(value) {
  440. console.log("选择了模板消息", value);
  441. this.form.tempId = value;
  442. this.templateInput = {};
  443. this.getTemplateMsgListById(value);
  444. },
  445. // 清空url填写
  446. onClearUrl() {
  447. this.form.articleContent = '';
  448. if( this.form.articleId ) {
  449. this.form.articleId = '';
  450. }
  451. },
  452. onClearSelect() {
  453. this.form.articleContent = '';
  454. },
  455. onOpenUrl() {
  456. window.open(this.form.articleContent);
  457. }
  458. }
  459. }
  460. </script>
  461. <style scoped lang="scss">
  462. .home-container {
  463. height: 600px;
  464. padding: 0 20px;
  465. //overflow-y: scroll;
  466. overflow-x: hidden;
  467. border: none;
  468. box-shadow: none;
  469. overflow-y: scroll;
  470. .form-container {
  471. height: 600px;
  472. padding: 5px 0;
  473. border: 1px solid #d8dce5;
  474. display: flex;
  475. justify-content: center;
  476. //align-items: center;
  477. overflow: scroll;
  478. p {
  479. margin: 5px 0;
  480. }
  481. .input-width-400 {
  482. width: 400px;
  483. }
  484. .input-width-600 {
  485. width: 600px;
  486. }
  487. .inline-form-item {
  488. display: inline-block;
  489. }
  490. }
  491. .footer-container {
  492. height: 60px;
  493. display: flex;
  494. justify-content: center;
  495. align-items: center;
  496. .el-button {
  497. width: 120px;
  498. }
  499. }
  500. .preview-container {
  501. //height: 400px;
  502. display: flex;
  503. justify-content: flex-start;
  504. align-items: flex-start;
  505. flex-direction: column;
  506. margin: 20px;
  507. border: 1px solid #d8dce5;
  508. .top {
  509. width: 100%;
  510. display: flex;
  511. justify-content: flex-start;
  512. align-items: flex-start;
  513. flex-direction: column;
  514. padding: 0 40px;
  515. border-bottom: 1px solid #d8dce5 ;
  516. overflow-y: scroll;
  517. }
  518. .fot {
  519. width: 100%;
  520. padding: 0 40px;
  521. display: flex;
  522. justify-content: space-between;
  523. align-items: center;
  524. &:hover {
  525. cursor: pointer;
  526. }
  527. }
  528. }
  529. .preview-btn {
  530. display: flex;
  531. justify-content: center;
  532. align-items: center;
  533. }
  534. }
  535. </style>