fix
Some checks failed
Check / lint (push) Has been cancelled
Check / typecheck (push) Has been cancelled
Check / build (build, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:app, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:app, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Has been cancelled

This commit is contained in:
caiyx 2024-12-06 16:55:15 +08:00
parent a22727c2a2
commit afc4d72db1
18 changed files with 453 additions and 78 deletions

View File

@ -5,7 +5,7 @@
"packageManager": "pnpm@8.14.1", "packageManager": "pnpm@8.14.1",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test:h5": "uni --mode test", "test:h5": "uni --mode test --port 2468",
"prod:h5": "uni --mode prod", "prod:h5": "uni --mode prod",
"build:h5:test": "uni build --mode test", "build:h5:test": "uni build --mode test",
"build:h5:prod": "uni build --mode prod" "build:h5:prod": "uni build --mode prod"

View File

@ -1,4 +1,5 @@
import request from '@/service/index.js' import request from '@/service/index.js'
import qs from 'qs'
// 获取聊天列表服务接口 // 获取聊天列表服务接口
export const ServeGetTalkList = (data) => { export const ServeGetTalkList = (data) => {
@ -158,3 +159,14 @@ export const ServeConfirmVoteHandle = (data) => {
data, data,
}) })
} }
export const uploadImg = (data,onProgressFn) => {
return request({
url: '/upload/img',
method: 'POST',
data:data,
baseURL:import.meta.env.VITE_EPR_BASEURL,
isFormData:true,
onUploadProgress:(progressEvent)=>onProgressFn(progressEvent,data.get('file'))
})
}

View File

@ -1,20 +1,39 @@
<script lang="ts" setup> <script lang="ts" setup>
import { getImageInfo } from '@/utils/functions' import { getImageInfo } from '@/utils/functions'
defineProps<{ const props = defineProps<{
extra: any extra: any
data: any data: any
maxWidth?: Boolean maxWidth?: Boolean
}>() }>()
const img = (src: string, width = 200) => { const img = (src: string, width = 200) => {
const info = getImageInfo(src) // console.log(props.extra);
let info = {
width: 0,
height: 0
}
if (src.includes('blob:http://')) {
info = {
width: props.extra.width,
height: props.extra.height
}
}else {
info = getImageInfo(src)
}
if (info.width == 0 || info.height == 0) { if (info.width == 0 || info.height == 0) {
return { return {
width: 450, width: 450,
height: 298 height: 298
} }
} }
if(info.width<300){
return {
width: 300,
height: info.height / (info.width / 300)
}
}
if (info.width < width) { if (info.width < width) {
return { return {
@ -34,7 +53,10 @@ const img = (src: string, width = 200) => {
class="im-message-image" class="im-message-image"
:class="{ left: data.float === 'left' }" :class="{ left: data.float === 'left' }"
> >
<tm-image preview :width="img(extra.url,450).width" :height="img(extra.url,450).height" :src="extra.url" /> <div class="image-container">
<tm-image preview :width="img(extra.url,350).width" :height="img(extra.url,350).height" :src="extra.url" />
<wd-circle custom-class="circleProgress" v-if="props.data.uploadCurrent && props.data.uploadCurrent<100" v-model="props.data.uploadCurrent" color="#ffffff" layer-color="#E3E3E3"></wd-circle>
</div>
</section> </section>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
@ -51,4 +73,15 @@ const img = (src: string, width = 200) => {
border-radius: 0 16rpx 16rpx 16rpx; border-radius: 0 16rpx 16rpx 16rpx;
} }
} }
.image-container {
position: relative;
.circleProgress {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
}
</style> </style>

View File

@ -22,6 +22,13 @@ const img = (src: string, width = 200) => {
} }
} }
if(info.width<300){
return {
width: 300,
height: info.height / (info.width / 300)
}
}
if (info.width < width) { if (info.width < width) {
return { return {
width: info.width, width: info.width,

View File

@ -18,7 +18,7 @@ const videoContext = ref()
const open = ref(false) const open = ref(false)
const img = (src: string, width = 200) => { const img = (src: string, width = 200) => {
console.log(props); // console.log(props);
const info: any = getImageInfo(src) const info: any = getImageInfo(src)

View File

@ -6,7 +6,7 @@ import * as message from '@/constant/message'
import { formatTalkItem, palyMusic, formatTalkRecord } from '@/utils/talk' import { formatTalkItem, palyMusic, formatTalkRecord } from '@/utils/talk'
// import { isElectronMode } from '@/utils/common' // import { isElectronMode } from '@/utils/common'
import { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat/index.js' import { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat/index.js'
import { useTalkStore, useDialogueStore } from '@/store' import { useTalkStore, useDialogueStore,useDialogueListStore } from '@/store'
/** /**
* 好友状态事件 * 好友状态事件
@ -166,13 +166,13 @@ class Talk extends Base {
*/ */
insertTalkRecord() { insertTalkRecord() {
let record = this.resource let record = this.resource
let newRecord = formatTalkRecord(this.getAccountId(), this.resource);
// 群成员变化的消息,需要更新群成员列表 // 群成员变化的消息,需要更新群成员列表
if ([1102, 1103, 1104].includes(record.msg_type)) { if ([1102, 1103, 1104].includes(record.msg_type)) {
useDialogueStore().updateGroupMembers() useDialogueStore().updateGroupMembers()
} }
useDialogueListStore().addDialogueRecord([newRecord],'add')
useDialogueStore().addDialogueRecord(formatTalkRecord(this.getAccountId(), this.resource)) useDialogueStore().addDialogueRecord(newRecord)
if (!this.isCurrSender()) { if (!this.isCurrSender()) {
// 推送已读消息 // 推送已读消息

View File

@ -1,13 +1,21 @@
import { reactive, computed, nextTick } from 'vue' import { reactive, computed, nextTick } from 'vue'
import { ServeTalkRecords } from '@/api/chat' import { ServeTalkRecords } from '@/api/chat'
import { useDialogueStore } from '@/store' import { useDialogueStore, useDialogueListStore } from '@/store'
import { formatTalkRecord } from '@/utils/talk' import { formatTalkRecord } from '@/utils/talk'
import { addClass, removeClass } from '@/utils/dom' import { addClass, removeClass } from '@/utils/dom'
import { storeToRefs } from 'pinia'
export const useTalkRecord = (uid) => { export const useTalkRecord = (uid) => {
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
const dialogueListStore = useDialogueListStore()
const records = computed(() => dialogueStore.records) const { getDialogueList } = storeToRefs(dialogueListStore)
const records = computed(() => {
const dialogueList = getDialogueList.value(dialogueStore.index_name)
if(dialogueList){
return dialogueList.records
}
return dialogueStore.records
} )
const location = reactive({ const location = reactive({
msgid: '', msgid: '',
@ -63,20 +71,20 @@ export const useTalkRecord = (uid) => {
// 加载数据列表 // 加载数据列表
const load = async (params) => { const load = async (params) => {
const request = { const request = {
limit:30,
...params,
talk_type: params.talk_type, talk_type: params.talk_type,
receiver_id: params.receiver_id, receiver_id: params.receiver_id,
cursor: loadConfig.cursor, cursor: loadConfig.cursor,
limit: 30
} }
loadConfig.status = 0 loadConfig.status = 0
let scrollHeight = 0 let scrollHeight = 0
const el = document.getElementById('imChatPanel') const el = document.getElementById('imChatPanel')
if (el) { // if (el) {
scrollHeight = el.scrollHeight // scrollHeight = el.scrollHeight
} // }
const { data, code } = await ServeTalkRecords(request) const { data, code } = await ServeTalkRecords(request)
if (code != 200) { if (code != 200) {
return (loadConfig.status = 1) return (loadConfig.status = 1)
@ -89,19 +97,18 @@ export const useTalkRecord = (uid) => {
) { ) {
return (location.msgid = '') return (location.msgid = '')
} }
const items = (data.items || []).map((item) => formatTalkRecord(uid, item)) const items = (data.items || []).map((item) => formatTalkRecord(uid, item))
if (request.cursor == 0) { if (request.cursor == 0) {
// 判断是否是初次加载 // 判断是否是初次加载
dialogueStore.clearDialogueRecord() dialogueStore.clearDialogueRecord()
} }
const reverseItems = items.toReversed()
dialogueStore.unshiftDialogueRecord(items.reverse()) dialogueStore.unshiftDialogueRecord(reverseItems)
dialogueListStore.addDialogueRecord(params.direction=='down'?reverseItems:items,params.direction=='down'?'add':'unshift')
loadConfig.status = items.length >= request.limit ? 1 : 2 const dialogueList = getDialogueList.value(dialogueStore.index_name)
loadConfig.status = dialogueList.records[0].sequence > 1 ? 1 : 2
loadConfig.cursor = data.cursor // loadConfig.cursor = data.cursor
nextTick(() => { nextTick(() => {
const el = document.getElementById('imChatPanel') const el = document.getElementById('imChatPanel')
@ -125,21 +132,38 @@ export const useTalkRecord = (uid) => {
} }
const onRefreshLoad = () => { const onRefreshLoad = () => {
let dialogueList = getDialogueList.value(dialogueStore.index_name)
if (dialogueList.records[0].sequence === 1 ) {
return false
}
if (loadConfig.status == 1) { if (loadConfig.status == 1) {
let filterList = dialogueList.records.filter(item=>item.sequence !== -1)
loadConfig.cursor = filterList[0].sequence
load({ load({
receiver_id: loadConfig.receiver_id, receiver_id: loadConfig.receiver_id,
talk_type: loadConfig.talk_type, talk_type: loadConfig.talk_type,
limit: 30 direction:'up',
no_limit:0,
}) })
} }
} }
const onLoad = (params) => { const onLoad = async (params) => {
loadConfig.cursor = 0 let dialogueList = getDialogueList.value(params.index_name)
if (!dialogueList) {
loadConfig.cursor = 0
}else{
let filterList = dialogueList.records.filter(item=>item.sequence !== -1)
if(params.direction=='up'){
loadConfig.cursor = filterList[0].sequence
}else{
loadConfig.cursor = filterList[filterList.length-1].sequence
}
}
loadConfig.receiver_id = params.receiver_id loadConfig.receiver_id = params.receiver_id
loadConfig.talk_type = params.talk_type loadConfig.talk_type = params.talk_type
load(params) await load(params)
} }
return { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage } return { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage }

View File

@ -0,0 +1,160 @@
<template>
<div class="emojiRoot">
<div @click="choosePhoto" class="flex flex-col items-center">
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
<tm-image :width="53" :height="44" :src="photoAlbum"></tm-image>
</div>
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">照片</div>
</div>
<div @click="takePhoto" class="flex flex-col items-center">
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
<tm-image :width="53" :height="44" :src="photoGraph"></tm-image>
</div>
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">拍摄</div>
</div>
<div @click="chooseFile" class="flex flex-col items-center">
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
<tm-image :width="53" :height="44" :src="folder"></tm-image>
</div>
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">文件</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, defineProps, defineEmits } from "vue"
import dayjs from "dayjs";
import { beautifyTime } from '@/utils/datetime'
import { useDialogueListStore, useDialogueStore, useUserStore } from '@/store'
import { useSessionMenu } from '@/hooks'
import photoAlbum from '@/static/image/chatList/photoAlbum.png'
import photoGraph from '@/static/image/chatList/photoGraph.png'
import folder from '@/static/image/chatList/folder.png'
import { uploadImg } from '@/api/chat'
import { uniqueId } from '@/utils'
const props = defineProps({
sendUserInfo: {
type: Object,
default: {},
required: true
}
});
const dialogueListStore = useDialogueListStore()
const dialogueStore = useDialogueStore()
const userStore = useUserStore()
const emit = defineEmits(['selectImg','updateUploadProgress'])
const onProgressFn = (progress, file,id) => {
emit('updateUploadProgress', id, progress.loaded / progress.total * 100)
}
const onUploadImage = (file) => {
return new Promise((resolve) => {
let image = new Image()
image.src = URL.createObjectURL(file)
image.onload = () => {
const form = new FormData()
form.append('file', file)
form.append("source", "fonchain-chat");
form.append("urlParam", `width=${image.width}&height=${image.height}`);
let randomId = uniqueId()
dialogueListStore.addDialogueRecord([
{
avatar: userStore.avatar,
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
extra: {
height: image.height,
name: "",
size: 0,
url: image.src,
width: image.width
},
float: "right",
isCheck: false,
is_mark: 0,
is_read: 0,
is_revoke: 0,
msg_id: randomId,
msg_type: 3,
nickname: userStore.nickname,
receiver_id: dialogueStore.talk.receiver_id,
sequence: -1,
talk_type: dialogueStore.talk.talk_type,
user_id: userStore.uid,
uploadCurrent: 0
}
], 'add')
uploadImg(form, (e, file) => onProgressFn(e, file, randomId)).then(({ status, data, msg }) => {
if (status == 0) {
// message.success('')
resolve({
url: data.ori_url,
size: file.size,
width: image.width,
height: image.height
})
} else {
resolve('')
message.error(msg)
}
})
}
})
}
const choosePhoto = () => {
uni.chooseImage({
sourceType: ['album'],
success: async (res) => {
console.log(res.tempFiles);
res.tempFiles.forEach(async item => {
if (item.type.indexOf('image/') === 0) {
console.log("进入图片")
let data = await onUploadImage(item)
return false;
emit('selectImg', data)
// if (src) {
// quill.insertEmbed(index, 'image', src)
// quill.setSelection(index + 1)
// }
return
}
if (item.type.indexOf('video/') === 0) {
console.log("进入视频")
let fn = emitCall('video_event', item, () => { })
emit('editor-event', fn)
}
})
},
fail: (err) => {
console.log(err);
message.error(err)
}
})
}
const takePhoto = () => {
}
const chooseFile = () => {
}
</script>
<style lang="scss" scoped>
.emojiRoot {
width: 100%;
height: 232rpx;
padding: 20rpx 130rpx 0 130rpx;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
</style>

View File

@ -15,19 +15,46 @@
</div> </div>
<div class="root"> <div class="root">
<div class="dialogBox"> <div class="dialogBox">
<ZPaging :fixed="false" use-chat-record-mode :use-page-scroll="false" :refresher-enabled="false" <ZPaging
:show-scrollbar="false" :loading-more-enabled="true" :hide-empty-view="true" height="100%" ref="pagingRef" :fixed="false"
@scrolltolower="onRefreshLoad"> use-chat-record-mode
:use-page-scroll="false"
:refresher-enabled="false"
:show-scrollbar="false"
:loading-more-enabled="false"
:hide-empty-view="true"
height="100%"
ref="zpagingRef"
:use-virtual-list="true"
:preload-page="1"
cell-height-mode="dynamic"
:loading-more-custom-style="{display:'none',height:'0'}"
@virtualListChange="virtualListChange"
@scrolltolower="onRefreshLoad"
>
<!-- <template #top>
<div class="load-toolbar pointer">
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span>
<span v-else class="no-more"> 没有更多消息了 </span>
</div>
</template> -->
<!-- 数据加载状态栏 --> <!-- 数据加载状态栏 -->
<div class="message-item" v-for="(item, index) in reversedRecords" style="transform: scaleY(-1);" <div
:key="item.msg_id" :id="item.msg_id"> class="message-item"
v-for="item in virtualList"
:id="`zp-id-${item.zp_index}`"
:key="item.zp_index"
style="transform: scaleY(-1);"
>
<!-- 系统消息 --> <!-- 系统消息 -->
<div v-if="item.msg_type >= 1000" class="message-box"> <div v-if="item.msg_type >= 1000" class="message-box">
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" /> <component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" />
</div> </div>
<!-- 撤回消息 --> <!-- 撤回消息 -->
<div v-else-if="item.is_revoke == 1" class="message-box"> <div v-else-if="item.is_revoke == 1" class="message-box">
<revoke-message :login_uid="uid" :user_id="item.user_id" :nickname="item.nickname" <revoke-message :login_uid="userStore.uid" :user_id="item.user_id" :nickname="item.nickname"
:talk_type="item.talk_type" :datetime="item.created_at" /> :talk_type="item.talk_type" :datetime="item.created_at" />
</div> </div>
@ -37,9 +64,9 @@
'multi-select-check': item.isCheck 'multi-select-check': item.isCheck
}"> }">
<!-- 多选按钮 --> <!-- 多选按钮 -->
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column"> <!-- <aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
<n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" /> <n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" />
</aside> </aside> -->
<!-- 头像信息 --> <!-- 头像信息 -->
<aside class="avatar-column"> <aside class="avatar-column">
@ -50,7 +77,7 @@
<!-- 主体信息 --> <!-- 主体信息 -->
<main class="main-column"> <main class="main-column">
<div class="talk-title"> <div class="talk-title">
<span class="nickname pointer" v-show="talk_type == 2 && item.float == 'left'" <span class="nickname pointer" v-show="talkParams.type == 2 && item.float == 'left'"
@click="onClickNickname(item)"> @click="onClickNickname(item)">
<span class="at">@</span>{{ item.nickname }} <span class="at">@</span>{{ item.nickname }}
</span> </span>
@ -62,16 +89,16 @@
:data="item" :max-width="true" :source="'panel'" :data="item" :max-width="true" :source="'panel'"
@contextmenu.prevent="onContextMenu($event, item)" /> @contextmenu.prevent="onContextMenu($event, item)" />
<div class="talk-tools"> <!-- <div class="talk-tools">
<template v-if="talk_type == 1 && item.float == 'right'"> <template v-if="talkParams.type == 1 && item.float == 'right'">
<loading theme="outline" size="19" fill="#000" :strokeWidth="1" class="icon-rotate" <loading theme="outline" size="19" fill="#000" :strokeWidth="1" class="icon-rotate"
v-show="item.send_status == 1" /> v-show="item.send_status == 1" />
<span v-show="item.send_status == 1"> 正在发送... </span> <span v-show="item.send_status == 1"> 正在发送... </span>
<!-- <span v-show="item.send_status != 1"> 已送达 </span> --> <span v-show="item.send_status != 1"> 已送达 </span>
</template> </template>
</div> </div> -->
</div> </div>
<div v-if="item.extra.reply" class="talk-reply pointer" <div v-if="item.extra.reply" class="talk-reply pointer"
@ -87,10 +114,11 @@
</div> </div>
<div class="load-toolbar pointer" style="transform: scaleY(-1);"> <div class="load-toolbar pointer" style="transform: scaleY(-1);">
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span> <span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span> <span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span>
<span v-else class="no-more"> 没有更多消息了 </span> <span v-else class="no-more"> 没有更多消息了 </span>
</div> </div>
</ZPaging> </ZPaging>
</div> </div>
</div> </div>
@ -102,13 +130,17 @@
<!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]" <!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]"
placeholder=""></tm-input> --> placeholder=""></tm-input> -->
</div> </div>
<tm-image :margin="[10,0]" @click="state.isOpenEmojiPanel = !state.isOpenEmojiPanel" :width="52" :height="52" :round="12" <tm-image :margin="[10,0]" @click="handleEmojiPanel" :width="52" :height="52" :round="12"
:src="state.isOpenEmojiPanel ? keyboard : smile"></tm-image> :src="state.isOpenEmojiPanel ? keyboard : smile"></tm-image>
<tm-image :margin="[10,0]" :width="52" :height="52" :round="12" :src="addCircleGray"></tm-image> <tm-image @click="handleFilePanel" :margin="[10,0]" :width="52" :height="52" :round="12" :src="addCircleGray"></tm-image>
<tm-button @click="onSendMessageClick" :margin="[0,0]" :padding="[0,30]" color="#46299D" :fontSize="28" size="mini" :shadow="0" label="发送"></tm-button> <tm-button @click="onSendMessageClick" :margin="[0,0]" :padding="[0,30]" color="#46299D" :fontSize="28" size="mini" :shadow="0" label="发送"></tm-button>
</div> </div>
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]"> <div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
<emoji-panel @on-select="onEmoticonEvent" /> <emojiPanel @on-select="onEmoticonEvent" />
</div>
<div v-if="state.isOpenFilePanel" class="mt-[16rpx]">
<filePanel @selectImg="handleSelectImg" />
</div> </div>
<!--底部安全区--> <!--底部安全区-->
<div class="content-placeholder"></div> <div class="content-placeholder"></div>
@ -116,12 +148,12 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, watch, computed, onMounted } from 'vue'; import { ref, reactive, watch, computed, onMounted,onUnmounted } from 'vue';
import { QuillEditor, Quill } from '@vueup/vue-quill' import { QuillEditor, Quill } from '@vueup/vue-quill'
import EmojiBlot from './formats/emoji' import EmojiBlot from './formats/emoji'
import { useChatList } from "@/store/chatList/index.js"; import { useChatList } from "@/store/chatList/index.js";
import { useAuth } from "@/store/auth"; import { useAuth } from "@/store/auth";
import { useUserStore, useDialogueStore, useUploadsStore, useEditorDraftStore,useTalkStore,useSettingsStore } from '@/store' import { useUserStore, useDialogueStore, useUploadsStore, useEditorDraftStore,useTalkStore,useSettingsStore,useDialogueListStore } from '@/store'
import addCircleGray from "@/static/image/chatList/addCircleGray.png"; import addCircleGray from "@/static/image/chatList/addCircleGray.png";
import { MessageComponents, ForwardableMessageType } from '@/constant/message' import { MessageComponents, ForwardableMessageType } from '@/constant/message'
import { formatTime, parseTime } from '@/utils/datetime' import { formatTime, parseTime } from '@/utils/datetime'
@ -133,19 +165,23 @@ import { emitCall } from '@/utils/common'
import ZPaging from "@/uni_modules/z-paging/components/z-paging/z-paging.vue"; import ZPaging from "@/uni_modules/z-paging/components/z-paging/z-paging.vue";
import useZPaging from "@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js"; import useZPaging from "@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js";
import emojiPanel from './components/emojiPanel.vue' import emojiPanel from './components/emojiPanel.vue'
import filePanel from './components/filePanel.vue'
import lodash from "lodash"; import lodash from "lodash";
import { ServePublishMessage } from '@/api/chat' import { ServePublishMessage } from '@/api/chat'
import { storeToRefs } from 'pinia'
Quill.register('formats/emoji', EmojiBlot) Quill.register('formats/emoji', EmojiBlot)
const dialogueListStore = useDialogueListStore()
const { getDialogueList } = storeToRefs(dialogueListStore)
const talkStore = useTalkStore() const talkStore = useTalkStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const userStore = useUserStore() const userStore = useUserStore()
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
const editorDraftStore = useEditorDraftStore() const editorDraftStore = useEditorDraftStore()
const editor = ref() const editor = ref()
const pagingRef = ref(null) const zpagingRef = ref(null)
useZPaging(pagingRef) useZPaging(zpagingRef)
const indexName = computed(() => dialogueStore.index_name) const indexName = computed(() => dialogueStore.index_name)
const talkParams = reactive({ const talkParams = reactive({
uid: computed(() => userStore.uid), uid: computed(() => userStore.uid),
@ -159,9 +195,21 @@ const talkParams = reactive({
}) })
const state = ref({ const state = ref({
isOpenEmojiPanel: false isOpenEmojiPanel: false,
isOpenFilePanel: false
}) })
const virtualList = ref([])
const handleEmojiPanel = () => {
state.value.isOpenFilePanel = false;
state.value.isOpenEmojiPanel = !state.value.isOpenEmojiPanel
}
const handleFilePanel = () => {
state.value.isOpenEmojiPanel = false;
state.value.isOpenFilePanel = !state.value.isOpenFilePanel
}
const onSendMessage = (data = {}, callBack) => { const onSendMessage = (data = {}, callBack) => {
let message = { let message = {
...data, ...data,
@ -320,7 +368,6 @@ const onEditorChange = () => {
} }
const onClipboardMatcher = (node, Delta) => { const onClipboardMatcher = (node, Delta) => {
// debugger
const ops = [] const ops = []
Delta.ops.forEach((op) => { Delta.ops.forEach((op) => {
@ -394,33 +441,40 @@ const editorOption = {
placeholder: '', placeholder: '',
} }
const handleSelectImg = (data) => {
onSendMessage({ type: 'image', ...data })
}
const virtualListChange = (vList) => {
virtualList.value = vList
}
watch(() => records, (newValue, oldValue) => { watch(() => reversedRecords.value, (newValue, oldValue) => {
console.log(newValue); zpagingRef.value?.complete(newValue)
}, { deep: true, immediate: true }) }, { deep: true, immediate: true })
watch(() => talkParams.uid, (newValue, oldValue) => { onMounted(async() => {
const dialogueList = getDialogueList.value(talkParams.index_name)
if (dialogueList?.records?.length) {
debugger
zpagingRef.value?.complete(dialogueList.records)
}
let objT = { let objT = {
uid: talkParams.uid, uid: talkParams.uid,
talk_type: talkParams.type, talk_type: talkParams.type,
receiver_id: talkParams.receiver_id, receiver_id: talkParams.receiver_id,
index_name: talkParams.index_name index_name: talkParams.index_name,
direction:dialogueList?'down':'up',
no_limit: dialogueList?1:0,
} }
onLoad({ ...objT, limit: 30 }) onLoad({ ...objT })
}, { immediate: true })
onMounted(() => {
let objT = {
uid: talkParams.uid,
talk_type: talkParams.type,
receiver_id: talkParams.receiver_id,
index_name: talkParams.index_name
}
onLoad({ ...objT, limit: 30 })
}) })
onUnmounted(() => {
dialogueStore.setDialogue({})
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
uni-page-body, uni-page-body,

View File

@ -9,16 +9,19 @@ const request = new Request({
interceptors: { interceptors: {
//实例的请求拦截器 //实例的请求拦截器
requestInterceptors: (config) => { requestInterceptors: (config) => {
config.headers['Content-Type'] = config.method === 'get' ? config.headers["Content-Type"] =
'application/x-www-form-urlencoded' : config.method === "get"
'application/json'; ? "application/x-www-form-urlencoded"
config.headers['Authorization'] = token.value : config.contentType
? config.contentType
: "application/json";
config.headers['Authorization'] = token.value || '';
if (config.isFormData) { if (config.isFormData) {
config.headers['Content-Type'] = 'multipart/form-data'; config.headers['Content-Type'] = 'multipart/form-data';
} }
return config; return config;
}, },
//实例的响应拦截器
responseInterceptors: async (res) => { responseInterceptors: async (res) => {
if(res.data.status===1){ if(res.data.status===1){
message.warning(res.data.msg) message.warning(res.data.msg)

View File

@ -1,7 +1,4 @@
import axios from 'axios'; import axios from 'axios';
import { createUniAppAxiosAdapter } from '@uni-helper/axios-adapter'
axios.defaults.adapter = createUniAppAxiosAdapter()
class Request { class Request {
// axios 实例 // axios 实例
instance; instance;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -4,7 +4,7 @@ import {uniStorage} from "@/utils/uniStorage.js"
import {ref} from 'vue' import {ref} from 'vue'
export const useAuth = createGlobalState(() => { export const useAuth = createGlobalState(() => {
// const token = useStorage('token', '', uniStorage) // const token = useStorage('token', '', uniStorage)
const token = ref('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ffea38d3362de44912166520e87a6f38daaa6ef5faf4f643724f7a84be3e96d634f3c10379df86df00c605eb7b1120d2919e9b417e127bfe4828f3b56eb67ed1d62a401a9a4c065cf7b038001dd54637387e965bebe403133e408b3d4224f75952ee7682ff4e8904cce7d7c7e9766e958d253169bf05c6c5272dba34ff43ee1045') const token = ref('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ffea38d3362de44912166520e87a6f38dae8dda9ac1ca35393126a287143c4b6bf18216b911f8a76572c62563bf215ccfbf481b73f0bf6d5275d6ba21139798506790b4824a503c2fbbe8230a1118dd07583187e5c342dd8f8ca02f70dbd2dcea17ac3c5fbcf6e39893a63d2e79b34908949ce628eaede23e241afbcf5b83e01b9')
const refreshToken = useStorage('refreshToken', '', uniStorage) const refreshToken = useStorage('refreshToken', '', uniStorage)
const userInfo = useStorage('userInfo', {}, uniStorage) const userInfo = useStorage('userInfo', {}, uniStorage)
const leaderList = useStorage('leaderList', [], uniStorage) const leaderList = useStorage('leaderList', [], uniStorage)

View File

@ -5,4 +5,5 @@ export * from '@/store/modules/talk'
export * from '@/store/modules/dialogue' export * from '@/store/modules/dialogue'
export * from '@/store/modules/editor-draft' export * from '@/store/modules/editor-draft'
export * from '@/store/modules/uploads' export * from '@/store/modules/uploads'
export * from '@/store/modules/dialogueList'
// export * from '@/store/modules/note' // export * from '@/store/modules/note'

View File

@ -0,0 +1,79 @@
import { defineStore } from 'pinia'
import { useDialogueStore } from '@/store'
import lodash from 'lodash'
export const useDialogueListStore = defineStore('dialogueList', {
persist: true,
state: () => {
return {
dialogueList:[]
}
},
getters: {
getDialogueList:(state)=>{
return (indexName) => state.dialogueList.find(item=>item.index_name===indexName)
}
},
actions: {
// 添加对话
addDialogueRecord(newRecords,type='add') {
console.log(newRecords);
const dialogue = lodash.cloneDeep(useDialogueStore())
if (!dialogue || typeof dialogue !== 'object') return
// 检查是否已存在相同 index_name 的对话
const existingIndex = this.$state.dialogueList.findIndex(item => item.index_name === dialogue.index_name)
if (existingIndex === -1) {
// 如果不存在,直接添加
this.$state.dialogueList.push(dialogue)
} else {
// 如果对话存在,处理 records 数组
const { records = [] } = dialogue
newRecords.forEach(newRecord => {
const recordIndex = this.$state.dialogueList[existingIndex].records.findIndex(
record => record.msg_id === newRecord.msg_id
)
if (recordIndex === -1) {
// 如果记录不存在,添加到 records 数组
if(type==='add'){
this.$state.dialogueList[existingIndex].records.push(newRecord)
}else{
this.$state.dialogueList[existingIndex].records.unshift(newRecord)
}
}
})
// 更新除 records 和 index_name 外的其他属性
const { index_name, records: _, ...updateProps } = dialogue
this.$state.dialogueList[existingIndex] = {
...this.$state.dialogueList[existingIndex],
...updateProps
}
}
},
// // 更新上传图片进度
// updateUploadProgress(msgId,progress){
// console.log('updateUploadProgress');
// const dialogue = lodash.cloneDeep(useDialogueStore())
// const item = this.getDialogueList(dialogue.index_name)
// const record = item.records.find(item=>item.msg_id===msgId)
// if(record){
// record.uploadCurrent = progress
// console.log(record.uploadCurrent);
// }
// }
// 自增好友键盘输入事件
// triggerKeyboard() {
// this.keyboard = true
// clearTimeout(keyboardTimeout)
// keyboardTimeout = setTimeout(() => (this.keyboard = false), 2000)
// },
}
})

View File

@ -82,3 +82,8 @@ export const convertBd09ToWgs84 = (bd_lon, bd_lat) => {
latitude: wgs84[1], latitude: wgs84[1],
}; };
} }
export const uniqueId = () => 'id-' + new Date().getTime().toString(36) + '-' + Math.random().toString(36).substr(2, 9);