mp:完善微信公众号的消息

This commit is contained in:
YunaiV
2023-01-17 23:20:56 +08:00
parent 6fc0b3fc54
commit 68ef11ee87
52 changed files with 378 additions and 494 deletions

View File

@ -9,7 +9,7 @@ export function getDraftPage(query) {
})
}
// 创建草稿
// 创建公众号草稿
export function createDraft(accountId, articles) {
return request({
url: '/mp/draft/create?accountId=' + accountId,
@ -20,7 +20,7 @@ export function createDraft(accountId, articles) {
})
}
// 更新草稿
// 更新公众号草稿
export function updateDraft(accountId, mediaId, articles) {
return request({
url: '/mp/draft/update?accountId=' + accountId + '&mediaId=' + mediaId,
@ -29,7 +29,7 @@ export function updateDraft(accountId, mediaId, articles) {
})
}
// 删除草稿
// 删除公众号草稿
export function deleteDraft(accountId, mediaId) {
return request({
url: '/mp/draft/delete?accountId=' + accountId + '&mediaId=' + mediaId,

View File

@ -1,6 +1,6 @@
import request from '@/utils/request'
// 获得粉丝消息分页
// 获得公众号消息分页
export function getMessagePage(query) {
return request({
url: '/mp/message/page',

View File

@ -60,6 +60,7 @@ export const DICT_TYPE = {
// ========== MP 模块 ==========
MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型
MP_MESSAGE_TYPE: 'mp_message_type', // 消息类型
// ========== MALL - PRODUCT 模块 ==========
PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态

View File

@ -50,49 +50,12 @@ SOFTWARE.
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
</el-row>
<!-- 列表 -->
<!-- tab -->
<el-tab-pane name="1">
<span slot="label"><i class="el-icon-star-off"></i> 关注时回复</span>
<el-table v-loading="loading" :data="list">
<el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:auto-reply:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['mp:auto-reply:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="2">
<span slot="label"><i class="el-icon-chat-line-round"></i> 消息回复</span>
<el-table v-loading="loading" :data="list">
<el-table-column label="请求消息类型" align="center" prop="requestMessageType"/>
<el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:auto-reply:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['mp:auto-reply:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="3">
<span slot="label"><i class="el-icon-news"></i> 关键词回复</span>
@ -101,13 +64,42 @@ SOFTWARE.
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="关键词" align="center" prop="requestKeyword"/>
<el-table-column label="匹配类型" align="center" prop="requestMatch">
<el-table-column label="请求消息类型" align="center" prop="requestMessageType" v-if="type === '2'" />
<el-table-column label="关键词" align="center" prop="requestKeyword" v-if="type === '3'" />
<el-table-column label="匹配类型" align="center" prop="requestMatch" v-if="type === '3'">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
</template>
</el-table-column>
<el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>
<el-table-column label="回复消息类型" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.MP_MESSAGE_TYPE" :value="scope.row.responseMessageType"/>
</template>
</el-table-column>
<el-table-column label="回复内容" align="center">
<template slot-scope="scope">
<div v-if="scope.row.responseMessageType === 'text'">{{ scope.row.responseContent }}</div>
<div v-else-if="scope.row.responseMessageType === 'voice'">
<wx-voice-player :url="scope.row.responseMediaUrl" />
</div>
<div v-else-if="scope.row.responseMessageType === 'image'">
<a target="_blank" :href="scope.row.responseMediaUrl">
<img :src="scope.row.responseMediaUrl" style="width: 100px">
</a>
</div>
<div v-else-if="scope.row.responseMessageType === 'video' || scope.row.responseMessageType === 'shortvideo'">
<wx-video-player :url="scope.row.responseMediaUrl" style="margin-top: 10px" />
</div>
<div v-else-if="scope.row.responseMessageType === 'news'">
<wx-news :articles="scope.row.responseArticles" />
</div>
<div v-else-if="scope.row.responseMessageType === 'music'">
<wx-music :title="scope.row.responseTitle" :description="scope.row.responseDescription"
:thumb-media-url="scope.row.responseThumbMediaUrl"
:music-url="scope.row.responseMusicUrl" :hq-music-url="scope.row.responseHqMusicUrl" />
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
@ -130,9 +122,9 @@ SOFTWARE.
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="消息类型" prop="requestMessageType" v-if="type === '2'">
<el-select v-model="form.requestMessageType" placeholder="请选择">
<el-option v-for="item in dictData.get('wx_req_type')" :key="item.value" :label="item.label"
:value="item.value" :disabled="item.disabled" v-if="item.value !== 'event'">
</el-option>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"
v-if="requestMessageTypes.includes(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="匹配类型" prop="requestMatch" v-if="type === '3'">
@ -157,6 +149,12 @@ SOFTWARE.
</template>
<script>
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
import WxLocation from '@/views/mp/components/wx-location/main.vue';
import WxMusic from '@/views/mp/components/wx-music/main.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
import { getSimpleAccounts } from "@/api/mp/account";
import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updateAutoReply } from "@/api/mp/autoReply";
@ -164,12 +162,20 @@ import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updat
export default {
name: 'mpAutoReply',
components: {
WxVideoPlayer,
WxVoicePlayer,
WxMsg,
WxLocation,
WxMusic,
WxNews,
WxReplySelect
},
data() {
return {
// tab 类型1、关注时回复2、消息回复3、关键词回复
type: '3',
// 允许选择的请求消息类型
requestMessageTypes: ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'],
// 遮罩层
loading: true,
// 显示搜索条件
@ -202,8 +208,6 @@ export default {
},
hackResetWxReplySelect: false, // 重置 WxReplySelect 组件,解决无法清除的问题
dictData: new Map(),
// 公众号账号列表
accounts: []
}
@ -218,33 +222,6 @@ export default {
// 加载数据
this.getList();
})
// TODO 芋艿:字典数据,一起搞
this.dictData.set('wx_req_type',[{
value: 'text',
label: '文本'
},{
value: 'image',
label: '图片'
},{
value: 'voice',
label: '语音'
},{
value: 'video',
label: '视频'
},{
value: 'shortvideo',
label: '小视频'
},{
value: 'location',
label: '地理位置'
},{
value: 'link',
label: '链接消息'
},{
value: 'event',
label: '事件推送'
}])
},
methods: {
/** 查询列表 */
@ -295,7 +272,8 @@ export default {
this.open = true
this.title = '新增自动回复';
this.objData = {
type : 'text'
type : 'text',
accountId: this.queryParams.accountId,
}
},
/** 修改按钮操作 */

View File

@ -9,13 +9,13 @@
<div class="msg-main">
<div class="msg-div" :id="'msg-div' + nowStr">
<!-- 加载更多 -->
<div v-loading="tableLoading"></div>
<div v-if="!tableLoading">
<div v-loading="loading"></div>
<div v-if="!loading">
<div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
<div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div>
</div>
<!-- 消息列表 -->
<div class="execution" v-for="item in tableData" :key='item.id'>
<div class="execution" v-for="item in list" :key='item.id'>
<div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
<div class="avatar-div">
<img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar">
@ -124,20 +124,20 @@ import { getUser } from "@/api/mp/user";
},
props: {
userId: {
type: String,
type: Number,
required: true
},
},
data() {
return {
nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
sendLoading: false, // 发送消息是否加载中
tableLoading: false, // 消息列表是否正在加载中
loading: false, // 消息列表是否正在加载中
loadMore: true, // 是否可以加载更多
tableData: [], // 消息列表
page: {
list: [], // 消息列表
queryParams: {
pageNo: 1, // 当前页数
pageSize: 14, // 每页显示多少条
accountId: undefined,
},
user: { // 由于微信不再提供昵称,直接使用“用户”展示
nickname: '用户',
@ -148,6 +148,9 @@ import { getUser } from "@/api/mp/user";
nickname: '公众号',
avatar: require("@/assets/images/wechat.png"),
},
// ========= 消息发送 =========
sendLoading: false, // 发送消息是否加载中
objData: { // 微信发送消息
type: 'text',
},
@ -156,14 +159,19 @@ import { getUser } from "@/api/mp/user";
created() {
// 获得用户信息
getUser(this.userId).then(response => {
this.user.nickname = response.data.nickname | this.user.nickname;
this.user.avatar = response.data.avatar | this.user.avatar;
this.user.nickname = response.data.nickname && response.data.nickname.length > 0 ?
response.data.nickname : this.user.nickname;
this.user.avatar = response.data.avatar && this.user.avatar.length > 0 ?
response.data.avatar : this.user.avatar;
this.user.accountId = response.data.accountId;
// 设置公众号账号编号
this.queryParams.accountId = response.data.accountId;
this.objData.accountId = response.data.accountId;
})
// 加载消息
this.refreshChange()
// 加载消息
console.log(this.queryParams)
this.refreshChange()
})
},
methods:{
sendMsg(){
@ -190,7 +198,7 @@ import { getUser } from "@/api/mp/user";
})).then(response => {
this.sendLoading = false
// 添加到消息列表,并滚动
this.tableData = [...this.tableData , ...[response.data] ]
this.list = [...this.list , ...[response.data] ]
this.scrollToBottom()
// 重置 objData 状态
this.$refs['replySelect'].deleteObj(); // 重置,避免 tab 的数据未清理
@ -199,15 +207,16 @@ import { getUser } from "@/api/mp/user";
})
},
loadingMore() {
this.page.pageNo++
this.getPage(this.page)
this.queryParams.pageNo++
this.getPage(this.queryParams)
},
getPage(page, params) {
this.tableLoading = true
this.loading = true
getMessagePage(Object.assign({
pageNo: page.pageNo,
pageSize: page.pageSize,
userId: this.userId
userId: this.userId,
accountId: page.accountId,
}, params)).then(response => {
// 计算当前的滚动高度
const msgDiv = document.getElementById('msg-div' + this.nowStr);
@ -218,16 +227,16 @@ import { getUser } from "@/api/mp/user";
// 处理数据
const data = response.data.list.reverse();
this.tableData = [...data, ...this.tableData]
this.tableLoading = false
if (data.length < this.page.pageSize || data.length === 0){
this.list = [...data, ...this.list]
this.loading = false
if (data.length < this.queryParams.pageSize || data.length === 0){
this.loadMore = false
}
this.page.pageNo = page.pageNo
this.page.pageSize = page.pageSize
this.queryParams.pageNo = page.pageNo
this.queryParams.pageSize = page.pageSize
// 滚动到原来的位置
if(this.page.pageNo === 1) { // 定位到消息底部
if(this.queryParams.pageNo === 1) { // 定位到消息底部
this.scrollToBottom()
} else if (data.length !== 0) { // 定位滚动条
this.$nextTick(() => {
@ -242,7 +251,7 @@ import { getUser } from "@/api/mp/user";
* 刷新回调
*/
refreshChange() {
this.getPage(this.page)
this.getPage(this.queryParams)
},
/** 定位到消息底部 */
scrollToBottom: function () {
@ -267,6 +276,8 @@ import { getUser } from "@/api/mp/user";
height: 50vh;
overflow: auto;
background-color: #eaeaea;
margin-left: 10px;
margin-right: 10px;
}
.msg-send {
padding: 10px;

View File

@ -66,8 +66,8 @@ SOFTWARE.
<div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
</div>
<div class="save_div">
<el-button class="save_btn" type="success" size="small" @click="handleSave">保存并发布菜单</el-button>
<el-button class="save_btn" type="danger" size="small" @click="handleDelete">清空菜单</el-button>
<el-button class="save_btn" type="success" size="small" @click="handleSave" v-hasPermi="['mp:menu:save']">保存并发布菜单</el-button>
<el-button class="save_btn" type="danger" size="small" @click="handleDelete" v-hasPermi="['mp:menu:delete']">清空菜单</el-button>
</div>
</div>
<!--右边配置-->

View File

@ -8,18 +8,19 @@
<el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<!-- 等待处理 -->
<el-form-item label="消息类型" prop="msgType">
<el-select v-model="queryParams.msgType" placeholder="请选择消息类型" clearable size="small">
<el-option label="请选择字典生成" value=""/>
<!-- TODO 等待处理 -->
<el-form-item label="消息类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择消息类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="用户标识" prop="openid">
<el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@ -42,12 +43,11 @@
<el-table-column label="消息类型" align="center" prop="type" width="80"/>
<el-table-column label="发送方" align="center" prop="sendFrom" width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.sendFrom === 1" type="success">用户</el-tag>
<el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag>
<el-tag v-else type="info">公众号</el-tag>
</template>
</el-table-column>
<el-table-column label="用户标识" align="center" prop="openid" width="300" />
<!-- TODO 芋艿发送/接收 -->
<el-table-column label="内容" prop="content">
<template slot-scope="scope">
<!-- 事件区域 -->
@ -118,10 +118,9 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- TODO 芋艿增加消息按钮 -->
<!-- <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"-->
<!-- v-hasPermi="['mp:message:update']">修改-->
<!-- </el-button>-->
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleSend(scope.row)"
v-hasPermi="['mp:message:send']">消息
</el-button>
</template>
</el-table-column>
</el-table>
@ -129,24 +128,26 @@
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<el-dialog title="用户消息" :visible.sync="open" width="40%">
<wx-msg user-id="3" v-if="true" />
<!-- 发送消息的弹窗 -->
<el-dialog title="粉丝消息列表" :visible.sync="open" width="50%">
<wx-msg :user-id="userId" v-if="open" />
</el-dialog>
</div>
</template>
<script>
import { getMessagePage } from "@/api/mp/message";
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
import WxLocation from '@/views/mp/components/wx-location/main.vue';
import WxMusic from '@/views/mp/components/wx-music/main.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import { getMessagePage } from "@/api/mp/message";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
name: "WxFansMsg",
name: "MpMessage",
components: {
WxVideoPlayer,
WxVoicePlayer,
@ -167,76 +168,52 @@ export default {
total: 0,
// 粉丝消息列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: true,
open: false,
// 查询参数
dateRangeCreateTime: [],
queryParams: {
pageNo: 1,
pageSize: 10,
openid: null,
nickname: null,
headimgUrl: null,
wxAccountId: null,
msgType: null,
content: null,
resContent: null,
isRes: null,
mediaId: null,
picUrl: null,
picPath: null,
accountId: null,
type: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {},
// 操作的用户编号
userId: 0,
// 公众号账号列表
accounts: []
};
},
created() {
this.getList();
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询消息')
return false
}
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行查询
getMessagePage(params).then(response => {
getMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
openid: undefined,
nickname: undefined,
headimgUrl: undefined,
wxAccountId: undefined,
msgType: undefined,
content: undefined,
resContent: undefined,
isRes: undefined,
mediaId: undefined,
picUrl: undefined,
picPath: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
@ -244,10 +221,17 @@ export default {
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery();
},
handleSend(row) {
this.userId = row.userId;
this.open = true;
},
}
};
</script>

View File

@ -77,7 +77,7 @@ require('echarts/lib/component/legend')
import { getInterfaceSummary, getUserSummary, getUserCumulate, getUpstreamMessage} from '@/api/mp/statistics'
import { datePickerOptions } from "@/utils/constants";
import {addTime, beginOfDay, betweenDay, endOfDay, formatDate} from "@/utils/dateUtils";
import {getSimpleAccounts} from "@/api/mp/account";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
name: 'mpStatistics',