Sfoglia il codice sorgente

!70 微信组件按照vue3的方式重写了一遍
Merge pull request !70 from &wxr/dev

芋道源码 2 anni fa
parent
commit
e3bcfa50e9

+ 2 - 0
package.json

@@ -29,6 +29,7 @@
     "@form-create/designer": "^3.1.0",
     "@form-create/element-ui": "^3.1.17",
     "@iconify/iconify": "^3.1.0",
+    "@videojs-player/vue": "^1.0.0",
     "@vueuse/core": "^9.13.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
@@ -58,6 +59,7 @@
     "qs": "^6.11.1",
     "steady-xml": "^0.1.0",
     "url": "^0.11.0",
+    "video.js": "^8.0.4",
     "vue": "3.2.47",
     "vue-i18n": "9.2.2",
     "vue-router": "^4.1.6",

BIN
src/assets/imgs/profile.jpg


BIN
src/assets/imgs/wechat.png


+ 302 - 0
src/views/mp/components/wx-material-select/main.vue

@@ -0,0 +1,302 @@
+<!--
+  - Copyright (C) 2018-2019
+  - All rights reserved, Designed By www.joolun.com
+  芋道源码:
+  ① 移除 avue 组件,使用 ElementUI 原生组件
+-->
+<template>
+  <!-- 类型:图片 -->
+  <div v-if="objData.type === 'image'">
+    <div class="waterfall" v-loading="loading">
+      <div class="waterfall-item" v-for="item in list" :key="item.mediaId">
+        <img class="material-img" :src="item.url" />
+        <p class="item-name">{{ item.name }}</p>
+        <el-row class="ope-row">
+          <el-button type="success" @click="selectMaterialFun(item)"
+            >选择
+            <i class="el-icon-circle-check el-icon--right"></i>
+          </el-button>
+        </el-row>
+      </div>
+    </div>
+    <!-- 分页组件 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getMaterialPageFun"
+    />
+  </div>
+  <!-- 类型:语音 -->
+  <div v-else-if="objData.type === 'voice'">
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="mediaId" />
+      <el-table-column label="文件名" align="center" prop="name" />
+      <el-table-column label="语音" align="center">
+        <template #default="scope">
+          <wx-voice-player :url="scope.row.url" />
+        </template>
+      </el-table-column>
+      <el-table-column label="上传时间" align="center" prop="createTime" width="180">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        align="center"
+        fixed="right"
+        class-name="small-padding fixed-width"
+      >
+        <template #default="scope">
+          <el-button type="text" icon="el-icon-circle-plus" @click="selectMaterialFun(scope.row)"
+            >选择
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页组件 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getPage"
+    />
+  </div>
+  <div v-else-if="objData.type === 'video'">
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="mediaId" />
+      <el-table-column label="文件名" align="center" prop="name" />
+      <el-table-column label="标题" align="center" prop="title" />
+      <el-table-column label="介绍" align="center" prop="introduction" />
+      <el-table-column label="视频" align="center">
+        <template #default="scope">
+          <wx-video-player :url="scope.row.url" />
+        </template>
+      </el-table-column>
+      <el-table-column label="上传时间" align="center" prop="createTime" width="180">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        align="center"
+        fixed="right"
+        class-name="small-padding fixed-width"
+      >
+        <template #default="scope">
+          <el-button type="text" icon="el-icon-circle-plus" @click="selectMaterialFun(scope.row)"
+            >选择
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页组件 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getMaterialPageFun"
+    />
+  </div>
+  <div v-else-if="objData.type === 'news'">
+    <div class="waterfall" v-loading="loading">
+      <div class="waterfall-item" v-for="item in list" :key="item.mediaId">
+        <div v-if="item.content && item.content.newsItem">
+          <wx-news :articles="item.content.newsItem" />
+          <el-row class="ope-row">
+            <el-button type="success" @click="selectMaterialFun(item)">
+              选择<i class="el-icon-circle-check el-icon--right"></i>
+            </el-button>
+          </el-row>
+        </div>
+      </div>
+    </div>
+    <!-- 分页组件 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getMaterialPageFun"
+    />
+  </div>
+</template>
+
+<script lang="ts" name="WxMaterialSelect">
+import WxNews from '@/views/mp/components/wx-news/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 { getMaterialPage } from '@/api/mp/material'
+import { getFreePublishPage } from '@/api/mp/freePublish'
+import { getDraftPage } from '@/api/mp/draft'
+import { dateFormatter, parseTime } from '@/utils/formatTime'
+import { defineComponent, PropType } from 'vue'
+
+export default defineComponent({
+  components: {
+    WxNews,
+    WxVoicePlayer,
+    WxVideoPlayer
+  },
+  props: {
+    objData: {
+      type: Object, // type - 类型;accountId - 公众号账号编号
+      required: true
+    },
+    newsType: {
+      // 图文类型:1、已发布图文;2、草稿箱图文
+      type: String as PropType<string>,
+      default: '1'
+    }
+  },
+  setup(props, ctx) {
+    // 遮罩层
+    const loading = ref(false)
+    // 总条数
+    const total = ref(0)
+    // 数据列表
+    const list = ref([])
+    // 查询参数
+    const queryParams = reactive({
+      pageNo: 1,
+      pageSize: 10,
+      accountId: props.objData.accountId
+    })
+    const objDataRef = reactive(props.objData)
+    const newsTypeRef = ref(props.newsType)
+
+    const selectMaterialFun = (item) => {
+      ctx.emit('selectMaterial', item)
+    }
+    /** 搜索按钮操作 */
+    const handleQuery = () => {
+      queryParams.pageNo = 1
+      getPage()
+    }
+    const getPage = () => {
+      loading.value = true
+      if (objDataRef.type === 'news' && newsTypeRef.value === '1') {
+        // 【图文】+ 【已发布】
+        getFreePublishPageFun()
+      } else if (objDataRef.type === 'news' && newsTypeRef.value === '2') {
+        // 【图文】+ 【草稿】
+        getDraftPageFun()
+      } else {
+        // 【素材】
+        getMaterialPageFun()
+      }
+    }
+
+    const getMaterialPageFun = async () => {
+      let data = await getMaterialPage({
+        ...queryParams,
+        type: objDataRef.type
+      })
+      list.value = data.list
+      total.value = data.total
+      loading.value = false
+    }
+    const getFreePublishPageFun = async () => {
+      let data = await getFreePublishPage(queryParams)
+      data.list.foreach((item) => {
+        const newsItem = item.content.newsItem
+        newsItem.forEach((article) => {
+          article.picUrl = article.thumbUrl
+        })
+      })
+      list.value = data.list
+      total.value = data.total
+      loading.value = false
+    }
+
+    const getDraftPageFun = async () => {
+      let data = await getDraftPage(queryParams)
+      data.list.forEach((item) => {
+        const newsItem = item.content.newsItem
+        newsItem.forEach((article) => {
+          article.picUrl = article.thumbUrl
+        })
+      })
+      list.value = data.list
+      total.value = data.total
+      loading.value = false
+    }
+
+    onMounted(async () => {
+      getPage()
+    })
+    return {
+      handleQuery,
+      dateFormatter,
+      selectMaterialFun,
+      getMaterialPageFun,
+      getPage,
+      parseTime,
+      newsTypeRef,
+      queryParams,
+      objDataRef,
+      list,
+      total,
+      loading
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+/*瀑布流样式*/
+.waterfall {
+  width: 100%;
+  column-gap: 10px;
+  column-count: 5;
+  margin: 0 auto;
+}
+
+.waterfall-item {
+  padding: 10px;
+  margin-bottom: 10px;
+  break-inside: avoid;
+  border: 1px solid #eaeaea;
+}
+
+.material-img {
+  width: 100%;
+}
+
+p {
+  line-height: 30px;
+}
+
+@media (min-width: 992px) and (max-width: 1300px) {
+  .waterfall {
+    column-count: 3;
+  }
+  p {
+    color: red;
+  }
+}
+
+@media (min-width: 768px) and (max-width: 991px) {
+  .waterfall {
+    column-count: 2;
+  }
+  p {
+    color: orange;
+  }
+}
+
+@media (max-width: 767px) {
+  .waterfall {
+    column-count: 1;
+  }
+}
+
+/*瀑布流样式*/
+</style>

+ 152 - 155
src/views/mp/components/wx-msg/main.vue

@@ -6,7 +6,7 @@
   ② 代码优化,补充注释,提升阅读性
 -->
 <template>
-  <div class="msg-main">
+  <ContentWrap>
     <div class="msg-div" :id="'msg-div' + nowStr">
       <!-- 加载更多 -->
       <div v-loading="loading"></div>
@@ -26,9 +26,9 @@
               :src="item.sendFrom === 1 ? user.avatar : mp.avatar"
               class="avue-comment__avatar"
             />
-            <div class="avue-comment__author">{{
-              item.sendFrom === 1 ? user.nickname : mp.nickname
-            }}</div>
+            <div class="avue-comment__author"
+              >{{ item.sendFrom === 1 ? user.nickname : mp.nickname }}
+            </div>
           </div>
           <div class="avue-comment__main">
             <div class="avue-comment__header">
@@ -40,37 +40,41 @@
             >
               <!-- 【事件】区域 -->
               <div v-if="item.type === 'event' && item.event === 'subscribe'">
-                <el-tag type="success" size="mini">关注</el-tag>
+                <el-tag type="success">关注</el-tag>
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
-                <el-tag type="danger" size="mini">取消关注</el-tag>
+                <el-tag type="danger">取消关注</el-tag>
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'CLICK'">
-                <el-tag size="mini">点击菜单</el-tag>【{{ item.eventKey }}】
+                <el-tag>点击菜单</el-tag>
+                【{{ item.eventKey }}】
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'VIEW'">
-                <el-tag size="mini">点击菜单链接</el-tag>【{{ item.eventKey }}】
+                <el-tag>点击菜单链接</el-tag>
+                【{{ item.eventKey }}】
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'">
-                <el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
+                <el-tag>扫码结果</el-tag>
+                【{{ item.eventKey }}】
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'scancode_push'">
-                <el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
+                <el-tag>扫码结果</el-tag>
+                【{{ item.eventKey }}】
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'pic_sysphoto'">
-                <el-tag size="mini">系统拍照发图</el-tag>
+                <el-tag>系统拍照发图</el-tag>
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'pic_photo_or_album'">
-                <el-tag size="mini">拍照或者相册</el-tag>
+                <el-tag>拍照或者相册</el-tag>
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'pic_weixin'">
-                <el-tag size="mini">微信相册</el-tag>
+                <el-tag>微信相册</el-tag>
               </div>
               <div v-else-if="item.type === 'event' && item.event === 'location_select'">
-                <el-tag size="mini">选择地理位置</el-tag>
+                <el-tag>选择地理位置</el-tag>
               </div>
               <div v-else-if="item.type === 'event'">
-                <el-tag type="danger" size="mini">未知事件类型</el-tag>
+                <el-tag type="danger">未知事件类型</el-tag>
               </div>
               <!-- 【消息】区域 -->
               <div v-else-if="item.type === 'text'">{{ item.content }}</div>
@@ -124,10 +128,10 @@
       <wx-reply-select ref="replySelect" :objData="objData" />
       <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
     </div>
-  </div>
+  </ContentWrap>
 </template>
 
-<script>
+<script lang="ts" name="WxMsg">
 import { getMessagePage, sendMessage } from '@/api/mp/message'
 import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
 import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
@@ -136,9 +140,14 @@ import WxNews from '@/views/mp/components/wx-news/main.vue'
 import WxLocation from '@/views/mp/components/wx-location/main.vue'
 import WxMusic from '@/views/mp/components/wx-music/main.vue'
 import { getUser } from '@/api/mp/mpuser'
+import { defineComponent } from 'vue'
+
+const message = useMessage() // 消息弹窗
+import profile from '@/assets/imgs/profile.jpg'
+import wechat from '@/assets/imgs/wechat.png'
+import { parseTime } from '@/utils/formatTime'
 
-export default {
-  name: 'WxMsg',
+export default defineComponent({
   components: {
     WxReplySelect,
     WxVideoPlayer,
@@ -153,160 +162,144 @@ export default {
       required: true
     }
   },
-  data() {
-    return {
-      nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
-      loading: false, // 消息列表是否正在加载中
-      loadMore: true, // 是否可以加载更多
-      list: [], // 消息列表
-      queryParams: {
-        pageNo: 1, // 当前页数
-        pageSize: 14, // 每页显示多少条
-        accountId: undefined
-      },
-      user: {
-        // 由于微信不再提供昵称,直接使用“用户”展示
-        nickname: '用户',
-        avatar: require('@/assets/images/profile.jpg'),
-        accountId: 0 // 公众号账号编号
-      },
-      mp: {
-        nickname: '公众号',
-        avatar: require('@/assets/images/wechat.png')
-      },
-
-      // ========= 消息发送 =========
-      sendLoading: false, // 发送消息是否加载中
-      objData: {
-        // 微信发送消息
-        type: 'text'
-      }
-    }
-  },
-  created() {
-    // 获得用户信息
-    getUser(this.userId).then((response) => {
-      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
+  setup(props) {
+    const nowStr = ref(new Date().getTime()) // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
+    const loading = ref(false) // 消息列表是否正在加载中
+    const loadMore = ref(true) // 是否可以加载更多
+    const list = ref<any[]>([]) // 消息列表
+    const queryParams = reactive({
+      pageNo: 1, // 当前页数
+      pageSize: 14, // 每页显示多少条
+      accountId: undefined
+    })
+    const user = reactive({
+      // 由于微信不再提供昵称,直接使用“用户”展示
+      nickname: '用户',
+      avatar: profile,
+      accountId: 0 // 公众号账号编号
+    })
+    const mp = reactive({
+      nickname: '公众号',
+      avatar: wechat
+    })
 
-      // 加载消息
-      console.log(this.queryParams)
-      this.refreshChange()
+    // ========= 消息发送 =========
+    const sendLoading = ref(false) // 发送消息是否加载中
+    const objData = reactive({
+      // 微信发送消息
+      type: 'text',
+      accountId: null,
+      articles: []
     })
-  },
-  methods: {
-    sendMsg() {
-      if (!this.objData) {
+
+    const replySelect = ref(null)
+    // 执行发送
+    const sendMsg = async () => {
+      if (!objData) {
         return
       }
-      // 公众号限制:客服消息,公众号只允许发送一条
-      if (this.objData.type === 'news' && this.objData.articles.length > 1) {
-        this.objData.articles = [this.objData.articles[0]]
-        this.$message({
-          showClose: true,
-          message: '图文消息条数限制在 1 条以内,已默认发送第一条',
-          type: 'success'
-        })
+      //     // 公众号限制:客服消息,公众号只允许发送一条
+      if (objData.type === 'news' && objData.articles.length > 1) {
+        objData.articles = [objData.articles[0]]
+        message.success('图文消息条数限制在 1 条以内,已默认发送第一条')
       }
-
-      // 执行发送
-      this.sendLoading = true
-      sendMessage(
-        Object.assign(
-          {
-            userId: this.userId
-          },
-          {
-            ...this.objData
-          }
-        )
-      )
-        .then((response) => {
-          this.sendLoading = false
-          // 添加到消息列表,并滚动
-          this.list = [...this.list, ...[response.data]]
-          this.scrollToBottom()
-          // 重置 objData 状态
-          this.$refs['replySelect'].deleteObj() // 重置,避免 tab 的数据未清理
-        })
-        .catch(() => {
-          this.sendLoading = false
-        })
-    },
-    loadingMore() {
-      this.queryParams.pageNo++
-      this.getPage(this.queryParams)
-    },
-    getPage(page, params) {
-      this.loading = true
-      getMessagePage(
+      let data = await sendMessage(Object.assign({ userId: props.userId }, { ...objData }))
+      sendLoading.value = false
+      list.value = [...list.value, ...[data]]
+      scrollToBottom()
+      //ts檢查的時候會判斷這個組件可能是空的,所以需要進行斷言。
+      //避免 tab 的数据未清理
+      const deleteObj = (replySelect.value as any).deleteObj
+      if (deleteObj) {
+        deleteObj()
+      }
+    }
+    const loadingMore = () => {
+      queryParams.pageNo++
+      getPage(queryParams, null)
+    }
+    const getPage = async (page, params) => {
+      loading.value = true
+      let dataTemp = await getMessagePage(
         Object.assign(
           {
             pageNo: page.pageNo,
             pageSize: page.pageSize,
-            userId: this.userId,
+            userId: props.userId,
             accountId: page.accountId
           },
           params
         )
-      ).then((response) => {
-        // 计算当前的滚动高度
-        const msgDiv = document.getElementById('msg-div' + this.nowStr)
-        let scrollHeight = 0
-        if (msgDiv) {
-          scrollHeight = msgDiv.scrollHeight
-        }
-
-        // 处理数据
-        const data = response.data.list.reverse()
-        this.list = [...data, ...this.list]
-        this.loading = false
-        if (data.length < this.queryParams.pageSize || data.length === 0) {
-          this.loadMore = false
-        }
-        this.queryParams.pageNo = page.pageNo
-        this.queryParams.pageSize = page.pageSize
-
-        // 滚动到原来的位置
-        if (this.queryParams.pageNo === 1) {
-          // 定位到消息底部
-          this.scrollToBottom()
-        } else if (data.length !== 0) {
-          // 定位滚动条
-          this.$nextTick(() => {
-            if (scrollHeight !== 0) {
-              msgDiv.scrollTop =
-                document.getElementById('msg-div' + this.nowStr).scrollHeight - scrollHeight - 100
+      )
+      const msgDiv = document.getElementById('msg-div' + nowStr.value)
+      let scrollHeight = 0
+      if (msgDiv) {
+        scrollHeight = msgDiv.scrollHeight
+      }
+      // 处理数据
+      let data = dataTemp.list.reverse()
+      list.value = [...data, ...list.value]
+      loading.value = false
+      if (data.length < queryParams.pageSize || data.length === 0) {
+        loadMore.value = false
+      }
+      queryParams.pageNo = page.pageNo
+      queryParams.pageSize = page.pageSize
+      // 滚动到原来的位置
+      if (queryParams.pageNo === 1) {
+        // 定位到消息底部
+        scrollToBottom()
+      } else if (data.length !== 0) {
+        // 定位滚动条
+        await nextTick(() => {
+          if (scrollHeight !== 0) {
+            let div = document.getElementById('msg-div' + nowStr.value)
+            if (div && msgDiv) {
+              msgDiv.scrollTop = div.scrollHeight - scrollHeight - 100
             }
-          })
-        }
-      })
-    },
-    /**
-     * 刷新回调
-     */
-    refreshChange() {
-      this.getPage(this.queryParams)
-    },
+          }
+        })
+      }
+    }
+    const refreshChange = () => {
+      getPage(queryParams, null)
+    }
     /** 定位到消息底部 */
-    scrollToBottom: function () {
-      this.$nextTick(() => {
-        let div = document.getElementById('msg-div' + this.nowStr)
-        div.scrollTop = div.scrollHeight
+    const scrollToBottom = () => {
+      nextTick(() => {
+        let div = document.getElementById('msg-div' + nowStr.value)
+        if (div) {
+          div.scrollTop = div.scrollHeight
+        }
       })
     }
+
+    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,
+      parseTime,
+      scrollToBottom,
+      objData,
+      mp,
+      user,
+      queryParams,
+      list,
+      loadMore,
+      loading,
+      nowStr,
+      sendLoading
+    }
   }
-}
+})
 </script>
 <style lang="scss" scoped>
 /* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc  */
@@ -317,6 +310,7 @@ export default {
   margin-top: -30px;
   padding: 10px;
 }
+
 .msg-div {
   height: 50vh;
   overflow: auto;
@@ -324,13 +318,16 @@ export default {
   margin-left: 10px;
   margin-right: 10px;
 }
+
 .msg-send {
   padding: 10px;
 }
+
 .avatar-div {
   text-align: center;
   width: 80px;
 }
+
 .send-but {
   float: right;
   margin-top: 8px !important;

+ 706 - 623
src/views/mp/components/wx-reply/main.vue

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

+ 58 - 90
src/views/mp/components/wx-video-play/main.vue

@@ -8,110 +8,78 @@
       存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
     2)重构后的做法:后端接收到微信公众号的视频消息后,将视频消息的 media_id 的文件内容保存到文件服务器中,这样前端可以直接使用 URL 播放。
   ② 体验优化:弹窗关闭后,自动暂停视频的播放
+
 -->
 <template>
-  <div>
+  <div @click="playVideo()">
     <!-- 提示 -->
-    <div @click="playVideo()">
-      <el-icon>
-        <VideoPlay />
-      </el-icon>
+    <div>
+      <Icon icon="ep:video-play" class="mr-5px" />
       <p>点击播放视频</p>
     </div>
 
     <!-- 弹窗播放 -->
-    <el-dialog
-      title="视频播放"
-      v-model:visible="dialogVideo"
-      width="40%"
-      append-to-body
-      @close="closeDialog"
-    >
-      <video-player
-        v-if="playerOptions.sources[0].src"
-        class="video-player vjs-custom-skin"
-        ref="videoPlayerRef"
-        :playsinline="true"
-        :options="playerOptions"
-        @play="onPlayerPlay($event)"
-        @pause="onPlayerPause($event)"
-      />
+    <el-dialog v-model="dialogVideo" title="视频播放" width="40%" append-to-body>
+      <template #footer>
+        <video-player
+          v-if="dialogVideo"
+          class="video-player vjs-big-play-centered"
+          :src="url"
+          poster=""
+          crossorigin="anonymous"
+          playsinline
+          controls
+          :volume="0.6"
+          :height="320"
+          :playback-rates="[0.7, 1.0, 1.5, 2.0]"
+        />
+      </template>
+      <!--     事件,暫時沒用
+      @mounted="handleMounted"-->
+      <!--        @ready="handleEvent($event)"-->
+      <!--        @play="handleEvent($event)"-->
+      <!--        @pause="handleEvent($event)"-->
+      <!--        @ended="handleEvent($event)"-->
+      <!--        @loadeddata="handleEvent($event)"-->
+      <!--        @waiting="handleEvent($event)"-->
+      <!--        @playing="handleEvent($event)"-->
+      <!--        @canplay="handleEvent($event)"-->
+      <!--        @canplaythrough="handleEvent($event)"-->
+      <!--        @timeupdate="handleEvent(player?.currentTime())"-->
     </el-dialog>
   </div>
 </template>
 
-<script setup lang="ts" name="WxVideoPlayer">
-// 引入 videoPlayer 相关组件。教程:https://juejin.cn/post/6923056942281654285
-import { videoPlayer } from 'vue-video-player'
+<script lang="ts" name="WxVideoPlayer">
+//升级videojs6.0版本,重寫6.0版本
+import 'video.js/dist/video-js.css'
+import { defineComponent } from 'vue'
+import { VideoPlayer } from '@videojs-player/vue'
 import 'video.js/dist/video-js.css'
-import 'vue-video-player/src/custom-theme.css'
-import { VideoPlay } from '@element-plus/icons-vue'
 
-const props = defineProps({
-  url: {
-    // 视频地址,例如说:https://www.iocoder.cn/xxx.mp4
-    type: String,
-    required: true
-  }
-})
-const videoPlayerRef = ref()
-const dialogVideo = ref(false)
-const playerOptions = reactive({
-  playbackRates: [0.5, 1.0, 1.5, 2.0], // 播放速度
-  autoplay: false, // 如果 true,浏览器准备好时开始回放。
-  muted: false, // 默认情况下将会消除任何音频。
-  loop: false, // 导致视频一结束就重新开始。
-  preload: 'auto', // 建议浏览器在 <video> 加载元素后是否应该开始下载视频数据。auto 浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
-  language: 'zh-CN',
-  aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
-  fluid: true, // 当true时,Video.js player 将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
-  sources: [
-    {
-      type: 'video/mp4',
-      src: '' // 你的视频地址(必填)【重要】
+export default defineComponent({
+  components: {
+    VideoPlayer
+  },
+  props: {
+    url: {
+      // 视频地址,例如说:https://vjs.zencdn.net/v/oceans.mp4
+      type: String,
+      required: true
     }
-  ],
-  poster: '', // 你的封面地址
-  width: document.documentElement.clientWidth,
-  notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖 Video.js 无法播放媒体源时显示的默认信息。
-  controlBar: {
-    timeDivider: true,
-    durationDisplay: true,
-    remainingTimeDisplay: false,
-    fullscreenToggle: true //全屏按钮
-  }
-})
+  },
+  setup() {
+    // const videoPlayerRef = ref(null)
+    const dialogVideo = ref(false)
 
-const playVideo = () => {
-  dialogVideo.value = true
-  playerOptions.sources[0].src = props.url
-}
-const closeDialog = () => {
-  // 暂停播放
-  // videoPlayerRef.player.pause()
-}
-//   onPlayerPlay(player) {},
-//   // // eslint-disable-next-line @typescript-eslint/no-unused-vars
-//   // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
-//   onPlayerPause(player) {}
+    const handleEvent = (log) => {
+      console.log('Basic player event', log)
+    }
+    const playVideo = () => {
+      dialogVideo.value = true
+    }
 
-// methods: {
-//   playVideo() {
-//     this.dialogVideo = true
-//     // 设置地址
-//     this.playerOptions.sources[0]['src'] = this.url
-//   },
-//   closeDialog() {
-//     // 暂停播放
-//     this.$refs.videoPlayer.player.pause()
-//   },
-//
-//   //todo player组件引入可能有问题
-//
-//   // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
-//   onPlayerPlay(player) {},
-//   // // eslint-disable-next-line @typescript-eslint/no-unused-vars
-//   // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
-//   onPlayerPause(player) {}
-// }
+    return { handleEvent, playVideo, dialogVideo }
+  }
+})
 </script>

+ 1 - 0
src/views/mp/components/wx-voice-play/main.vue

@@ -25,6 +25,7 @@
 
 <script setup lang="ts" name="WxVoicePlayer">
 // 因为微信语音是 amr 格式,所以需要用到 amr 解码器:https://www.npmjs.com/package/benz-amr-recorder
+
 import BenzAMRRecorder from 'benz-amr-recorder'
 
 const props = defineProps({

+ 16 - 5
src/views/mp/freePublish/index.vue

@@ -19,8 +19,14 @@
         </el-select>
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />
+          重置
+        </el-button>
       </el-form-item>
     </el-form>
   </content-wrap>
@@ -63,12 +69,19 @@
 import { getFreePublishPage, deleteFreePublish } from '@/api/mp/freePublish'
 import * as MpAccountApi from '@/api/mp/account'
 import WxNews from '@/views/mp/components/wx-news/main.vue'
+
 const message = useMessage() // 消息弹窗
 
 const loading = ref(true) // 列表的加载中
 const total = ref(0) // 列表的总页数
 const list = ref([]) // 列表的数据
-const queryParams = reactive({
+interface QueryParams {
+  currentPage: number | undefined | string
+  pageNo: number | undefined | string
+  accountId: number | undefined | string
+}
+
+const queryParams: QueryParams = reactive({
   currentPage: 1, // 当前页数
   pageNo: 1, // 当前页数
   accountId: undefined // 当前页数
@@ -115,7 +128,6 @@ const resetQuery = () => {
   queryFormRef.value.resetFields()
   // 默认选中第一个
   if (accountList.value.length > 0) {
-    // @ts-ignore
     queryParams.accountId = accountList.value[0].id
   }
   handleQuery()
@@ -144,7 +156,6 @@ onMounted(async () => {
   accountList.value = await MpAccountApi.getSimpleAccountList()
   // 选中第一个
   if (accountList.value.length > 0) {
-    // @ts-ignore
     queryParams.accountId = accountList.value[0].id
   }
   await getList()

+ 36 - 15
src/views/mp/message/index.vue

@@ -51,8 +51,14 @@
         />
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />
+          重置
+        </el-button>
       </el-form-item>
     </el-form>
   </ContentWrap>
@@ -85,16 +91,20 @@
             <el-tag type="danger">取消关注</el-tag>
           </div>
           <div v-else-if="scope.row.type === 'event' && scope.row.event === 'CLICK'">
-            <el-tag>点击菜单</el-tag>【{{ scope.row.eventKey }}】
+            <el-tag>点击菜单</el-tag>
+            【{{ scope.row.eventKey }}】
           </div>
           <div v-else-if="scope.row.type === 'event' && scope.row.event === 'VIEW'">
-            <el-tag>点击菜单链接</el-tag>【{{ scope.row.eventKey }}】
+            <el-tag>点击菜单链接</el-tag>
+            【{{ scope.row.eventKey }}】
           </div>
           <div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_waitmsg'">
-            <el-tag>扫码结果</el-tag>【{{ scope.row.eventKey }}】
+            <el-tag>扫码结果</el-tag>
+            【{{ scope.row.eventKey }}】
           </div>
           <div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_push'">
-            <el-tag>扫码结果</el-tag>【{{ scope.row.eventKey }}】
+            <el-tag>扫码结果</el-tag>
+            【{{ scope.row.eventKey }}】
           </div>
           <div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_sysphoto'">
             <el-tag>系统拍照发图</el-tag>
@@ -125,7 +135,8 @@
             <wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" />
           </div>
           <div v-else-if="scope.row.type === 'link'">
-            <el-tag>链接</el-tag>:
+            <el-tag>链接</el-tag>
+            :
             <a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a>
           </div>
           <div v-else-if="scope.row.type === 'location'">
@@ -175,23 +186,26 @@
     />
 
     <!-- 发送消息的弹窗 -->
-    <el-dialog title="粉丝消息列表" v-model:visible="open" width="50%">
-      <wx-msg :user-id="userId" v-if="open" />
+    <el-dialog title="粉丝消息列表" v-model="open" @click="openDialog()" width="50%">
+      <template #footer>
+        <wx-msg :user-id="userId" v-if="open" />
+      </template>
     </el-dialog>
   </ContentWrap>
 </template>
 <script setup lang="ts" name="MpMessage">
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-// 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 WxMsg from '@/views/mp/components/wx-msg/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 WxMusic from '@/views/mp/components/wx-music/main.vue'
+import WxNews from '@/views/mp/components/wx-news/main.vue'
 import * as MpAccountApi from '@/api/mp/account'
 import * as MpMessageApi from '@/api/mp/message'
+
 const message = useMessage() // 消息弹窗
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { dateFormatter } from '@/utils/formatTime'
 
 const loading = ref(true) // 列表的加载中
 const total = ref(0) // 列表的总页数
@@ -248,6 +262,13 @@ const handleSend = async (row) => {
   open.value = true
 }
 
+const openDialog = () => {
+  open.value = true
+}
+// const closeDiaLog = () => {
+//   open.value = false
+// }
+
 /** 初始化 **/
 onMounted(async () => {
   accountList.value = await MpAccountApi.getSimpleAccountList()

+ 13 - 4
src/views/mp/tag/index.vue

@@ -28,13 +28,21 @@
         />
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />
+          重置
+        </el-button>
         <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['mp:tag:create']">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
+          <Icon icon="ep:plus" class="mr-5px" />
+          新增
         </el-button>
         <el-button type="success" plain @click="handleSync" v-hasPermi="['mp:tag:sync']">
-          <Icon icon="ep:refresh" class="mr-5px" /> 同步
+          <Icon icon="ep:refresh" class="mr-5px" />
+          同步
         </el-button>
       </el-form-item>
     </el-form>
@@ -91,6 +99,7 @@ import { dateFormatter } from '@/utils/formatTime'
 import * as MpTagApi from '@/api/mp/tag'
 import * as MpAccountApi from '@/api/mp/account'
 import TagForm from './TagForm.vue'
+
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化