zn-admin-vue3-wcs/src/views/mp/components/wx-reply/main.vue
2023-04-09 05:16:23 +00:00

691 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
移除多余的 rep 为前缀的变量 message 消息更简单
代码优化补充注释提升阅读性
优化消息的临时缓存策略发送消息时只清理被发送消息的 tab不会强制切回到 text 输入
支持发送视频消息时支持新建视频
-->
<template>
<el-tabs type="border-card" v-model="objDataRef.type" @tab-click="handleClick">
<!-- 类型 1文本 -->
<el-tab-pane name="text">
<template #label>
<el-row align="middle"><Icon icon="ep:document" /> 文本</el-row>
</template>
<el-input
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="objDataRef.content"
@input="inputContent"
/>
</el-tab-pane>
<!-- 类型 2图片 -->
<el-tab-pane name="image">
<template #label>
<el-row align="middle"><Icon icon="ep:picture" class="mr-5px" /> 图片</el-row>
</template>
<!-- 情况一:已经选择好素材、或者上传好图片 -->
<div class="select-item" v-if="objDataRef.url">
<img class="material-img" :src="objDataRef.url" />
<p class="item-name" v-if="objDataRef.name">{{ objDataRef.name }}</p>
<el-row class="ope-row" justify="center">
<el-button type="danger" circle @click="deleteObj">
<Icon icon="ep:delete" />
</el-button>
</el-row>
</div>
<!-- 情况二:未做完上述操作 -->
<el-row v-else style="text-align: center" align="middle">
<!-- 选择素材 -->
<el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial">
素材库选择 <Icon icon="ep:circle-check" />
</el-button>
<el-dialog title="选择图片" v-model="dialogImageVisible" width="90%" append-to-body>
<WxMaterialSelect :obj-data="objDataRef" @select-material="selectMaterial" />
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12" class="col-add">
<el-upload
:action="actionUrl"
:headers="headers"
multiple
:limit="1"
:file-list="fileList"
:data="uploadData"
:before-upload="beforeImageUpload"
:on-success="handleUploadSuccess"
>
<el-button type="primary">上传图片</el-button>
<template #tip>
<span>
<div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div>
</span>
</template>
</el-upload>
</el-col>
</el-row>
</el-tab-pane>
<!-- 类型 3语音 -->
<el-tab-pane name="voice">
<template #label>
<el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row>
</template>
<div class="select-item2" v-if="objDataRef.url">
<p class="item-name">{{ objDataRef.name }}</p>
<div class="item-infos">
<WxVoicePlayer :url="objDataRef.url" />
</div>
<el-row class="ope-row" justify="center">
<el-button type="danger" circle @click="deleteObj"><Icon icon="ep:delete" /></el-button>
</el-row>
</div>
<el-row v-else style="text-align: center">
<!-- 选择素材 -->
<el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial">
素材库选择<Icon icon="ep:circle-check" />
</el-button>
<el-dialog title="选择语音" v-model="dialogVoiceVisible" width="90%" append-to-body>
<WxMaterialSelect :objData="objData" @select-material="selectMaterial" />
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12" class="col-add">
<el-upload
:action="actionUrl"
:headers="headers"
multiple
:limit="1"
:file-list="fileList"
:data="uploadData"
:before-upload="beforeVoiceUpload"
:on-success="handleUploadSuccess"
>
<el-button type="primary">点击上传</el-button>
<template #tip>
<div class="el-upload__tip">
格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s
</div>
</template>
</el-upload>
</el-col>
</el-row>
</el-tab-pane>
<!-- 类型 4视频 -->
<el-tab-pane name="video">
<template #label>
<el-row align="middle"><Icon icon="ep:share" /> 视频</el-row>
</template>
<el-row>
<el-input
v-model="objDataRef.title"
class="input-margin-bottom"
placeholder="请输入标题"
@input="inputContent"
/>
<el-input
class="input-margin-bottom"
v-model="objDataRef.description"
placeholder="请输入描述"
@input="inputContent"
/>
<div style="text-align: center">
<WxVideoPlayer v-if="objDataRef.url" :url="objDataRef.url" />
</div>
<el-col>
<el-row style="text-align: center" align="middle">
<!-- 选择素材 -->
<el-col :span="12">
<el-button type="success" @click="openMaterial">
素材库选择 <Icon icon="ep:circle-check" />
</el-button>
<el-dialog title="选择视频" v-model="dialogVideoVisible" width="90%" append-to-body>
<WxMaterialSelect :objData="objDataRef" @select-material="selectMaterial" />
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12">
<el-upload
:action="actionUrl"
:headers="headers"
multiple
:limit="1"
:file-list="fileList"
:data="uploadData"
:before-upload="beforeVideoUpload"
:on-success="handleUploadSuccess"
>
<el-button type="primary">新建视频 <Icon icon="ep:upload" /></el-button>
</el-upload>
</el-col>
</el-row>
</el-col>
</el-row>
</el-tab-pane>
<!-- 类型 5图文 -->
<el-tab-pane name="news">
<template #label>
<el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row>
</template>
<el-row>
<div class="select-item" v-if="objDataRef.articles?.length > 0">
<WxNews :articles="objDataRef.articles" />
<el-col class="ope-row">
<el-button type="danger" circle @click="deleteObj">
<Icon icon="ep:delete" />
</el-button>
</el-col>
</div>
<!-- 选择素材 -->
<el-col :span="24" v-if="!objDataRef.content">
<el-row style="text-align: center" align="middle">
<el-col :span="24">
<el-button type="success" @click="openMaterial">
{{ newsType === '1' ? '选择已发布图文' : '选择草稿箱图文' }}
<icon icon="ep:circle-check" />
</el-button>
</el-col>
</el-row>
</el-col>
<el-dialog title="选择图文" v-model="dialogNewsVisible" width="90%" append-to-body>
<WxMaterialSelect
:objData="objDataRef"
@select-material="selectMaterial"
:newsType="newsType"
/>
</el-dialog>
</el-row>
</el-tab-pane>
<!-- 类型 6音乐 -->
<el-tab-pane name="music">
<template #label>
<el-row align="middle"><Icon icon="ep:service" />音乐</el-row>
</template>
<el-row align="middle" justify="center">
<el-col :span="6">
<el-row align="middle" justify="center" class="thumb-div">
<el-col :span="24">
<el-row align="middle" justify="center">
<img
style="width: 100px"
v-if="objDataRef.thumbMediaUrl"
:src="objDataRef.thumbMediaUrl"
/>
<icon v-else icon="ep:plus" />
</el-row>
<el-row align="middle" justify="center" style="margin-top: 2%">
<div class="thumb-but">
<el-upload
:action="actionUrl"
:headers="headers"
multiple
:limit="1"
:file-list="fileList"
:data="uploadData"
:before-upload="beforeThumbImageUpload"
:on-success="handleUploadSuccess"
>
<template #trigger>
<el-button type="text">本地上传</el-button>
</template>
<el-button type="primary" link @click="openMaterial" style="margin-left: 5px"
>素材库选择
</el-button>
</el-upload>
</div>
</el-row>
</el-col>
</el-row>
<el-dialog title="选择图片" v-model="dialogThumbVisible" width="80%" append-to-body>
<WxMaterialSelect
:objData="{ type: 'image', accountId: objDataRef.accountId }"
@select-material="selectMaterial"
/>
</el-dialog>
</el-col>
<el-col :span="18">
<el-input v-model="objDataRef.title" placeholder="请输入标题" @input="inputContent" />
<div style="margin: 20px 0"></div>
<el-input
v-model="objDataRef.description"
placeholder="请输入描述"
@input="inputContent"
/>
</el-col>
</el-row>
<div style="margin: 20px 0"></div>
<el-input v-model="objDataRef.musicUrl" placeholder="请输入音乐链接" @input="inputContent" />
<div style="margin: 20px 0"></div>
<el-input
v-model="objDataRef.hqMusicUrl"
placeholder="请输入高质量音乐链接"
@input="inputContent"
/>
</el-tab-pane>
</el-tabs>
</template>
<script lang="ts" name="WxReplySelect">
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
import { getAccessToken } from '@/utils/auth'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
WxNews,
WxMaterialSelect,
WxVoicePlayer,
WxVideoPlayer
},
props: {
objData: {
// 消息对象
type: Object, // 设置为 Object 的原因方便属性的传递
required: true
},
newsType: {
// 图文类型1已发布图文2草稿箱图文
type: String,
default: '1'
}
},
setup(props) {
const objDataRef = reactive(props.objData)
const message = useMessage() // 消息弹窗
const tempObj = new Map().set(objDataRef.type, Object.assign({}, objDataRef))
// ========== 素材选择的弹窗是否可见 ==========
const dialogNewsVisible = ref(false) // 图文
const dialogImageVisible = ref(false) // 图片
const dialogVoiceVisible = ref(false) // 语音
const dialogVideoVisible = ref(false) // 视频
const dialogThumbVisible = ref(false) // 缩略图
// ========== 文件上传图片语音视频 ==========
const fileList = ref([])
const uploadData = reactive({
accountId: undefined,
type: objDataRef.type,
title: '',
introduction: ''
})
const actionUrl = ref(
import.meta.env.VITE_API_BASEPATH + '/admin-api/mp/material/upload-temporary'
)
const headers = ref({ Authorization: 'Bearer ' + getAccessToken() }) // 设置上传的请求头部
const beforeThumbImageUpload = (file) => {
const isType =
file.type === 'image/jpeg' ||
file.type === 'image/png' ||
file.type === 'image/gif' ||
file.type === 'image/bmp' ||
file.type === 'image/jpg'
if (!isType) {
message.error('上传图片格式不对!')
return false
}
const isLt = file.size / 1024 / 1024 < 2
if (!isLt) {
message.error('上传图片大小不能超过 2M!')
return false
}
uploadData.accountId = objDataRef.accountId
return true
}
const beforeVoiceUpload = (file) => {
// 校验格式
const isType =
file.type === 'audio/mp3' ||
file.type === 'audio/mpeg' ||
file.type === 'audio/wma' ||
file.type === 'audio/wav' ||
file.type === 'audio/amr'
if (!isType) {
message.error('上传语音格式不对!' + file.type)
return false
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 2
if (!isLt) {
message.error('上传语音大小不能超过 2M!')
return false
}
uploadData.accountId = objDataRef.accountId
return true
}
const beforeImageUpload = (file) => {
// 校验格式
const isType =
file.type === 'image/jpeg' ||
file.type === 'image/png' ||
file.type === 'image/gif' ||
file.type === 'image/bmp' ||
file.type === 'image/jpg'
if (!isType) {
message.error('上传图片格式不对!')
return false
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 2
if (!isLt) {
message.error('上传图片大小不能超过 2M!')
return false
}
uploadData.accountId = objDataRef.accountId
return true
}
const beforeVideoUpload = (file) => {
// 校验格式
const isType = file.type === 'video/mp4'
if (!isType) {
message.error('上传视频格式不对!')
return false
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 10
if (!isLt) {
message.error('上传视频大小不能超过 10M!')
return false
}
uploadData.accountId = objDataRef.accountId
return true
}
const handleUploadSuccess = (response) => {
if (response.code !== 0) {
message.error('上传出错:' + response.msg)
return false
}
// 清空上传时的各种数据
fileList.value = []
uploadData.title = ''
uploadData.introduction = ''
// 上传好的文件,本质是个素材,所以可以进行选中
let item = response.data
selectMaterial(item)
}
/**
* 切换消息类型的 tab
*
* @param tab tab 没用 暂时删了tab
*/
const handleClick = () => {
// 设置后续文件上传的文件类型
uploadData.type = objDataRef.type
if (uploadData.type === 'music') {
// 【音乐】上传的是缩略图
uploadData.type = 'thumb'
}
// 从 tempObj 临时缓存中,获取对应的数据,并设置回 objDataRef
let tempObjItem = tempObj.get(objDataRef.type)
if (tempObjItem) {
objDataRef.content = tempObjItem.content ? tempObjItem.content : null
objDataRef.mediaId = tempObjItem.mediaId ? tempObjItem.mediaId : null
objDataRef.url = tempObjItem.url ? tempObjItem.url : null
objDataRef.name = tempObjItem.url ? tempObjItem.name : null
objDataRef.title = tempObjItem.title ? tempObjItem.title : null
objDataRef.description = tempObjItem.description ? tempObjItem.description : null
return
}
// 如果获取不到,需要把 objDataRef 复原
// 必须使用 $set 赋值,不然 input 无法输入内容
objDataRef.content = ''
objDataRef.mediaId = ''
objDataRef.url = ''
objDataRef.title = ''
objDataRef.description = ''
}
/**
* 选择素材,将设置设置到 objDataRef 变量
*
* @param item 素材
*/
const selectMaterial = (item) => {
// 选择好素材,所以隐藏弹窗
closeMaterial()
// 创建 tempObjItem 对象,并设置对应的值
let tempObjItem = {
type: '',
articles: [],
thumbMediaId: '',
thumbMediaUrl: '',
introduction: '',
title: '',
musicUrl: '',
hqMusicUrl: '',
mediaId: '',
url: '',
name: '',
description: ''
}
tempObjItem.type = objDataRef.type
if (objDataRef.type === 'news') {
tempObjItem.articles = item.content.newsItem
objDataRef.articles = item.content.newsItem
} else if (objDataRef.type === 'music') {
// 音乐需要特殊处理,因为选择的是图片的缩略图
tempObjItem.thumbMediaId = item.mediaId
objDataRef.thumbMediaId = item.mediaId
tempObjItem.thumbMediaUrl = item.url
objDataRef.thumbMediaUrl = item.url
// title、introduction、musicUrl、hqMusicUrl从 objDataRef 到 tempObjItem避免上传素材后被覆盖掉
tempObjItem.title = objDataRef.title || ''
tempObjItem.introduction = objDataRef.introduction || ''
tempObjItem.musicUrl = objDataRef.musicUrl || ''
tempObjItem.hqMusicUrl = objDataRef.hqMusicUrl || ''
} else if (objDataRef.type === 'image' || objDataRef.type === 'voice') {
tempObjItem.mediaId = item.mediaId
objDataRef.mediaId = item.mediaId
tempObjItem.url = item.url
objDataRef.url = item.url
tempObjItem.name = item.name
objDataRef.name = item.name
} else if (objDataRef.type === 'video') {
tempObjItem.mediaId = item.mediaId
objDataRef.mediaId = item.mediaId
tempObjItem.url = item.url
objDataRef.url = item.url
tempObjItem.name = item.name
objDataRef.name = item.name
// title、introduction从 item 到 tempObjItem因为素材里有 title、introduction
if (item.title) {
objDataRef.title = item.title || ''
tempObjItem.title = item.title || ''
}
if (item.introduction) {
objDataRef.description = item.introduction || '' // 消息使用的是 description素材使用的是 introduction所以转换下
tempObjItem.description = item.introduction || ''
}
} else if (objDataRef.type === 'text') {
objDataRef.content = item.content || ''
}
// 最终设置到临时缓存
tempObj.set(objDataRef.type, tempObjItem)
}
const openMaterial = () => {
if (objDataRef.type === 'news') {
dialogNewsVisible.value = true
} else if (objDataRef.type === 'image') {
dialogImageVisible.value = true
} else if (objDataRef.type === 'voice') {
dialogVoiceVisible.value = true
} else if (objDataRef.type === 'video') {
dialogVideoVisible.value = true
} else if (objDataRef.type === 'music') {
dialogThumbVisible.value = true
}
}
const closeMaterial = () => {
dialogNewsVisible.value = false
dialogImageVisible.value = false
dialogVoiceVisible.value = false
dialogVideoVisible.value = false
dialogThumbVisible.value = false
}
const deleteObj = () => {
if (objDataRef.type === 'news') {
objDataRef.articles = []
} else if (objDataRef.type === 'image') {
objDataRef.mediaId = null
objDataRef.url = null
objDataRef.name = null
} else if (objDataRef.type === 'voice') {
objDataRef.mediaId = null
objDataRef.url = null
objDataRef.name = null
} else if (objDataRef.type === 'video') {
objDataRef.mediaId = null
objDataRef.url = null
objDataRef.name = null
objDataRef.title = null
objDataRef.description = null
} else if (objDataRef.type === 'music') {
objDataRef.thumbMediaId = null
objDataRef.thumbMediaUrl = null
objDataRef.title = null
objDataRef.description = null
objDataRef.musicUrl = null
objDataRef.hqMusicUrl = null
} else if (objDataRef.type === 'text') {
objDataRef.content = null
}
// 覆盖缓存
tempObj.set(objDataRef.type, Object.assign({}, objDataRef))
}
/**
* 输入时,缓存每次 objDataRef 到 tempObj 中
*
* why不确定为什么 v-model="objDataRef.content" 不能自动缓存,所以通过这样的方式
*/
const inputContent = () => {
// 覆盖缓存
tempObj.set(objDataRef.type, Object.assign({}, objDataRef))
}
return {
inputContent,
dialogNewsVisible,
deleteObj,
openMaterial,
handleClick,
beforeImageUpload,
beforeVoiceUpload,
handleUploadSuccess,
beforeVideoUpload,
selectMaterial,
dialogImageVisible,
dialogVoiceVisible,
dialogThumbVisible,
actionUrl,
objDataRef,
headers,
fileList,
beforeThumbImageUpload,
uploadData,
dialogVideoVisible
}
}
})
</script>
<style lang="scss" scoped>
.public-account-management {
.el-input {
width: 70%;
margin-right: 2%;
}
}
.pagination {
text-align: right;
margin-right: 25px;
}
.select-item {
width: 280px;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.select-item2 {
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.ope-row {
padding-top: 10px;
text-align: center;
}
.input-margin-bottom {
margin-bottom: 2%;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.el-form-item__content {
line-height: unset !important;
}
.col-select {
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
width: 49.5%;
}
.col-select2 {
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
}
.col-add {
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
width: 49.5%;
float: right;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 100px !important;
height: 100px !important;
line-height: 100px !important;
text-align: center;
}
.material-img {
width: 100%;
}
.thumb-div {
display: inline-block;
text-align: center;
}
.item-infos {
width: 30%;
margin: auto;
}
</style>