Compare commits
	
		
			No commits in common. "a506b4dcc1c81ba3be47f87a421cbcb1d5ff6e6d" and "97f05d2c5cd637dc1105f14d2e529d82645054af" have entirely different histories.
		
	
	
		
			a506b4dcc1
			...
			97f05d2c5c
		
	
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -38,7 +38,7 @@ const img = (src: string, width = 200) => { | ||||
|     <div class="image-container"> | ||||
|       <n-image class="h-149px" :src="extra.url" /> | ||||
|       <!-- 上传中的loading蒙版 --> | ||||
|       <div v-if="extra.is_uploading" class="loading-overlay"> | ||||
|       <div v-if="props.extra.is_uploading" class="loading-overlay"> | ||||
|         <n-spin size="large" /> | ||||
|       </div> | ||||
|     </div> | ||||
| @ -53,7 +53,7 @@ const img = (src: string, width = 200) => { | ||||
|   height:149px; | ||||
|    | ||||
|   &.left { | ||||
|     background: #F4F4FC; | ||||
|     background: var(--im-message-right-bg-color); | ||||
|   } | ||||
| 
 | ||||
|   .image-container { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <script lang="ts" setup> | ||||
| import 'xgplayer/dist/index.min.css' | ||||
| import { ref, nextTick, watch, computed } from 'vue' | ||||
| import { ref, nextTick, watch } from 'vue' | ||||
| import { NImage, NModal, NCard, NProgress, NPopconfirm } from 'naive-ui' | ||||
| import { Play, Close, Pause, Right, Attention } from '@icon-park/vue-next' | ||||
| import { getImageInfo } from '@/utils/functions' | ||||
| @ -64,11 +64,6 @@ const updatePauseStatus = () => { | ||||
| // 初始化时检查状态 | ||||
| updatePauseStatus() | ||||
| 
 | ||||
| // 创建视频封面的URL | ||||
| const videoSrc = computed(() => { | ||||
|   // 即使在上传过程中也返回视频URL,这样可以显示视频封面 | ||||
|   return props.extra.url || '' | ||||
| }) | ||||
| // // 监听关键道具变化 | ||||
| // watch(() => props.extra.percentage, (newVal: number | undefined) => { | ||||
| //   // 确保进度更新时 UI 也实时更新   | ||||
| @ -141,7 +136,7 @@ function resumeUpload(e) { | ||||
|   > | ||||
|    | ||||
|     <!-- <n-image :src="extra.cover" preview-disabled /> --> | ||||
|     <video :src="videoSrc" :controls="false"></video> | ||||
|     <video :src="props.extra.url" :controls="false"></video> | ||||
|     <!-- 上传进度时的黑色半透明蒙层 --> | ||||
|     <div v-if="extra.is_uploading && !uploadFailed" class="upload-mask"></div> | ||||
|     <!-- 上传进度显示 --> | ||||
| @ -257,7 +252,7 @@ function resumeUpload(e) { | ||||
|   top: 0; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   background: rgba(0, 0, 0, 0.3); /* 降低不透明度,从0.45改为0.3,让视频封面能够显示 */ | ||||
|   background: rgba(0, 0, 0, 0.45); | ||||
|   z-index: 1; | ||||
|   border-radius: 5px; | ||||
| } | ||||
|  | ||||
| @ -15,7 +15,7 @@ const { showUserInfoModal } = useInject() | ||||
|     <div class="sys-text"> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a>{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.erp_user_id,user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
| 
 | ||||
|  | ||||
| @ -13,7 +13,7 @@ defineProps({ | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|  | ||||
| @ -14,14 +14,14 @@ const { showUserInfoModal } = useInject() | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|        | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|       <span>创建了群聊,并邀请了</span> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a >{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
|     </div> | ||||
|  | ||||
| @ -13,7 +13,7 @@ defineProps({ | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a> | ||||
|       <a @click="showUserInfoModal(data.user_id)"> | ||||
|         <!-- {{ data.nickname }} --> | ||||
|           管理员 | ||||
|       </a> | ||||
|  | ||||
| @ -13,14 +13,14 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|       <span>邀请了</span> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a>{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
| 
 | ||||
|  | ||||
| @ -13,14 +13,14 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|       <span>解除了</span> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a >{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
| 
 | ||||
|  | ||||
| @ -13,14 +13,14 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a> | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|       <span>将</span> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a>{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
| 
 | ||||
|  | ||||
| @ -13,14 +13,14 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a> | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|       <span>设置了</span> | ||||
| 
 | ||||
|       <template v-for="(user, index) in extra.members" :key="index"> | ||||
|         <a>{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
| 
 | ||||
|  | ||||
| @ -13,7 +13,7 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,7 @@ defineProps({ | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <template v-for="(user, index) in extra?.members" :key="index"> | ||||
|         <a >{{ user.nickname }}</a> | ||||
|         <a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a> | ||||
|         <em v-show="index < extra.members.length - 1">、</em> | ||||
|       </template> | ||||
|       <span>已离开此群</span> | ||||
|  | ||||
| @ -13,7 +13,7 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a > | ||||
|       <a @click="showUserInfoModal(extra.owner_id)"> | ||||
|         {{ extra.owner_name }} | ||||
|       </a> | ||||
| 
 | ||||
|  | ||||
| @ -13,9 +13,9 @@ const { showUserInfoModal } = useInject() | ||||
| <template> | ||||
|   <div class="im-message-sys-text"> | ||||
|     <div class="sys-text"> | ||||
|       <a >{{ extra.old_owner_name }}</a> | ||||
|       <a @click="showUserInfoModal(extra.old_owner_id)">{{ extra.old_owner_name }}</a> | ||||
|       <span>将群主转让给</span> | ||||
|       <a >{{ extra.new_owner_name }}</a> | ||||
|       <a @click="showUserInfoModal(extra.new_owner_id)">{{ extra.new_owner_name }}</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -10,6 +10,7 @@ | ||||
|     padding: 0 8px; | ||||
|     word-wrap: break-word; | ||||
|     color: #979191; | ||||
|     user-select: none; | ||||
|     font-weight: 300; | ||||
|     display: inline-block; | ||||
|     border-radius: 3px; | ||||
| @ -22,11 +23,13 @@ | ||||
| 
 | ||||
|     a { | ||||
|       color: #939596; | ||||
|      | ||||
|       cursor: pointer; | ||||
|       font-size: 12px; | ||||
|       font-weight: 400; | ||||
| 
 | ||||
|        | ||||
|       &:hover { | ||||
|         color: #462AA0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -96,7 +96,6 @@ export const MessageComponents = { | ||||
| 
 | ||||
| // 可转发的消息类型
 | ||||
| export const ForwardableMessageType = [ | ||||
|   ChatMsgTypeForward, | ||||
|   ChatMsgTypeText, | ||||
|   ChatMsgTypeCode, | ||||
|   ChatMsgTypeImage, | ||||
|  | ||||
| @ -245,6 +245,8 @@ export const useDialogueStore = defineStore('dialogue', { | ||||
|       }).then((res) => { | ||||
|         if (res.code == 200) { | ||||
|           this.batchDelDialogueRecord(msgIds) | ||||
|         } else { | ||||
|           window['$message'].warning(res.message) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
| @ -170,14 +170,14 @@ export const useUploadsStore = defineStore('uploads', { | ||||
|        | ||||
|       // 更新状态为上传中
 | ||||
|       currentItem.status = 1 | ||||
|       const updatedItem:any = this.findItem(uploadId) | ||||
|        | ||||
|       // 上传当前分片
 | ||||
|       try { | ||||
| 
 | ||||
|         const res = await ServeFileSubareaUpload(form) | ||||
|    | ||||
|         // 获取最新的项目状态,确保仍然存在且没有被暂停
 | ||||
|         | ||||
|         const updatedItem:any = this.findItem(uploadId) | ||||
|         if (res.code == 200) { | ||||
|           // 当前分片上传成功,增加索引
 | ||||
|           updatedItem.uploadIndex++ | ||||
| @ -209,12 +209,10 @@ export const useUploadsStore = defineStore('uploads', { | ||||
|          | ||||
|         } | ||||
|       } catch (error) { | ||||
|         updatedItem.onProgress(-1) | ||||
|         console.error("分片上传错误:", error); | ||||
|          | ||||
|         // 获取最新的项目状态
 | ||||
|         // 这里不应该重新定义变量,而是使用已有的updatedItem
 | ||||
|         // const updatedItem = this.findItem(uploadId)
 | ||||
|         const updatedItem = this.findItem(uploadId) | ||||
|         if (!updatedItem) return | ||||
|          | ||||
|         // 如果是暂停导致的错误,不改变状态
 | ||||
|  | ||||
| @ -18,7 +18,7 @@ export function isLoggedIn() { | ||||
|  */ | ||||
| export function getAccessToken() { | ||||
|   // return storage.get(AccessToken) || ''
 | ||||
|   return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b8993ebccf0349a53e3197efc45b9dbe3f2bf1dc0dddce6787811964e76efefec3b3fd39fce15d43989c156413f12de3f0c74c1ff1d3c5da214d3bcefef7546498e37fa73453c749a56ea66777488bd3550' | ||||
|   return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b891a491a664540c3af42964b31bedf8b1c93e8a754bb71e4b95d53ad8e6b16ac1575f536a9e7a062e44f3bb48a367623d38bd875a10afa3a53e79374ffda424138ed9ad4cab0d972432567ae7149b2bf3c' | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -53,13 +53,7 @@ request.interceptors.request.use((config) => { | ||||
| }, errorHandler) | ||||
| 
 | ||||
| // 响应拦截器
 | ||||
| request.interceptors.response.use((response) => { | ||||
|   console.log('response.data.status',response.data.status) | ||||
|   if(response.data.code !==200&&response.data.status!==0){ | ||||
|     window['$message'].warning(response.data.msg) | ||||
|   } | ||||
|   return response.data | ||||
| }, errorHandler) | ||||
| request.interceptors.response.use((response) => response.data, errorHandler) | ||||
| 
 | ||||
| /** | ||||
|  * GET 请求 | ||||
|  | ||||
| @ -330,7 +330,6 @@ const onContextMenuHandle = (key: string) => { | ||||
| 
 | ||||
| const onRowClick = (item: ITalkRecord) => { | ||||
|   if (dialogueStore.isOpenMultiSelect) { | ||||
|     console.log('item.msg_type',item.msg_type) | ||||
|     if (ForwardableMessageType.includes(item.msg_type)) { | ||||
|       item.isCheck = !item.isCheck | ||||
|     } else { | ||||
|  | ||||
| @ -101,13 +101,10 @@ const onSendImageEvent = ({ data, callBack }) => { | ||||
| 
 | ||||
| // 发送视频消息 | ||||
| const onSendVideoEvent = async ({ data }) => { | ||||
| 
 | ||||
|    | ||||
|   // 获取视频首帧作为封面图 | ||||
|   let videoPreview = null | ||||
|   try { | ||||
|     videoPreview = await getVideoImage(data) | ||||
|   } catch (error) { | ||||
|     console.error('获取视频封面失败:', error) | ||||
|   } | ||||
|   // let resp = await getVideoImage(data) | ||||
|    | ||||
|   // 先创建一个带有上传ID的临时消息对象,用于显示进度 | ||||
|   const uploadId = `video-${Date.now()}-${Math.floor(Math.random() * 1000)}` | ||||
| @ -126,7 +123,7 @@ const onSendVideoEvent = async ({ data }) => { | ||||
|     content: '', | ||||
|     created_at: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}'), | ||||
|     extra: { | ||||
|       url: videoPreview ? URL.createObjectURL(data) : '', // 使用本地视频URL作为预览 | ||||
|       url: '',  | ||||
|       size: data.size, | ||||
|       is_uploading: true, | ||||
|       upload_id: uploadId, | ||||
|  | ||||
| @ -10,34 +10,15 @@ interface IDropdown { | ||||
|   item: any | ||||
| } | ||||
| 
 | ||||
| const isRevoke = (uid: number, item: any): boolean => { | ||||
|   // 不是自己发的消息不能撤回
 | ||||
|   if (uid !== item.user_id) { | ||||
|     return false; | ||||
| const isRevoke = (uid: any, item: any): boolean => { | ||||
|   if (uid != item.user_id) { | ||||
|     return false | ||||
|   } | ||||
|    | ||||
|   // 检查消息是否在撤回时间限制内(5分钟)
 | ||||
|   const messageTime = dayjs(item.created_at); | ||||
|   const now = dayjs(); | ||||
|   const diffInMinutes = now.diff(messageTime, 'minute'); | ||||
|   return diffInMinutes <= 5; | ||||
| } | ||||
| // 判断是否可以添加撤回选项的函数
 | ||||
| const canAddRevokeOption = (uid: number, item: any, isManager: boolean): boolean => { | ||||
|   // 单聊情况:自己发的且在时间限制内
 | ||||
|   if (item.talk_type === 1) { | ||||
|     return isRevoke(uid, item) && item.float === 'right'; | ||||
|   } | ||||
|   // 群聊情况
 | ||||
|   else if (item.talk_type === 2) { | ||||
|     // 管理员可以撤回任何消息
 | ||||
|     if (isManager) { | ||||
|       return true; | ||||
|     } | ||||
|     // 普通成员只能撤回自己的且在时间限制内的消息
 | ||||
|     return isRevoke(uid, item) && item.float === 'right'; | ||||
|   } | ||||
|   return false; | ||||
| 
 | ||||
|   const messageTime = dayjs(item.created_at) | ||||
|   const now = dayjs() | ||||
|   const diffInMinutes = now.diff(messageTime, 'minute') | ||||
|   return diffInMinutes <= 5 | ||||
| } | ||||
| const dialogueStore = useDialogueStore() | ||||
| export function useMenu() { | ||||
| @ -68,8 +49,18 @@ export function useMenu() { | ||||
| 
 | ||||
|     dropdown.options.push({ label: '多选', key: 'multiSelect' }) | ||||
|     dropdown.options.push({ label: '引用', key: 'quote' }) | ||||
|     if (canAddRevokeOption(uid, item, (dialogueStore.groupInfo as any).is_manager)) { | ||||
|       dropdown.options.push({ label: '撤回', key: 'revoke' }); | ||||
|     //如果是单聊
 | ||||
|     if(item.talk_type===1){ | ||||
|       //撤回时间限制内,并且是自己发的
 | ||||
|       if(isRevoke(uid, item)&&item.float==='right'){ | ||||
|         dropdown.options.push({ label: `撤回`, key: 'revoke' }) | ||||
|       } | ||||
|       //群聊
 | ||||
|     }else if(item.talk_type===2){ | ||||
|       //管理员可以强制撤回所有成员信息
 | ||||
|       if ((dialogueStore.groupInfo as any).is_manager) { | ||||
|         dropdown.options.push({ label: `撤回`, key: 'revoke' }) | ||||
|       } | ||||
|     } | ||||
|     | ||||
|     dropdown.options.push({ label: '删除', key: 'delete' }) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user