Commit 29cf770457d5a8ed240002369e8da5aa5bae6374

Authored by YunaiV
1 parent ec872c70

mp:支持 wx-reply 发送图片消息

yudao-ui-admin/src/utils/auth.js
... ... @@ -94,3 +94,6 @@ export function setTenantId(username) {
94 94 export function removeTenantId() {
95 95 localStorage.removeItem(TenantIdKey)
96 96 }
  97 +
  98 +export class getToken {
  99 +}
... ...
yudao-ui-admin/src/views/mp/components/wx-msg/main.vue
... ... @@ -81,7 +81,7 @@
81 81 </div>
82 82 </div>
83 83 <div class="msg-send" v-loading="sendLoading">
84   - <WxReplySelect :objData="objData"></WxReplySelect>
  84 + <wx-reply-select :objData="objData" />
85 85 <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
86 86 </div>
87 87 </div>
... ... @@ -95,6 +95,7 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
95 95 import WxNews from '@/views/mp/components/wx-news/main.vue';
96 96 import WxLocation from '@/views/mp/components/wx-location/main.vue';
97 97 import WxMusic from '@/views/mp/components/wx-music/main.vue';
  98 +import {getUser} from "@/api/mp/user";
98 99  
99 100 export default {
100 101 name: "wxMsg",
... ... @@ -110,7 +111,7 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
110 111 userId: {
111 112 type: String,
112 113 required: true
113   - }
  114 + },
114 115 },
115 116 data() {
116 117 return {
... ... @@ -126,17 +127,27 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
126 127 user: { // 由于微信不再提供昵称,直接使用“用户”展示
127 128 nickname: '用户',
128 129 avatar: require("@/assets/images/profile.jpg"),
  130 + accountId: 0, // 公众号账号编号
129 131 },
130 132 mp: {
131 133 nickname: '公众号',
132 134 avatar: require("@/assets/images/wechat.png"),
133 135 },
134   - objData:{ // 微信发送消息
135   - repType: 'text'
  136 + objData: { // 微信发送消息
  137 + type: 'text',
136 138 },
137 139 }
138 140 },
139 141 created() {
  142 + // 获得用户信息
  143 + getUser(this.userId).then(response => {
  144 + this.user.nickname = response.data.nickname | this.user.nickname;
  145 + this.user.avatar = response.data.avatar | this.user.avatar;
  146 + // 设置公众号账号编号
  147 + this.objData.accountId = response.data.accountId;
  148 + })
  149 +
  150 + // 加载消息
140 151 this.refreshChange()
141 152 },
142 153 methods:{
... ... @@ -144,7 +155,7 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
144 155 if (!this.objData) {
145 156 return;
146 157 }
147   - if (this.objData.repType === 'news') {
  158 + if (this.objData.type === 'news') {
148 159 this.objData.content.articles = [this.objData.content.articles[0]]
149 160 this.$message({
150 161 showClose: true,
... ... @@ -157,7 +168,6 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
157 168 userId: this.userId
158 169 }, {
159 170 ...this.objData,
160   - type: this.objData.repType,
161 171 // content: this.objData.repContent,
162 172 // TODO 芋艿:临时适配,保证可用
163 173 })).then(response => {
... ... @@ -167,7 +177,8 @@ import {getMessagePage, sendMessage} from &#39;@/api/mp/message&#39;
167 177 this.scrollToBottom()
168 178 // 重置 objData 状态
169 179 this.objData = {
170   - repType: 'text'
  180 + type: 'text',
  181 + content: '',
171 182 }
172 183 }).catch(() => {
173 184 this.sendLoading = false
... ...
yudao-ui-admin/src/views/mp/components/wx-reply/main.vue
1 1 <!--
2 2 - Copyright (C) 2018-2019
3 3 - All rights reserved, Designed By www.joolun.com
  4 + 芋道源码:
  5 + ① 移除多余的 rep 为前缀的变量,让 message 消息更简单
  6 + ② 代码优化,补充注释,提升阅读性
4 7 -->
5 8 <template>
6   - <el-tabs type="border-card" v-model="objData.repType" @tab-click="handleClick">
  9 + <el-tabs type="border-card" v-model="objData.type" @tab-click="handleClick">
7 10 <!-- 类型 1:文本 -->
8 11 <el-tab-pane name="text">
9 12 <span slot="label"><i class="el-icon-document"></i> 文本</span>
10   - <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="objData.content" />
  13 + <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="objData.content" @input="inputContent" />
11 14 </el-tab-pane>
12 15 <!-- 类型 2:图片 -->
13 16 <el-tab-pane name="image">
14 17 <span slot="label"><i class="el-icon-picture"></i> 图片</span>
15 18 <el-row>
16   - <div class="select-item" v-if="objData.repUrl">
17   - <img class="material-img" :src="objData.repUrl">
18   - <p class="item-name" v-if="objData.repName">{{objData.repName}}</p>
  19 + <!-- 情况一:已经选择好素材、或者上传好图片 -->
  20 + <div class="select-item" v-if="objData.url">
  21 + <img class="material-img" :src="objData.url">
19 22 <el-row class="ope-row">
20 23 <el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
21 24 </el-row>
22 25 </div>
23   - <div v-if="!objData.repUrl">
  26 + <!-- 情况二:未做完上述操作 -->
  27 + <div v-else>
24 28 <el-row style="text-align: center">
  29 + <!-- 选择素材 -->
25 30 <el-col :span="12" class="col-select">
26 31 <el-button type="success" @click="openMaterial">素材库选择<i class="el-icon-circle-check el-icon--right"></i></el-button>
27 32 </el-col>
  33 + <!-- 文件上传 -->
28 34 <el-col :span="12" class="col-add">
29 35 <el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
30 36 :before-upload="beforeImageUpload" :on-success="handleUploadSuccess">
... ... @@ -34,6 +40,7 @@
34 40 </el-col>
35 41 </el-row>
36 42 </div>
  43 + <!-- 点击选择素材,会打开下面的弹窗 -->
37 44 <el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="90%" append-to-body>
38 45 <WxMaterialSelect :objData="objData" @selectMaterial="selectMaterial"></WxMaterialSelect>
39 46 </el-dialog>
... ... @@ -82,7 +89,7 @@
82 89 <el-input v-model="objData.repDesc" placeholder="请输入描述"></el-input>
83 90 <div style="margin: 20px 0;"></div>
84 91 <div style="text-align: center;">
85   - <a target="_blank" v-if="objData.repUrl" :href="objData.repUrl"><i class="icon-shipinbofang">&nbsp;播放视频</i></a>
  92 + <a target="_blank" v-if="objData.url" :href="objData.url"><i class="icon-shipinbofang">&nbsp;播放视频</i></a>
86 93 </div>
87 94 <div style="margin: 20px 0;"></div>
88 95 <div style="text-align: center">
... ... @@ -138,7 +145,7 @@
138 145 </div>
139 146 </div>
140 147 <el-dialog title="选择图片" :visible.sync="dialogThumbVisible" width="80%" append-to-body>
141   - <WxMaterialSelect :objData="{repType:'image'}" @selectMaterial="selectMaterial"></WxMaterialSelect>
  148 + <WxMaterialSelect :objData="{type:'image'}" @selectMaterial="selectMaterial"></WxMaterialSelect>
142 149 </el-dialog>
143 150 </el-col>
144 151 <el-col :span="18">
... ... @@ -148,7 +155,7 @@
148 155 </el-col>
149 156 </el-row>
150 157 <div style="margin: 20px 0;"></div>
151   - <el-input v-model="objData.repUrl" placeholder="请输入音乐链接"></el-input>
  158 + <el-input v-model="objData.url" placeholder="请输入音乐链接"></el-input>
152 159 <div style="margin: 20px 0;"></div>
153 160 <el-input v-model="objData.repHqUrl" placeholder="请输入高质量音乐链接"></el-input>
154 161 </el-tab-pane>
... ... @@ -160,7 +167,7 @@
160 167 import WxNews from '@/views/mp/components/wx-news/main.vue'
161 168 // import WxMaterialSelect from '@/components/wx-material-select/main.vue'
162 169 import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
163   - import { getToken } from '@/utils/auth'
  170 + import {getAccessToken, getToken} from '@/utils/auth'
164 171  
165 172 export default {
166 173 name: "wxReplySelect",
... ... @@ -170,11 +177,11 @@
170 177 WxVoicePlayer
171 178 },
172 179 props: {
173   - objData:{
174   - type: Object
  180 + objData: { // 消息对象。
  181 + type: Object, // 设置为 Object 的原因,方便属性的传递
  182 + required: true,
175 183 },
176   - // 图文类型:1、已发布图文;2、草稿箱图文
177   - newsType:{
  184 + newsType:{ // 图文类型:1、已发布图文;2、草稿箱图文
178 185 type: String,
179 186 default: "1"
180 187 },
... ... @@ -193,124 +200,176 @@
193 200 descs:[]//降序字段
194 201 },
195 202 tableLoading: false,
196   - dialogNewsVisible:false,
197   - dialogImageVisible:false,
198   - dialogVoiceVisible:false,
199   - dialogVideoVisible:false,
200   - dialogThumbVisible:false,
201   - tempObj: new Map().set(this.objData.repType,Object.assign({},this.objData)),
202   - fileList:[],
203   - uploadData:{
204   - "mediaType":this.objData.repType,
  203 +
  204 + tempObj: new Map().set( // 临时缓存,切换消息类型的 tab 的时候,可以保存对应的数据;
  205 + this.objData.type, // 消息类型
  206 + Object.assign({}, this.objData)), // 消息内容
  207 +
  208 + // ========== 素材选择的弹窗,是否可见 ==========
  209 + dialogNewsVisible: false, // 图文
  210 + dialogImageVisible: false, // 图片
  211 + dialogVoiceVisible: false, // 语音
  212 + dialogVideoVisible: false, // 视频
  213 + dialogThumbVisible: false, // 缩略图
  214 +
  215 + // ========== 文件上传(图片、语音、视频) ==========
  216 + fileList: [], // 文件列表
  217 + uploadData: {
  218 + "accountId": undefined,
  219 + "type": this.objData.type,
205 220 "title":'',
206 221 "introduction":''
207 222 },
208   - actionUrl: process.env.VUE_APP_BASE_API +'/wxmaterial/materialFileUpload',
209   - headers: {
210   - Authorization: 'Bearer ' + getToken()
211   - }
  223 + actionUrl: process.env.VUE_APP_BASE_API +'/admin-api/mp/material/upload-temporary',
  224 + headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
212 225 }
213 226 },
214   - computed: {
215   -
216   - },
217 227 methods:{
218 228 beforeThumbImageUpload(file){
219   - const isType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/bmp' || file.type === 'image/jpg';
220   - const isLt = file.size / 1024 / 1024 < 2;
  229 + const isType = file.type === 'image/jpeg'
  230 + || file.type === 'image/png'
  231 + || file.type === 'image/gif'
  232 + || file.type === 'image/bmp'
  233 + || file.type === 'image/jpg';
221 234 if (!isType) {
222 235 this.$message.error('上传图片格式不对!');
  236 + return false;
223 237 }
  238 + const isLt = file.size / 1024 / 1024 < 2;
224 239 if (!isLt) {
225   - this.$message.error('上传图片大小不能超过2M!');
  240 + this.$message.error('上传图片大小不能超过 2M!');
  241 + return false;
226 242 }
227   - return isType && isLt;
228   - },
229   - deleteObj(){
230   - this.$delete(this.objData,'repName')
231   - this.$delete(this.objData,'repUrl')
232   - this.$delete(this.objData,'content')
  243 + this.uploadData.accountId = this.objData.accountId;
  244 + return true;
233 245 },
234 246 beforeVoiceUpload(file){
235 247 this.tableLoading = true
236   - const isType = file.type === 'audio/mp3' || file.type === 'audio/wma' || file.type === 'audio/wav' || file.type === 'audio/amr';
237   - const isLt = file.size / 1024 / 1024 < 2;
  248 + // 校验格式
  249 + const isType = file.type === 'audio/mp3'
  250 + || file.type === 'audio/wma'
  251 + || file.type === 'audio/wav'
  252 + || file.type === 'audio/amr';
238 253 if (!isType) {
239 254 this.$message.error('上传语音格式不对!');
  255 + return false;
240 256 }
  257 + // 校验大小
  258 + const isLt = file.size / 1024 / 1024 < 2;
241 259 if (!isLt) {
242   - this.$message.error('上传语音大小不能超过2M!');
  260 + this.$message.error('上传语音大小不能超过 2M!');
  261 + return false;
243 262 }
244   - return isType && isLt;
  263 + this.uploadData.accountId = this.objData.accountId;
  264 + return true;
245 265 },
246 266 beforeImageUpload(file){
247   - const isType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/bmp' || file.type === 'image/jpg';
248   - const isLt = file.size / 1024 / 1024 < 2;
  267 + // 校验格式
  268 + const isType = file.type === 'image/jpeg'
  269 + || file.type === 'image/png'
  270 + || file.type === 'image/gif'
  271 + || file.type === 'image/bmp'
  272 + || file.type === 'image/jpg';
249 273 if (!isType) {
250 274 this.$message.error('上传图片格式不对!');
  275 + return false;
251 276 }
  277 + // 校验大小
  278 + const isLt = file.size / 1024 / 1024 < 2;
252 279 if (!isLt) {
253   - this.$message.error('上传图片大小不能超过2M!');
  280 + this.$message.error('上传图片大小不能超过 2M!');
  281 + return false;
254 282 }
255   - return isType && isLt;
  283 + this.uploadData.accountId = this.objData.accountId;
  284 + return true;
256 285 },
257   - handleUploadSuccess(response, file, fileList){
258   - if(response.code == 200){
259   - this.fileList = []
260   - this.uploadData.title = ''
261   - this.uploadData.introduction = ''
262   - let item = response.data
263   - this.selectMaterial(item)
264   - }else{
  286 + handleUploadSuccess(response, file, fileList) {
  287 + if (response.code !== 0) {
265 288 this.$message.error('上传出错:' + response.msg)
  289 + return false;
266 290 }
  291 +
  292 + // 清空上传时的各种数据
  293 + this.fileList = []
  294 + this.uploadData.title = ''
  295 + this.uploadData.introduction = ''
  296 +
  297 + // 上传好的文件,本质是个素材,所以可以进行选中
  298 + let item = response.data
  299 + this.selectMaterial(item)
267 300 },
268   - handleClick(tab, event){
269   - this.uploadData.mediaType = this.objData.repType
270   - let tempObjItem = this.tempObj.get(this.objData.repType)
271   - if(tempObjItem){
272   - this.objData.repName = tempObjItem.repName ? tempObjItem.repName : null
273   - this.objData.repMediaId = tempObjItem.repMediaId ? tempObjItem.repMediaId : null
274   - this.objData.media_id = tempObjItem.media_id ? tempObjItem.media_id : null
275   - this.objData.repUrl = tempObjItem.repUrl ? tempObjItem.repUrl : null
  301 + /**
  302 + * 切换消息类型的 tab
  303 + *
  304 + * @param tab tab
  305 + */
  306 + handleClick(tab) {
  307 + // 设置后续文件上传的文件类型
  308 + this.uploadData.type = this.objData.type;
  309 + if (this.uploadData.type === 'music') { // 【音乐】上传的是缩略图
  310 + this.uploadData.type = 'thumb';
  311 + }
  312 +
  313 + // 从 tempObj 临时缓存中,获取对应的数据,并设置回 objData
  314 + let tempObjItem = this.tempObj.get(this.objData.type)
  315 + // console.log(this.objData.type)
  316 + // console.log(tempObjItem)
  317 + console.log(this.objData)
  318 + // console.log(this.tempObj)
  319 + if (tempObjItem) {
276 320 this.objData.content = tempObjItem.content ? tempObjItem.content : null
277   - this.objData.repDesc = tempObjItem.repDesc ? tempObjItem.repDesc : null
278   - }else{
279   - this.$delete(this.objData,'repName')
280   - this.$delete(this.objData,'repMediaId')
281   - this.$delete(this.objData,'media_id')
282   - this.$delete(this.objData,'repUrl')
283   - this.$delete(this.objData,'content')
284   - this.$delete(this.objData,'repDesc')
  321 + this.objData.mediaId = tempObjItem.mediaId ? tempObjItem.mediaId : null
  322 + this.objData.url = tempObjItem.url ? tempObjItem.url : null
  323 +
  324 + // TODO 芋艿:临时注释掉,看看有没用
  325 + // this.objData.repName = tempObjItem.repName ? tempObjItem.repName : null
  326 + // this.objData.repMediaId = tempObjItem.repMediaId ? tempObjItem.repMediaId : null
  327 + // this.objData.repDesc = tempObjItem.repDesc ? tempObjItem.repDesc : null
  328 + return;
285 329 }
  330 + // 如果获取不到,需要把 objData 复原
  331 + this.objData.mediaId = undefined;
  332 + this.objData.content = undefined;
  333 + // this.$delete(this.objData,'repName')
  334 + // this.$delete(this.objData,'repMediaId')
  335 + // this.$delete(this.objData,'mediaId')
  336 + // this.$delete(this.objData,'url')
  337 + // this.$delete(this.objData,'content') // TODO 芋艿:貌似删除了,文本无法输入
  338 + // this.$delete(this.objData,'repDesc')
286 339 },
287   - selectMaterial(item){
  340 + /**
  341 + * 选择素材,将设置设置到 objData 变量
  342 + *
  343 + * @param item 素材
  344 + */
  345 + selectMaterial(item) {
  346 + // 选择好素材,所以隐藏弹窗
  347 + this.closeMaterial();
  348 +
  349 + // 创建 tempObjItem 对象,并设置对应的值
288 350 let tempObjItem = {}
289   - tempObjItem.repType = this.objData.repType
290   - tempObjItem.repMediaId = item.mediaId
291   - tempObjItem.media_id = item.mediaId
292   - tempObjItem.content = item.content
  351 + tempObjItem.type = this.objData.type
  352 + tempObjItem.mediaId = item.mediaId
  353 + // tempObjItem.repMediaId = item.mediaId // TODO 芋艿:应该可以注释吧?
  354 + // tempObjItem.content = item.content // TODO 芋艿:应该可以注释吧?
293 355  
294   - this.dialogNewsVisible = false
295   - this.dialogImageVisible = false
296   - this.dialogVoiceVisible = false
297   - this.dialogVideoVisible = false
298   - this.objData.repMediaId = item.mediaId
299   - this.objData.media_id = item.mediaId
  356 + // this.objData.repMediaId = item.mediaId
  357 + this.objData.mediaId = item.mediaId
300 358 this.objData.content = item.content
301   - if(this.objData.repType == 'music'){
  359 + if (this.objData.type === 'music') {
302 360 tempObjItem.repThumbMediaId = item.mediaId
303 361 tempObjItem.repThumbUrl = item.url
304 362 this.objData.repThumbMediaId = item.mediaId
305 363 this.objData.repThumbUrl = item.url
306   - this.dialogThumbVisible = false
307   - }else{
308   - tempObjItem.repName = item.name
309   - tempObjItem.repUrl = item.url
310   - this.objData.repName = item.name
311   - this.objData.repUrl = item.url
  364 + this.dialogThumbVisible = false // TODO 芋艿:这里为什么单独写???
  365 + } else{
  366 + // tempObjItem.repName = item.name
  367 + // tempObjItem.url = item.url
  368 + // this.objData.repName = item.name
  369 + this.objData.mediaId = item.mediaId
  370 + this.objData.url = item.url
312 371 }
313   - if(this.objData.repType == 'video'){
  372 + if(this.objData.type == 'video'){
314 373 // getMaterialVideo({
315 374 // mediaId:item.mediaId
316 375 // }).then(response => {
... ... @@ -318,33 +377,54 @@
318 377 // let data = response.data
319 378 // this.$set(this.objData,'repName',data.title)
320 379 // this.$set(this.objData,'repDesc',data.description)
321   - // this.$set(this.objData,'repUrl',data.downUrl)
  380 + // this.$set(this.objData,'url',data.downUrl)
322 381 // tempObjItem.repDesc = data.description
323   - // tempObjItem.repUrl = data.downUrl
  382 + // tempObjItem.url = data.downUrl
324 383 // }
325 384 // })
326 385 }
327   - this.tempObj.set(this.objData.repType,tempObjItem)
  386 + // 最终设置到临时缓存
  387 + this.tempObj.set(this.objData.type, tempObjItem)
328 388 },
329   - openMaterial(){
330   - if(this.objData.repType == 'news'){
  389 + openMaterial() {
  390 + if (this.objData.type === 'news') {
331 391 this.dialogNewsVisible = true
332   - }else if(this.objData.repType == 'image'){
  392 + } else if(this.objData.type === 'image') {
333 393 this.dialogImageVisible = true
334   - }else if(this.objData.repType == 'voice'){
  394 + } else if(this.objData.type === 'voice') {
335 395 this.dialogVoiceVisible = true
336   - }else if(this.objData.repType == 'video'){
  396 + } else if(this.objData.type === 'video') {
337 397 this.dialogVideoVisible = true
338   - }else if(this.objData.repType == 'music'){
  398 + } else if(this.objData.type === 'music') {
339 399 this.dialogThumbVisible = true
340 400 }
341 401 },
  402 + closeMaterial() {
  403 + this.dialogNewsVisible = false
  404 + this.dialogImageVisible = false
  405 + this.dialogVoiceVisible = false
  406 + this.dialogVideoVisible = false
  407 + },
  408 + deleteObj() {
  409 + if (this.objData.type === 'news') {
  410 + // TODO 芋艿,待实现
  411 + } else if(this.objData.type === 'image') {
  412 + this.$delete(this.objData, 'url')
  413 + this.$delete(this.objData, 'mediaId')
  414 + } else if(this.objData.type === 'voice') {
  415 + // TODO 芋艿,待实现
  416 + } else if(this.objData.type === 'video') {
  417 + // TODO 芋艿,待实现
  418 + } else if(this.objData.type === 'music') {
  419 + // TODO 芋艿,待实现
  420 + }
  421 + },
342 422 getPage(page, params) {
343 423 this.tableLoading = true
344 424 // getPage(Object.assign({
345 425 // current: page.currentPage,
346 426 // size: page.pageSize,
347   - // type:this.objData.repType
  427 + // type:this.objData.type
348 428 // }, params)).then(response => {
349 429 // this.tableData = response.data.items
350 430 // this.page.total = response.data.totalCount
... ... @@ -357,6 +437,15 @@
357 437 this.page.currentPage = 1
358 438 this.page.pageSize = val
359 439 this.getPage(this.page)
  440 + },
  441 + /**
  442 + * 输入时,缓存每次 objData 到 tempObj 中
  443 + *
  444 + * why?不确定为什么 v-model="objData.content" 不能自动缓存,所以通过这样的方式
  445 + */
  446 + inputContent() {
  447 + let tempObjItem = {...this.objData};
  448 + this.tempObj.set(this.objData.type, tempObjItem);
360 449 }
361 450 }
362 451 };
... ...