Browse Source

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_scrm_companyUI

caoliqin 1 week ago
parent
commit
c26a4c6ecc

+ 9 - 0
src/api/qw/im.js

@@ -145,4 +145,13 @@ export function getFsCourseVideoListBySidebar(data) {
   })
 }
 
+// 创建小程序链接
+export function createMiniLink(data) {
+  return request({
+    url: '/qw/qwMsg/createMiniLink',
+    method: 'post',
+    data: data
+  })
+}
+
 

+ 1 - 0
src/assets/icons/svg/miniprogram.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1748500743664" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5042" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m256.717 460.186a151.962 151.962 0 0 1-87.347 65.74 83.251 83.251 0 0 1-24.474 4.096 29.082 29.082 0 0 1 0-58.163 15.667 15.667 0 0 0 6.451-1.229 91.443 91.443 0 0 0 55.91-40.96 75.264 75.264 0 0 0 11.06-39.628c0-45.978-42.496-83.866-94.31-83.866a105.267 105.267 0 0 0-51.2 13.414 81.92 81.92 0 0 0-43.725 70.452v244.224a138.445 138.445 0 0 1-72.704 120.422 159.642 159.642 0 0 1-79.77 20.48c-84.378 0-153.6-63.488-153.6-142.029a136.192 136.192 0 0 1 19.763-69.837 151.962 151.962 0 0 1 87.347-65.74 85.914 85.914 0 0 1 24.474-4.096 29.082 29.082 0 1 1 0 58.163 15.667 15.667 0 0 0-6.451 1.229 95.949 95.949 0 0 0-55.91 40.96 75.264 75.264 0 0 0-11.06 39.628c0 45.978 42.496 83.866 94.925 83.866a105.267 105.267 0 0 0 51.2-13.414 81.92 81.92 0 0 0 43.622-70.452V390.35a138.752 138.752 0 0 1 72.807-120.525 151.245 151.245 0 0 1 79.155-21.504c84.378 0 153.6 63.488 153.6 142.029a136.192 136.192 0 0 1-19.763 69.837z" fill="#00B240" p-id="5043"></path></svg>

+ 118 - 0
src/components/LemonUI/components/message/miniprogram.vue

@@ -0,0 +1,118 @@
+<script>
+import SvgIcon from '@/components/SvgIcon'
+
+export default {
+  name: "lemonMessageMiniprogram",
+  inheritAttrs: false,
+  inject: ["IMUI"],
+  components: {
+    SvgIcon
+  },
+  render() {
+    return (
+      <lemon-message-basic
+        class="lemon-message-miniprogram"
+        props={{...this.$attrs}}
+        scopedSlots={{
+          content: props => {
+            return this.renderHtml(props.content)
+          }
+        }}
+      />
+    );
+  },
+  methods: {
+    renderHtml(data) {
+      let content = data
+      if (typeof data === 'string') {
+        try {
+          // 尝试将字符串解析为JSON对象
+          content = JSON.parse(data)
+        } catch (e) {
+          console.error('解析小程序消息内容失败:', e)
+          content = { title: '小程序' }
+        }
+      }
+      
+      return (
+        <div>
+          <div class="lemon-message-miniprogram__header">
+            <div class="lemon-message-miniprogram__title">{content.title || '小程序'}</div>
+          </div>
+          <div class="lemon-message-miniprogram__body">
+            {content.thumbnail && (
+              <div class="lemon-message-miniprogram__cover">
+                <img src={content.thumbnail} alt=""/>
+              </div>
+            )}
+          </div>
+          <div class="lemon-message-miniprogram__footer">
+            <svg-icon icon-class="miniprogram" class="lemon-message-miniprogram__icon"></svg-icon>
+            <span>小程序</span>
+          </div>
+        </div>
+      )
+    }
+  }
+};
+</script>
+
+<style lang="stylus">
+@import '../../styles/utils/index'
+
++b(lemon-message-miniprogram)
+  +b(lemon-message)
+    +e(content)
+      width 240px // 设置小程序卡片宽度
+      border-radius 4px // 设置圆角
+      background-color #fff // 设置背景色
+      overflow hidden // 防止内容溢出
+      box-shadow 0 2px 8px rgba(0, 0, 0, 0.1) // 添加阴影效果
+      padding 0 // 重置内边距
+
+  +e(header)
+    padding 10px // 设置内边距
+    border-bottom 1px solid #f0f0f0 // 添加底部边框
+
+  +e(title)
+    font-size 14px // 设置字体大小
+    font-weight 500 // 设置字体粗细
+    color #333 // 设置字体颜色
+    line-height 1.4 // 设置行高
+    overflow hidden // 防止文本溢出
+    text-overflow ellipsis // 文本溢出显示省略号
+    white-space nowrap // 不换行
+
+  +e(body)
+    position relative // 设置相对定位
+
+  +e(cover)
+    width 100% // 设置图片容器宽度
+    height 120px // 设置图片容器高度
+    overflow hidden // 防止图片溢出
+
+    img
+      width 100% // 设置图片宽度
+      height 100% // 设置图片高度
+      object-fit cover // 图片填充方式
+      display block // 设置为块级元素
+      background transparent // 设置背景透明
+
+  +e(footer)
+    display flex // 使用弹性布局
+    align-items center // 垂直居中
+    padding 8px 10px // 设置内边距
+    font-size 12px // 设置字体大小
+    color #999 // 设置字体颜色
+    background-color #f8f8f8 // 设置背景色
+
+    i
+      margin-right 4px // 设置图标右边距
+      font-size 14px // 设置图标大小
+      color color-primary // 使用主题色变量
+
+  +e(icon)
+    margin-right 4px // 设置右边距
+    font-size 16px // 设置图标大小
+    color color-primary // 使用主题色变量
+</style>

+ 3 - 1
src/components/LemonUI/index.js

@@ -15,6 +15,7 @@ import lemonMessageEvent from "./components/message/event";
 import LemonMessageVoice from "./components/message/voice";
 import LemonMessageVideo from "./components/message/video";
 import LemonMessageEmotionDynamic from "./components/message/emotionDynamic.vue";
+import LemonMessageMiniprogram from "./components/message/miniprogram.vue";
 
 
 import LemonIMUI from "./components/index";
@@ -41,7 +42,8 @@ const components = [
   LemonMessageVoice,
   LemonMessageVideo,
   LemonRecords,
-  LemonMessageEmotionDynamic
+  LemonMessageEmotionDynamic,
+  LemonMessageMiniprogram
 ];
 const install = (Vue) => {
   Vue.directive("LemonContextmenu", Contextmenu);

+ 3 - 0
src/components/LemonUI/lastContentRender.js

@@ -21,4 +21,7 @@ export default {
   emotionDynamic(message) {
     return '[自定义表情]';
   },
+  miniprogram(message) {
+    return '[小程序]';
+  }
 };

+ 1 - 1
src/components/LemonUI/utils/constant.js

@@ -6,7 +6,7 @@ export const DEFAULT_MENUS = [DEFAULT_MENU_LASTMESSAGES, DEFAULT_MENU_CONTACTS];
 /**
  * 聊天消息类型
  */
-export const MESSAGE_TYPE = ["voice", "file", "video", "image", "text"];
+export const MESSAGE_TYPE = ["voice", "file", "video", "image", "text", "emotionDynamic", "miniprogram"];
 
 /**
  * 聊天消息状态

+ 3 - 1
src/store/index.js

@@ -6,6 +6,7 @@ import tagsView from './modules/tagsView'
 import permission from './modules/permission'
 import settings from './modules/settings'
 import getters from './getters'
+import qwIm from './modules/qwIm'
 
 Vue.use(Vuex)
 
@@ -15,7 +16,8 @@ const store = new Vuex.Store({
     user,
     tagsView,
     permission,
-    settings
+    settings,
+    qwIm
   },
   getters
 })

+ 22 - 0
src/store/modules/qwIm.js

@@ -0,0 +1,22 @@
+const state = {
+  shareCourse: null,
+}
+
+const mutations = {
+  SHARE_COURSE: (state, data) => {
+    state.shareCourse = data
+  }
+}
+
+const actions = {
+  shareCourse({ commit }, data) {
+    commit('SHARE_COURSE', data)
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+}

+ 74 - 2
src/views/qw/qwChat/qq.vue

@@ -71,6 +71,7 @@
       append-to-body
       :with-header="false"
       size="35%"
+      :destroy-on-close="true"
       :title="detail.title" :visible.sync="detail.open">
       <userDetail ref="userDetail" />
     </el-drawer>
@@ -89,6 +90,8 @@ import UserDetail from "@/views/qw/qwChat/userDetail/index.vue";
 import {ImSocket} from "@/utils/ImSocket";
 import {getToken} from '@/utils/auth'
 import {uploadOss} from "@/api/common";
+import {mapState} from "vuex";
+import {generateUUID} from "@/components/LemonUI/utils";
 
 let pages = {};
 export default {
@@ -171,6 +174,9 @@ export default {
       }
     });
   },
+  computed: {
+    ...mapState('qwIm', ['shareCourse'])
+  },
   watch: {
     showQw(nv, ov) {
       if (nv) {
@@ -178,6 +184,50 @@ export default {
           this.$refs.IMUI.messageViewToBottom();
         });
       }
+    },
+    shareCourse(nv) {
+      if (nv) {
+        console.log('shareCourse', nv)
+        // 发送小程序卡片消息
+        const IMUI = this.$refs.IMUI;
+        const contact = IMUI.currentContact;
+        if (!contact) {
+          return;
+        }
+
+        // 创建消息对象
+        let message = {
+          id: generateUUID(),
+          status: 'going',
+          type: 'miniprogram',
+          sendTime: Date.now(),
+          content: {
+            appid: nv.miniprogramAppid,
+            pagepath: nv.miniprogramPage,
+            title: nv.miniprogramTitle,
+            thumbnail: nv.miniprogramPicUrl
+          },
+          toContactId: contact.conversationId,
+          fromUser: {
+            id: this.qwUser.id,
+            displayName: this.qwUser.qwUserName,
+            avatar: "https://cos.his.cdwjyyh.com/fs/20241231/22a765a96da247d1b83ea94fef438a41.png"
+          },
+        };
+
+        this.detail.open = false
+        this.$store.dispatch('qwIm/shareCourse', null)
+
+        this.appendRemoteMessage(message)
+        // 使用handleSend方法发送消息
+        this.handleSend(
+          message,
+          (replaceMessage = { status: "succeed" }) => {
+            IMUI.updateMessage(Object.assign(message, replaceMessage));
+          },
+          null
+        );
+      }
     }
   },
   mounted() {
@@ -212,10 +262,11 @@ export default {
         const { IMUI } = this.$refs;
         let message = JSON.parse(data);
         this.appendRemoteMessage(message)
+
         let conversation = IMUI.findConversation(message.toContactId);
         if (conversation.msgId) {
           conversation.lastSendTime = message.sendTime;
-          conversation.lastContent = message.content;
+          conversation.lastContent = IMUI.lastContentRender(message)//message.content;
           IMUI.topPopConversations(conversation);
         }
       })
@@ -358,7 +409,6 @@ export default {
       }
       // image
       else if(message.type === "image"){
-        console.log("Event:image-click", message, next, file)
         const formData = new FormData();
         formData.append("file", file);
         uploadOss(formData).then(response => {
@@ -380,6 +430,28 @@ export default {
           }
         })
       }
+      // 小程序
+      else if(message.type === "miniprogram"){
+        // 小程序消息参数
+        params = {
+          "sessionId": message.toContactId,
+          "appKey": this.qwUser.appKey,
+          "content": JSON.stringify(message.content),
+          "msgType": 5 // 小程序消息类型
+        };
+        sendMsg(params).then(response => {
+          const {code} = response
+          if(code === 200){
+            let conversation = IMUI.findConversation(message.toContactId);
+            conversation.lastSendTime = message.sendTime;
+            conversation.lastContent = "小程序";
+            IMUI.topPopConversations(conversation);
+            next && next();
+          } else {
+            next && next({status:'failed'})
+          }
+        });
+      }
       // file
       else if(message.type === "file"){
         console.log("Event:file-click", message, next, file)

+ 28 - 5
src/views/qw/qwChat/userDetail/courseManage.vue

@@ -53,11 +53,18 @@
           <div class="video-info">
             <div class="video-title">{{item.title}}</div>
             <div class="video-meta">
+              <i class="el-icon-s-order" style="font-size: 18px;margin-right: 5px"></i>
               <span class="video-date">{{formatDate(item.createTime)}}</span>
             </div>
           </div>
           <div class="video-actions">
-            <el-button type="primary" size="mini" round @click="handleShare(item)">分享课程</el-button>
+            <el-button 
+              type="primary" 
+              size="mini" 
+              round 
+              :loading="sharingVideoId === item.videoId" 
+              :disabled="sharingVideoId === item.videoId" 
+              @click="handleShare(item)">分享课程</el-button>
           </div>
         </li>
       </ul>
@@ -77,7 +84,7 @@
 </template>
 
 <script>
-import {getFsCourseListBySidebar, getFsCourseVideoListBySidebar} from "@/api/qw/im";
+import {createMiniLink, getFsCourseListBySidebar, getFsCourseVideoListBySidebar} from "@/api/qw/im";
 
 export default {
   name: "courseManage",
@@ -109,6 +116,7 @@ export default {
       },
       videoList: [],
       defaultImage: require('@/assets/image/default-image.png'),
+      sharingVideoId: null
     }
   },
   created() {
@@ -223,7 +231,22 @@ export default {
       return `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`;
     },
     handleShare(item) {
-      console.log('分享', item)
+      const params = {
+        extId: this.userId,
+        courseId: item.courseId,
+        videoId: item.videoId,
+        title: item.title,
+      }
+      this.sharingVideoId = item.videoId;
+      createMiniLink(params).then(response => {
+        if (response.code === 200) {
+          this.$store.dispatch('qwIm/shareCourse', response.data)
+        }
+        this.sharingVideoId = null;
+      }).catch(error => {
+        this.$message.error('分享课程失败');
+        this.sharingVideoId = null;
+      })
     }
   }
 }
@@ -277,7 +300,7 @@ export default {
 .video-item {
   display: flex;
   padding: 15px;
-  margin-bottom: 10px;
+  margin-bottom: 5px;
   background-color: #fff;
   border-radius: 8px;
   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
@@ -335,7 +358,7 @@ export default {
 
 .video-meta {
   display: flex;
-  align-items: center;
+  align-items: flex-end;
   font-size: 12px;
   color: #909399;
   margin-bottom: 5px;