mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-03 03:38:44 +08:00 
			
		
		
		
	refactor: MP消息管理 ts重构
This commit is contained in:
		@@ -39,79 +39,79 @@
 | 
				
			|||||||
              :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''"
 | 
					              :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              <!-- 【事件】区域 -->
 | 
					              <!-- 【事件】区域 -->
 | 
				
			||||||
              <div v-if="item.type === 'event' && item.event === 'subscribe'">
 | 
					              <div v-if="item.type === MsgType.Event && item.event === 'subscribe'">
 | 
				
			||||||
                <el-tag type="success">关注</el-tag>
 | 
					                <el-tag type="success">关注</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'unsubscribe'">
 | 
				
			||||||
                <el-tag type="danger">取消关注</el-tag>
 | 
					                <el-tag type="danger">取消关注</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'CLICK'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'CLICK'">
 | 
				
			||||||
                <el-tag>点击菜单</el-tag>
 | 
					                <el-tag>点击菜单</el-tag>
 | 
				
			||||||
                【{{ item.eventKey }}】
 | 
					                【{{ item.eventKey }}】
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'VIEW'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'VIEW'">
 | 
				
			||||||
                <el-tag>点击菜单链接</el-tag>
 | 
					                <el-tag>点击菜单链接</el-tag>
 | 
				
			||||||
                【{{ item.eventKey }}】
 | 
					                【{{ item.eventKey }}】
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'scancode_waitmsg'">
 | 
				
			||||||
                <el-tag>扫码结果</el-tag>
 | 
					                <el-tag>扫码结果</el-tag>
 | 
				
			||||||
                【{{ item.eventKey }}】
 | 
					                【{{ item.eventKey }}】
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'scancode_push'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'scancode_push'">
 | 
				
			||||||
                <el-tag>扫码结果</el-tag>
 | 
					                <el-tag>扫码结果</el-tag>
 | 
				
			||||||
                【{{ item.eventKey }}】
 | 
					                【{{ item.eventKey }}】
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'pic_sysphoto'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'pic_sysphoto'">
 | 
				
			||||||
                <el-tag>系统拍照发图</el-tag>
 | 
					                <el-tag>系统拍照发图</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'pic_photo_or_album'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'pic_photo_or_album'">
 | 
				
			||||||
                <el-tag>拍照或者相册</el-tag>
 | 
					                <el-tag>拍照或者相册</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'pic_weixin'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'pic_weixin'">
 | 
				
			||||||
                <el-tag>微信相册</el-tag>
 | 
					                <el-tag>微信相册</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event' && item.event === 'location_select'">
 | 
					              <div v-else-if="item.type === MsgType.Event && item.event === 'location_select'">
 | 
				
			||||||
                <el-tag>选择地理位置</el-tag>
 | 
					                <el-tag>选择地理位置</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'event'">
 | 
					              <div v-else-if="item.type === MsgType.Event">
 | 
				
			||||||
                <el-tag type="danger">未知事件类型</el-tag>
 | 
					                <el-tag type="danger">未知事件类型</el-tag>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <!-- 【消息】区域 -->
 | 
					              <!-- 【消息】区域 -->
 | 
				
			||||||
              <div v-else-if="item.type === 'text'">{{ item.content }}</div>
 | 
					              <div v-else-if="item.type === MsgType.Text">{{ item.content }}</div>
 | 
				
			||||||
              <div v-else-if="item.type === 'voice'">
 | 
					              <div v-else-if="item.type === MsgType.Voice">
 | 
				
			||||||
                <wx-voice-player :url="item.mediaUrl" :content="item.recognition" />
 | 
					                <WxVoicePlayer :url="item.mediaUrl" :content="item.recognition" />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'image'">
 | 
					              <div v-else-if="item.type === MsgType.Image">
 | 
				
			||||||
                <a target="_blank" :href="item.mediaUrl">
 | 
					                <a target="_blank" :href="item.mediaUrl">
 | 
				
			||||||
                  <img :src="item.mediaUrl" style="width: 100px" />
 | 
					                  <img :src="item.mediaUrl" style="width: 100px" />
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div
 | 
					              <div
 | 
				
			||||||
                v-else-if="item.type === 'video' || item.type === 'shortvideo'"
 | 
					                v-else-if="item.type === MsgType.Video || item.type === 'shortvideo'"
 | 
				
			||||||
                style="text-align: center"
 | 
					                style="text-align: center"
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
                <wx-video-player :url="item.mediaUrl" />
 | 
					                <WxVideoPlayer :url="item.mediaUrl" />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'link'" class="avue-card__detail">
 | 
					              <div v-else-if="item.type === MsgType.Link" class="avue-card__detail">
 | 
				
			||||||
                <el-link type="success" :underline="false" target="_blank" :href="item.url">
 | 
					                <el-link type="success" :underline="false" target="_blank" :href="item.url">
 | 
				
			||||||
                  <div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
 | 
					                  <div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
 | 
				
			||||||
                </el-link>
 | 
					                </el-link>
 | 
				
			||||||
                <div class="avue-card__info" style="height: unset">{{ item.description }}</div>
 | 
					                <div class="avue-card__info" style="height: unset">{{ item.description }}</div>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <!-- TODO 芋艿:待完善 -->
 | 
					              <!-- TODO 芋艿:待完善 -->
 | 
				
			||||||
              <div v-else-if="item.type === 'location'">
 | 
					              <div v-else-if="item.type === MsgType.Location">
 | 
				
			||||||
                <wx-location
 | 
					                <WxLocation
 | 
				
			||||||
                  :label="item.label"
 | 
					                  :label="item.label"
 | 
				
			||||||
                  :location-y="item.locationY"
 | 
					                  :location-y="item.locationY"
 | 
				
			||||||
                  :location-x="item.locationX"
 | 
					                  :location-x="item.locationX"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'news'" style="width: 300px">
 | 
					              <div v-else-if="item.type === MsgType.News" style="width: 300px">
 | 
				
			||||||
                <!-- TODO 芋艿:待测试;详情页也存在类似的情况 -->
 | 
					                <!-- TODO 芋艿:待测试;详情页也存在类似的情况 -->
 | 
				
			||||||
                <wx-news :articles="item.articles" />
 | 
					                <WxNews :articles="item.articles" />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div v-else-if="item.type === 'music'">
 | 
					              <div v-else-if="item.type === MsgType.Music">
 | 
				
			||||||
                <wx-music
 | 
					                <WxMusic
 | 
				
			||||||
                  :title="item.title"
 | 
					                  :title="item.title"
 | 
				
			||||||
                  :description="item.description"
 | 
					                  :description="item.description"
 | 
				
			||||||
                  :thumb-media-url="item.thumbMediaUrl"
 | 
					                  :thumb-media-url="item.thumbMediaUrl"
 | 
				
			||||||
@@ -125,44 +125,35 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="msg-send" v-loading="sendLoading">
 | 
					    <div class="msg-send" v-loading="sendLoading">
 | 
				
			||||||
      <wx-reply-select ref="replySelect" :objData="objData" />
 | 
					      <WxReplySelect ref="replySelectRef" :objData="objData" />
 | 
				
			||||||
      <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
 | 
					      <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </ContentWrap>
 | 
					  </ContentWrap>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" name="WxMsg">
 | 
					<script setup lang="ts" name="WxMsg">
 | 
				
			||||||
import { getMessagePage, sendMessage } from '@/api/mp/message'
 | 
					 | 
				
			||||||
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
 | 
					import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
 | 
				
			||||||
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
 | 
					import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
 | 
				
			||||||
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
 | 
					import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
 | 
				
			||||||
import WxNews from '@/views/mp/components/wx-news/main.vue'
 | 
					import WxNews from '@/views/mp/components/wx-news/main.vue'
 | 
				
			||||||
import WxLocation from '@/views/mp/components/wx-location/main.vue'
 | 
					import WxLocation from '@/views/mp/components/wx-location/main.vue'
 | 
				
			||||||
import WxMusic from '@/views/mp/components/wx-music/main.vue'
 | 
					import WxMusic from '@/views/mp/components/wx-music/main.vue'
 | 
				
			||||||
 | 
					import { getMessagePage, sendMessage } from '@/api/mp/message'
 | 
				
			||||||
import { getUser } from '@/api/mp/user'
 | 
					import { getUser } from '@/api/mp/user'
 | 
				
			||||||
import { defineComponent } from 'vue'
 | 
					import { formatDate } from '@/utils/formatTime'
 | 
				
			||||||
 | 
					 | 
				
			||||||
const message = useMessage() // 消息弹窗
 | 
					 | 
				
			||||||
import profile from '@/assets/imgs/profile.jpg'
 | 
					import profile from '@/assets/imgs/profile.jpg'
 | 
				
			||||||
import wechat from '@/assets/imgs/wechat.png'
 | 
					import wechat from '@/assets/imgs/wechat.png'
 | 
				
			||||||
import { formatDate } from '@/utils/formatTime'
 | 
					import { MsgType } from './types'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					const message = useMessage() // 消息弹窗
 | 
				
			||||||
  components: {
 | 
					
 | 
				
			||||||
    WxReplySelect,
 | 
					const props = defineProps({
 | 
				
			||||||
    WxVideoPlayer,
 | 
					 | 
				
			||||||
    WxVoicePlayer,
 | 
					 | 
				
			||||||
    WxNews,
 | 
					 | 
				
			||||||
    WxLocation,
 | 
					 | 
				
			||||||
    WxMusic
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  props: {
 | 
					 | 
				
			||||||
  userId: {
 | 
					  userId: {
 | 
				
			||||||
    type: Number,
 | 
					    type: Number,
 | 
				
			||||||
    required: true
 | 
					    required: true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  },
 | 
					})
 | 
				
			||||||
  setup(props) {
 | 
					
 | 
				
			||||||
const nowStr = ref(new Date().getTime()) // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
 | 
					const nowStr = ref(new Date().getTime()) // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
 | 
				
			||||||
const loading = ref(false) // 消息列表是否正在加载中
 | 
					const loading = ref(false) // 消息列表是否正在加载中
 | 
				
			||||||
const loadMore = ref(true) // 是否可以加载更多
 | 
					const loadMore = ref(true) // 是否可以加载更多
 | 
				
			||||||
@@ -172,52 +163,87 @@ export default defineComponent({
 | 
				
			|||||||
  pageSize: 14, // 每页显示多少条
 | 
					  pageSize: 14, // 每页显示多少条
 | 
				
			||||||
  accountId: undefined
 | 
					  accountId: undefined
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
    const user = reactive({
 | 
					
 | 
				
			||||||
 | 
					interface User {
 | 
				
			||||||
 | 
					  nickname: string
 | 
				
			||||||
 | 
					  avatar: string
 | 
				
			||||||
 | 
					  accountId: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
// 由于微信不再提供昵称,直接使用“用户”展示
 | 
					// 由于微信不再提供昵称,直接使用“用户”展示
 | 
				
			||||||
 | 
					const user: User = reactive({
 | 
				
			||||||
  nickname: '用户',
 | 
					  nickname: '用户',
 | 
				
			||||||
  avatar: profile,
 | 
					  avatar: profile,
 | 
				
			||||||
  accountId: 0 // 公众号账号编号
 | 
					  accountId: 0 // 公众号账号编号
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
    const mp = reactive({
 | 
					
 | 
				
			||||||
 | 
					interface Mp {
 | 
				
			||||||
 | 
					  nickname: string
 | 
				
			||||||
 | 
					  avatar: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const mp: Mp = reactive({
 | 
				
			||||||
  nickname: '公众号',
 | 
					  nickname: '公众号',
 | 
				
			||||||
  avatar: wechat
 | 
					  avatar: wechat
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ========= 消息发送 =========
 | 
					// ========= 消息发送 =========
 | 
				
			||||||
const sendLoading = ref(false) // 发送消息是否加载中
 | 
					const sendLoading = ref(false) // 发送消息是否加载中
 | 
				
			||||||
    const objData = reactive({
 | 
					interface ObjData {
 | 
				
			||||||
 | 
					  type: MsgType
 | 
				
			||||||
 | 
					  accountId: number | null
 | 
				
			||||||
 | 
					  articles: any[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 微信发送消息
 | 
					// 微信发送消息
 | 
				
			||||||
      type: 'text',
 | 
					const objData: ObjData = reactive({
 | 
				
			||||||
 | 
					  type: MsgType.Text,
 | 
				
			||||||
  accountId: null,
 | 
					  accountId: null,
 | 
				
			||||||
  articles: []
 | 
					  articles: []
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const replySelect = ref(null)
 | 
					const replySelectRef = ref<InstanceType<typeof WxReplySelect> | null>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 完成加载 */
 | 
				
			||||||
 | 
					onMounted(async () => {
 | 
				
			||||||
 | 
					  const data = await getUser(props.userId)
 | 
				
			||||||
 | 
					  user.nickname = data.nickname?.length > 0 ? data.nickname : user.nickname
 | 
				
			||||||
 | 
					  user.avatar = user.avatar?.length > 0 ? data.avatar : user.avatar
 | 
				
			||||||
 | 
					  user.accountId = data.accountId
 | 
				
			||||||
 | 
					  queryParams.accountId = data.accountId
 | 
				
			||||||
 | 
					  objData.accountId = data.accountId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  refreshChange()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 执行发送
 | 
					// 执行发送
 | 
				
			||||||
const sendMsg = async () => {
 | 
					const sendMsg = async () => {
 | 
				
			||||||
  if (!objData) {
 | 
					  if (!objData) {
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
      //     // 公众号限制:客服消息,公众号只允许发送一条
 | 
					  // 公众号限制:客服消息,公众号只允许发送一条
 | 
				
			||||||
      if (objData.type === 'news' && objData.articles.length > 1) {
 | 
					  if (objData.type === MsgType.News && objData.articles.length > 1) {
 | 
				
			||||||
    objData.articles = [objData.articles[0]]
 | 
					    objData.articles = [objData.articles[0]]
 | 
				
			||||||
    message.success('图文消息条数限制在 1 条以内,已默认发送第一条')
 | 
					    message.success('图文消息条数限制在 1 条以内,已默认发送第一条')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
      let data = await sendMessage(Object.assign({ userId: props.userId }, { ...objData }))
 | 
					
 | 
				
			||||||
 | 
					  const data = await sendMessage(Object.assign({ userId: props.userId }, { ...objData }))
 | 
				
			||||||
  sendLoading.value = false
 | 
					  sendLoading.value = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  list.value = [...list.value, ...[data]]
 | 
					  list.value = [...list.value, ...[data]]
 | 
				
			||||||
  scrollToBottom()
 | 
					  scrollToBottom()
 | 
				
			||||||
      //ts檢查的時候會判斷這個組件可能是空的,所以需要進行斷言。
 | 
					
 | 
				
			||||||
 | 
					  //ts检查的時候会判断这个组件可能是空的,所以需要进行断言。
 | 
				
			||||||
  //避免 tab 的数据未清理
 | 
					  //避免 tab 的数据未清理
 | 
				
			||||||
      const deleteObj = (replySelect.value as any).deleteObj
 | 
					  const deleteObj = replySelectRef.value?.deleteObj
 | 
				
			||||||
  if (deleteObj) {
 | 
					  if (deleteObj) {
 | 
				
			||||||
    deleteObj()
 | 
					    deleteObj()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const loadingMore = () => {
 | 
					const loadingMore = () => {
 | 
				
			||||||
  queryParams.pageNo++
 | 
					  queryParams.pageNo++
 | 
				
			||||||
  getPage(queryParams, null)
 | 
					  getPage(queryParams, null)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getPage = async (page, params) => {
 | 
					const getPage = async (page, params) => {
 | 
				
			||||||
  loading.value = true
 | 
					  loading.value = true
 | 
				
			||||||
  let dataTemp = await getMessagePage(
 | 
					  let dataTemp = await getMessagePage(
 | 
				
			||||||
@@ -231,13 +257,14 @@ export default defineComponent({
 | 
				
			|||||||
      params
 | 
					      params
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const msgDiv = document.getElementById('msg-div' + nowStr.value)
 | 
					  const msgDiv = document.getElementById('msg-div' + nowStr.value)
 | 
				
			||||||
  let scrollHeight = 0
 | 
					  let scrollHeight = 0
 | 
				
			||||||
  if (msgDiv) {
 | 
					  if (msgDiv) {
 | 
				
			||||||
    scrollHeight = msgDiv.scrollHeight
 | 
					    scrollHeight = msgDiv.scrollHeight
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // 处理数据
 | 
					  // 处理数据
 | 
				
			||||||
      let data = dataTemp.list.reverse()
 | 
					  const data = dataTemp.list.reverse()
 | 
				
			||||||
  list.value = [...data, ...list.value]
 | 
					  list.value = [...data, ...list.value]
 | 
				
			||||||
  loading.value = false
 | 
					  loading.value = false
 | 
				
			||||||
  if (data.length < queryParams.pageSize || data.length === 0) {
 | 
					  if (data.length < queryParams.pageSize || data.length === 0) {
 | 
				
			||||||
@@ -261,9 +288,11 @@ export default defineComponent({
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const refreshChange = () => {
 | 
					const refreshChange = () => {
 | 
				
			||||||
  getPage(queryParams, null)
 | 
					  getPage(queryParams, null)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 定位到消息底部 */
 | 
					/** 定位到消息底部 */
 | 
				
			||||||
const scrollToBottom = () => {
 | 
					const scrollToBottom = () => {
 | 
				
			||||||
  nextTick(() => {
 | 
					  nextTick(() => {
 | 
				
			||||||
@@ -273,34 +302,8 @@ export default defineComponent({
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
    onMounted(async () => {
 | 
					 | 
				
			||||||
      let data = await getUser(props.userId)
 | 
					 | 
				
			||||||
      user.nickname = data.nickname && data.nickname.length > 0 ? data.nickname : user.nickname
 | 
					 | 
				
			||||||
      user.avatar = data.avatar && user.avatar.length > 0 ? data.avatar : user.avatar
 | 
					 | 
				
			||||||
      user.accountId = data.accountId
 | 
					 | 
				
			||||||
      queryParams.accountId = data.accountId
 | 
					 | 
				
			||||||
      objData.accountId = data.accountId
 | 
					 | 
				
			||||||
      refreshChange()
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      sendMsg,
 | 
					 | 
				
			||||||
      loadingMore,
 | 
					 | 
				
			||||||
      formatDate,
 | 
					 | 
				
			||||||
      scrollToBottom,
 | 
					 | 
				
			||||||
      objData,
 | 
					 | 
				
			||||||
      mp,
 | 
					 | 
				
			||||||
      user,
 | 
					 | 
				
			||||||
      queryParams,
 | 
					 | 
				
			||||||
      list,
 | 
					 | 
				
			||||||
      loadMore,
 | 
					 | 
				
			||||||
      loading,
 | 
					 | 
				
			||||||
      nowStr,
 | 
					 | 
				
			||||||
      sendLoading
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc  */
 | 
					/* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc  */
 | 
				
			||||||
@import './comment.scss';
 | 
					@import './comment.scss';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/views/mp/components/wx-msg/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/views/mp/components/wx-msg/types.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					export enum MsgType {
 | 
				
			||||||
 | 
					  Event = 'event',
 | 
				
			||||||
 | 
					  Text = 'text',
 | 
				
			||||||
 | 
					  Voice = 'voice',
 | 
				
			||||||
 | 
					  Image = 'image',
 | 
				
			||||||
 | 
					  Video = 'video',
 | 
				
			||||||
 | 
					  Link = 'link',
 | 
				
			||||||
 | 
					  Location = 'location',
 | 
				
			||||||
 | 
					  Music = 'music',
 | 
				
			||||||
 | 
					  News = 'news'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,14 +9,7 @@
 | 
				
			|||||||
      label-width="68px"
 | 
					      label-width="68px"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <el-form-item label="公众号" prop="accountId">
 | 
					      <el-form-item label="公众号" prop="accountId">
 | 
				
			||||||
        <el-select v-model="queryParams.accountId" placeholder="请选择公众号" class="!w-240px">
 | 
					        <WxMpSelect @change="onAccountChanged" />
 | 
				
			||||||
          <el-option
 | 
					 | 
				
			||||||
            v-for="item in accountList"
 | 
					 | 
				
			||||||
            :key="item.id"
 | 
					 | 
				
			||||||
            :label="item.name"
 | 
					 | 
				
			||||||
            :value="item.id"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </el-select>
 | 
					 | 
				
			||||||
      </el-form-item>
 | 
					      </el-form-item>
 | 
				
			||||||
      <el-form-item label="消息类型" prop="type">
 | 
					      <el-form-item label="消息类型" prop="type">
 | 
				
			||||||
        <el-select v-model="queryParams.type" placeholder="请选择消息类型" class="!w-240px">
 | 
					        <el-select v-model="queryParams.type" placeholder="请选择消息类型" class="!w-240px">
 | 
				
			||||||
@@ -84,70 +77,76 @@
 | 
				
			|||||||
      <el-table-column label="内容" prop="content">
 | 
					      <el-table-column label="内容" prop="content">
 | 
				
			||||||
        <template #default="scope">
 | 
					        <template #default="scope">
 | 
				
			||||||
          <!-- 【事件】区域 -->
 | 
					          <!-- 【事件】区域 -->
 | 
				
			||||||
          <div v-if="scope.row.type === 'event' && scope.row.event === 'subscribe'">
 | 
					          <div v-if="scope.row.type === MsgType.Event && scope.row.event === 'subscribe'">
 | 
				
			||||||
            <el-tag type="success">关注</el-tag>
 | 
					            <el-tag type="success">关注</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'unsubscribe'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'unsubscribe'">
 | 
				
			||||||
            <el-tag type="danger">取消关注</el-tag>
 | 
					            <el-tag type="danger">取消关注</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'CLICK'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'CLICK'">
 | 
				
			||||||
            <el-tag>点击菜单</el-tag>
 | 
					            <el-tag>点击菜单</el-tag>
 | 
				
			||||||
            【{{ scope.row.eventKey }}】
 | 
					            【{{ scope.row.eventKey }}】
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'VIEW'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'VIEW'">
 | 
				
			||||||
            <el-tag>点击菜单链接</el-tag>
 | 
					            <el-tag>点击菜单链接</el-tag>
 | 
				
			||||||
            【{{ scope.row.eventKey }}】
 | 
					            【{{ scope.row.eventKey }}】
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_waitmsg'">
 | 
					          <div
 | 
				
			||||||
 | 
					            v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_waitmsg'"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
            <el-tag>扫码结果</el-tag>
 | 
					            <el-tag>扫码结果</el-tag>
 | 
				
			||||||
            【{{ scope.row.eventKey }}】
 | 
					            【{{ scope.row.eventKey }}】
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_push'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'scancode_push'">
 | 
				
			||||||
            <el-tag>扫码结果</el-tag>
 | 
					            <el-tag>扫码结果</el-tag>
 | 
				
			||||||
            【{{ scope.row.eventKey }}】
 | 
					            【{{ scope.row.eventKey }}】
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_sysphoto'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_sysphoto'">
 | 
				
			||||||
            <el-tag>系统拍照发图</el-tag>
 | 
					            <el-tag>系统拍照发图</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_photo_or_album'">
 | 
					          <div
 | 
				
			||||||
 | 
					            v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_photo_or_album'"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
            <el-tag>拍照或者相册</el-tag>
 | 
					            <el-tag>拍照或者相册</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_weixin'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'pic_weixin'">
 | 
				
			||||||
            <el-tag>微信相册</el-tag>
 | 
					            <el-tag>微信相册</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event' && scope.row.event === 'location_select'">
 | 
					          <div
 | 
				
			||||||
 | 
					            v-else-if="scope.row.type === MsgType.Event && scope.row.event === 'location_select'"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
            <el-tag>选择地理位置</el-tag>
 | 
					            <el-tag>选择地理位置</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'event'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Event">
 | 
				
			||||||
            <el-tag type="danger">未知事件类型</el-tag>
 | 
					            <el-tag type="danger">未知事件类型</el-tag>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <!-- 【消息】区域 -->
 | 
					          <!-- 【消息】区域 -->
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'text'">{{ scope.row.content }}</div>
 | 
					          <div v-else-if="scope.row.type === MsgType.Text">{{ scope.row.content }}</div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'voice'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Voice">
 | 
				
			||||||
            <wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" />
 | 
					            <wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'image'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Image">
 | 
				
			||||||
            <a target="_blank" :href="scope.row.mediaUrl">
 | 
					            <a target="_blank" :href="scope.row.mediaUrl">
 | 
				
			||||||
              <img :src="scope.row.mediaUrl" style="width: 100px" />
 | 
					              <img :src="scope.row.mediaUrl" style="width: 100px" />
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'video' || scope.row.type === 'shortvideo'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Video || scope.row.type === 'shortvideo'">
 | 
				
			||||||
            <wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" />
 | 
					            <wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'link'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Link">
 | 
				
			||||||
            <el-tag>链接</el-tag>
 | 
					            <el-tag>链接</el-tag>
 | 
				
			||||||
            :
 | 
					            :
 | 
				
			||||||
            <a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a>
 | 
					            <a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'location'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Location">
 | 
				
			||||||
            <wx-location
 | 
					            <WxLocation
 | 
				
			||||||
              :label="scope.row.label"
 | 
					              :label="scope.row.label"
 | 
				
			||||||
              :location-y="scope.row.locationY"
 | 
					              :location-y="scope.row.locationY"
 | 
				
			||||||
              :location-x="scope.row.locationX"
 | 
					              :location-x="scope.row.locationX"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'music'">
 | 
					          <div v-else-if="scope.row.type === MsgType.Music">
 | 
				
			||||||
            <wx-music
 | 
					            <WxMusic
 | 
				
			||||||
              :title="scope.row.title"
 | 
					              :title="scope.row.title"
 | 
				
			||||||
              :description="scope.row.description"
 | 
					              :description="scope.row.description"
 | 
				
			||||||
              :thumb-media-url="scope.row.thumbMediaUrl"
 | 
					              :thumb-media-url="scope.row.thumbMediaUrl"
 | 
				
			||||||
@@ -155,8 +154,8 @@
 | 
				
			|||||||
              :hq-music-url="scope.row.hqMusicUrl"
 | 
					              :hq-music-url="scope.row.hqMusicUrl"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else-if="scope.row.type === 'news'">
 | 
					          <div v-else-if="scope.row.type === MsgType.News">
 | 
				
			||||||
            <wx-news :articles="scope.row.articles" />
 | 
					            <WxNews :articles="scope.row.articles" />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div v-else>
 | 
					          <div v-else>
 | 
				
			||||||
            <el-tag type="danger">未知消息类型</el-tag>
 | 
					            <el-tag type="danger">未知消息类型</el-tag>
 | 
				
			||||||
@@ -177,7 +176,7 @@
 | 
				
			|||||||
      </el-table-column>
 | 
					      </el-table-column>
 | 
				
			||||||
    </el-table>
 | 
					    </el-table>
 | 
				
			||||||
    <!-- 分页组件 -->
 | 
					    <!-- 分页组件 -->
 | 
				
			||||||
    <pagination
 | 
					    <Pagination
 | 
				
			||||||
      v-show="total > 0"
 | 
					      v-show="total > 0"
 | 
				
			||||||
      :total="total"
 | 
					      :total="total"
 | 
				
			||||||
      v-model:page="queryParams.pageNo"
 | 
					      v-model:page="queryParams.pageNo"
 | 
				
			||||||
@@ -186,9 +185,14 @@
 | 
				
			|||||||
    />
 | 
					    />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- 发送消息的弹窗 -->
 | 
					    <!-- 发送消息的弹窗 -->
 | 
				
			||||||
    <el-dialog title="粉丝消息列表" v-model="open" @click="openDialog()" width="50%">
 | 
					    <el-dialog
 | 
				
			||||||
 | 
					      title="粉丝消息列表"
 | 
				
			||||||
 | 
					      v-model="showMessageBox"
 | 
				
			||||||
 | 
					      @click="showMessageBox = true"
 | 
				
			||||||
 | 
					      width="50%"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
      <template #footer>
 | 
					      <template #footer>
 | 
				
			||||||
        <wx-msg :user-id="userId" v-if="open" />
 | 
					        <WxMsg :user-id="userId" v-if="showMessageBox" />
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
    </el-dialog>
 | 
					    </el-dialog>
 | 
				
			||||||
  </ContentWrap>
 | 
					  </ContentWrap>
 | 
				
			||||||
@@ -200,17 +204,27 @@ import WxMsg from '@/views/mp/components/wx-msg/main.vue'
 | 
				
			|||||||
import WxLocation from '@/views/mp/components/wx-location/main.vue'
 | 
					import WxLocation from '@/views/mp/components/wx-location/main.vue'
 | 
				
			||||||
import WxMusic from '@/views/mp/components/wx-music/main.vue'
 | 
					import WxMusic from '@/views/mp/components/wx-music/main.vue'
 | 
				
			||||||
import WxNews from '@/views/mp/components/wx-news/main.vue'
 | 
					import WxNews from '@/views/mp/components/wx-news/main.vue'
 | 
				
			||||||
import * as MpAccountApi from '@/api/mp/account'
 | 
					import WxMpSelect from '@/views/mp/components/WxMpSelect.vue'
 | 
				
			||||||
import * as MpMessageApi from '@/api/mp/message'
 | 
					import * as MpMessageApi from '@/api/mp/message'
 | 
				
			||||||
 | 
					 | 
				
			||||||
const message = useMessage() // 消息弹窗
 | 
					 | 
				
			||||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 | 
					import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 | 
				
			||||||
import { dateFormatter } from '@/utils/formatTime'
 | 
					import { dateFormatter } from '@/utils/formatTime'
 | 
				
			||||||
 | 
					import { MsgType } from '@/views/mp/components/wx-msg/types'
 | 
				
			||||||
 | 
					import type { FormInstance } from 'element-plus'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const loading = ref(true) // 列表的加载中
 | 
					const loading = ref(true) // 列表的加载中
 | 
				
			||||||
const total = ref(0) // 列表的总页数
 | 
					const total = ref(0) // 列表的总页数
 | 
				
			||||||
const list = ref([]) // 列表的数据
 | 
					const list = ref<any[]>([]) // 列表的数据
 | 
				
			||||||
const queryParams = reactive({
 | 
					
 | 
				
			||||||
 | 
					// 搜索参数
 | 
				
			||||||
 | 
					interface QueryParams {
 | 
				
			||||||
 | 
					  pageNo: number
 | 
				
			||||||
 | 
					  pageSize: number
 | 
				
			||||||
 | 
					  openid: string | null
 | 
				
			||||||
 | 
					  accountId: number | null
 | 
				
			||||||
 | 
					  type: MsgType | null
 | 
				
			||||||
 | 
					  createTime: string[] | []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const queryParams: QueryParams = reactive({
 | 
				
			||||||
  pageNo: 1,
 | 
					  pageNo: 1,
 | 
				
			||||||
  pageSize: 10,
 | 
					  pageSize: 10,
 | 
				
			||||||
  openid: null,
 | 
					  openid: null,
 | 
				
			||||||
@@ -218,19 +232,18 @@ const queryParams = reactive({
 | 
				
			|||||||
  type: null,
 | 
					  type: null,
 | 
				
			||||||
  createTime: []
 | 
					  createTime: []
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
const queryFormRef = ref() // 搜索的表单
 | 
					const queryFormRef = ref<FormInstance | null>(null) // 搜索的表单
 | 
				
			||||||
// TODO 芋艿:下面应该移除
 | 
					const showMessageBox = ref(false) // 是否显示弹出层
 | 
				
			||||||
const open = ref(false) // 是否显示弹出层
 | 
					 | 
				
			||||||
const userId = ref(0) // 操作的用户编号
 | 
					const userId = ref(0) // 操作的用户编号
 | 
				
			||||||
const accountList = ref<MpAccountApi.AccountVO[]>([]) // 公众号账号列表
 | 
					
 | 
				
			||||||
 | 
					/** 侦听accountId */
 | 
				
			||||||
 | 
					const onAccountChanged = (id?: number) => {
 | 
				
			||||||
 | 
					  queryParams.accountId = id as number
 | 
				
			||||||
 | 
					  handleQuery()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 查询列表 */
 | 
					/** 查询列表 */
 | 
				
			||||||
const getList = async () => {
 | 
					const getList = async () => {
 | 
				
			||||||
  // 如果没有选中公众号账号,则进行提示。
 | 
					 | 
				
			||||||
  if (!queryParams.accountId) {
 | 
					 | 
				
			||||||
    await message.error('未选中公众号,无法查询消息')
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    loading.value = true
 | 
					    loading.value = true
 | 
				
			||||||
    const data = await MpMessageApi.getMessagePage(queryParams)
 | 
					    const data = await MpMessageApi.getMessagePage(queryParams)
 | 
				
			||||||
@@ -249,34 +262,15 @@ const handleQuery = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** 重置按钮操作 */
 | 
					/** 重置按钮操作 */
 | 
				
			||||||
const resetQuery = async () => {
 | 
					const resetQuery = async () => {
 | 
				
			||||||
  queryFormRef.value.resetFields()
 | 
					  const accountId = queryParams.accountId
 | 
				
			||||||
  // 默认选中第一个
 | 
					  queryFormRef.value?.resetFields()
 | 
				
			||||||
  if (accountList.value.length > 0) {
 | 
					  queryParams.accountId = accountId
 | 
				
			||||||
    // @ts-ignore
 | 
					 | 
				
			||||||
    queryParams.accountId = accountList.value[0].id
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  handleQuery()
 | 
					  handleQuery()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
const handleSend = async (row) => {
 | 
					
 | 
				
			||||||
 | 
					/** 打开消息发送窗口 */
 | 
				
			||||||
 | 
					const handleSend = async (row: any) => {
 | 
				
			||||||
  userId.value = row.userId
 | 
					  userId.value = row.userId
 | 
				
			||||||
  open.value = true
 | 
					  showMessageBox.value = true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
const openDialog = () => {
 | 
					 | 
				
			||||||
  open.value = true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
// const closeDiaLog = () => {
 | 
					 | 
				
			||||||
//   open.value = false
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** 初始化 **/
 | 
					 | 
				
			||||||
onMounted(async () => {
 | 
					 | 
				
			||||||
  accountList.value = await MpAccountApi.getSimpleAccountList()
 | 
					 | 
				
			||||||
  // 选中第一个
 | 
					 | 
				
			||||||
  if (accountList.value.length > 0) {
 | 
					 | 
				
			||||||
    // @ts-ignore
 | 
					 | 
				
			||||||
    queryParams.accountId = accountList.value[0].id
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  await getList()
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user