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
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:
parent
a22727c2a2
commit
afc4d72db1
@ -5,7 +5,7 @@
|
||||
"packageManager": "pnpm@8.14.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test:h5": "uni --mode test",
|
||||
"test:h5": "uni --mode test --port 2468",
|
||||
"prod:h5": "uni --mode prod",
|
||||
"build:h5:test": "uni build --mode test",
|
||||
"build:h5:prod": "uni build --mode prod"
|
||||
|
@ -1,4 +1,5 @@
|
||||
import request from '@/service/index.js'
|
||||
import qs from 'qs'
|
||||
|
||||
// 获取聊天列表服务接口
|
||||
export const ServeGetTalkList = (data) => {
|
||||
@ -158,3 +159,14 @@ export const ServeConfirmVoteHandle = (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'))
|
||||
})
|
||||
}
|
||||
|
@ -1,20 +1,39 @@
|
||||
<script lang="ts" setup>
|
||||
import { getImageInfo } from '@/utils/functions'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
extra: any
|
||||
data: any
|
||||
maxWidth?: Boolean
|
||||
}>()
|
||||
|
||||
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) {
|
||||
return {
|
||||
width: 450,
|
||||
height: 298
|
||||
}
|
||||
}
|
||||
if(info.width<300){
|
||||
return {
|
||||
width: 300,
|
||||
height: info.height / (info.width / 300)
|
||||
}
|
||||
}
|
||||
|
||||
if (info.width < width) {
|
||||
return {
|
||||
@ -34,7 +53,10 @@ const img = (src: string, width = 200) => {
|
||||
class="im-message-image"
|
||||
: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>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
@ -51,4 +73,15 @@ const img = (src: string, width = 200) => {
|
||||
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>
|
||||
|
@ -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) {
|
||||
return {
|
||||
width: info.width,
|
||||
|
@ -18,7 +18,7 @@ const videoContext = ref()
|
||||
const open = ref(false)
|
||||
|
||||
const img = (src: string, width = 200) => {
|
||||
console.log(props);
|
||||
// console.log(props);
|
||||
|
||||
const info: any = getImageInfo(src)
|
||||
|
||||
|
@ -6,7 +6,7 @@ import * as message from '@/constant/message'
|
||||
import { formatTalkItem, palyMusic, formatTalkRecord } from '@/utils/talk'
|
||||
// import { isElectronMode } from '@/utils/common'
|
||||
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() {
|
||||
let record = this.resource
|
||||
|
||||
let newRecord = formatTalkRecord(this.getAccountId(), this.resource);
|
||||
// 群成员变化的消息,需要更新群成员列表
|
||||
if ([1102, 1103, 1104].includes(record.msg_type)) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
}
|
||||
|
||||
useDialogueStore().addDialogueRecord(formatTalkRecord(this.getAccountId(), this.resource))
|
||||
useDialogueListStore().addDialogueRecord([newRecord],'add')
|
||||
useDialogueStore().addDialogueRecord(newRecord)
|
||||
|
||||
if (!this.isCurrSender()) {
|
||||
// 推送已读消息
|
||||
|
@ -1,13 +1,21 @@
|
||||
import { reactive, computed, nextTick } from 'vue'
|
||||
import { ServeTalkRecords } from '@/api/chat'
|
||||
import { useDialogueStore } from '@/store'
|
||||
import { useDialogueStore, useDialogueListStore } from '@/store'
|
||||
import { formatTalkRecord } from '@/utils/talk'
|
||||
import { addClass, removeClass } from '@/utils/dom'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
export const useTalkRecord = (uid) => {
|
||||
const dialogueStore = useDialogueStore()
|
||||
|
||||
const records = computed(() => dialogueStore.records)
|
||||
const dialogueListStore = useDialogueListStore()
|
||||
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({
|
||||
msgid: '',
|
||||
@ -63,20 +71,20 @@ export const useTalkRecord = (uid) => {
|
||||
// 加载数据列表
|
||||
const load = async (params) => {
|
||||
const request = {
|
||||
limit:30,
|
||||
...params,
|
||||
talk_type: params.talk_type,
|
||||
receiver_id: params.receiver_id,
|
||||
cursor: loadConfig.cursor,
|
||||
limit: 30
|
||||
}
|
||||
|
||||
loadConfig.status = 0
|
||||
|
||||
let scrollHeight = 0
|
||||
const el = document.getElementById('imChatPanel')
|
||||
if (el) {
|
||||
scrollHeight = el.scrollHeight
|
||||
}
|
||||
|
||||
// if (el) {
|
||||
// scrollHeight = el.scrollHeight
|
||||
// }
|
||||
const { data, code } = await ServeTalkRecords(request)
|
||||
if (code != 200) {
|
||||
return (loadConfig.status = 1)
|
||||
@ -89,19 +97,18 @@ export const useTalkRecord = (uid) => {
|
||||
) {
|
||||
return (location.msgid = '')
|
||||
}
|
||||
|
||||
const items = (data.items || []).map((item) => formatTalkRecord(uid, item))
|
||||
|
||||
if (request.cursor == 0) {
|
||||
// 判断是否是初次加载
|
||||
dialogueStore.clearDialogueRecord()
|
||||
}
|
||||
|
||||
dialogueStore.unshiftDialogueRecord(items.reverse())
|
||||
|
||||
loadConfig.status = items.length >= request.limit ? 1 : 2
|
||||
|
||||
loadConfig.cursor = data.cursor
|
||||
const reverseItems = items.toReversed()
|
||||
dialogueStore.unshiftDialogueRecord(reverseItems)
|
||||
dialogueListStore.addDialogueRecord(params.direction=='down'?reverseItems:items,params.direction=='down'?'add':'unshift')
|
||||
const dialogueList = getDialogueList.value(dialogueStore.index_name)
|
||||
loadConfig.status = dialogueList.records[0].sequence > 1 ? 1 : 2
|
||||
// loadConfig.cursor = data.cursor
|
||||
|
||||
nextTick(() => {
|
||||
const el = document.getElementById('imChatPanel')
|
||||
@ -125,21 +132,38 @@ export const useTalkRecord = (uid) => {
|
||||
}
|
||||
|
||||
const onRefreshLoad = () => {
|
||||
let dialogueList = getDialogueList.value(dialogueStore.index_name)
|
||||
if (dialogueList.records[0].sequence === 1 ) {
|
||||
return false
|
||||
}
|
||||
if (loadConfig.status == 1) {
|
||||
let filterList = dialogueList.records.filter(item=>item.sequence !== -1)
|
||||
loadConfig.cursor = filterList[0].sequence
|
||||
load({
|
||||
receiver_id: loadConfig.receiver_id,
|
||||
talk_type: loadConfig.talk_type,
|
||||
limit: 30
|
||||
direction:'up',
|
||||
no_limit:0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const onLoad = (params) => {
|
||||
loadConfig.cursor = 0
|
||||
const onLoad = async (params) => {
|
||||
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.talk_type = params.talk_type
|
||||
|
||||
load(params)
|
||||
await load(params)
|
||||
}
|
||||
|
||||
return { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage }
|
||||
|
160
src/pages/dialog/components/filePanel.vue
Normal file
160
src/pages/dialog/components/filePanel.vue
Normal 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>
|
@ -15,19 +15,46 @@
|
||||
</div>
|
||||
<div class="root">
|
||||
<div class="dialogBox">
|
||||
<ZPaging :fixed="false" use-chat-record-mode :use-page-scroll="false" :refresher-enabled="false"
|
||||
:show-scrollbar="false" :loading-more-enabled="true" :hide-empty-view="true" height="100%" ref="pagingRef"
|
||||
@scrolltolower="onRefreshLoad">
|
||||
<ZPaging
|
||||
:fixed="false"
|
||||
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);"
|
||||
:key="item.msg_id" :id="item.msg_id">
|
||||
<div
|
||||
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">
|
||||
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" />
|
||||
</div>
|
||||
<!-- 撤回消息 -->
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
@ -37,9 +64,9 @@
|
||||
'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" />
|
||||
</aside>
|
||||
</aside> -->
|
||||
|
||||
<!-- 头像信息 -->
|
||||
<aside class="avatar-column">
|
||||
@ -50,7 +77,7 @@
|
||||
<!-- 主体信息 -->
|
||||
<main class="main-column">
|
||||
<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)">
|
||||
<span class="at">@</span>{{ item.nickname }}
|
||||
</span>
|
||||
@ -62,16 +89,16 @@
|
||||
:data="item" :max-width="true" :source="'panel'"
|
||||
@contextmenu.prevent="onContextMenu($event, item)" />
|
||||
|
||||
<div class="talk-tools">
|
||||
<template v-if="talk_type == 1 && item.float == 'right'">
|
||||
<!-- <div class="talk-tools">
|
||||
<template v-if="talkParams.type == 1 && item.float == 'right'">
|
||||
<loading theme="outline" size="19" fill="#000" :strokeWidth="1" class="icon-rotate"
|
||||
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>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div v-if="item.extra.reply" class="talk-reply pointer"
|
||||
@ -87,10 +114,11 @@
|
||||
|
||||
</div>
|
||||
<div class="load-toolbar pointer" style="transform: scaleY(-1);">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
</ZPaging>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,13 +130,17 @@
|
||||
<!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]"
|
||||
placeholder=""></tm-input> -->
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
<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 class="content-placeholder"></div>
|
||||
@ -116,12 +148,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 EmojiBlot from './formats/emoji'
|
||||
import { useChatList } from "@/store/chatList/index.js";
|
||||
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 { MessageComponents, ForwardableMessageType } from '@/constant/message'
|
||||
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 useZPaging from "@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js";
|
||||
import emojiPanel from './components/emojiPanel.vue'
|
||||
import filePanel from './components/filePanel.vue'
|
||||
import lodash from "lodash";
|
||||
import { ServePublishMessage } from '@/api/chat'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
Quill.register('formats/emoji', EmojiBlot)
|
||||
|
||||
const dialogueListStore = useDialogueListStore()
|
||||
const { getDialogueList } = storeToRefs(dialogueListStore)
|
||||
const talkStore = useTalkStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const editorDraftStore = useEditorDraftStore()
|
||||
const editor = ref()
|
||||
const pagingRef = ref(null)
|
||||
useZPaging(pagingRef)
|
||||
const zpagingRef = ref(null)
|
||||
useZPaging(zpagingRef)
|
||||
const indexName = computed(() => dialogueStore.index_name)
|
||||
const talkParams = reactive({
|
||||
uid: computed(() => userStore.uid),
|
||||
@ -159,9 +195,21 @@ const talkParams = reactive({
|
||||
})
|
||||
|
||||
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) => {
|
||||
let message = {
|
||||
...data,
|
||||
@ -320,7 +368,6 @@ const onEditorChange = () => {
|
||||
}
|
||||
|
||||
const onClipboardMatcher = (node, Delta) => {
|
||||
// debugger
|
||||
const ops = []
|
||||
|
||||
Delta.ops.forEach((op) => {
|
||||
@ -394,33 +441,40 @@ const editorOption = {
|
||||
placeholder: '',
|
||||
}
|
||||
|
||||
const handleSelectImg = (data) => {
|
||||
onSendMessage({ type: 'image', ...data })
|
||||
}
|
||||
|
||||
const virtualListChange = (vList) => {
|
||||
virtualList.value = vList
|
||||
}
|
||||
|
||||
|
||||
watch(() => records, (newValue, oldValue) => {
|
||||
console.log(newValue);
|
||||
watch(() => reversedRecords.value, (newValue, oldValue) => {
|
||||
zpagingRef.value?.complete(newValue)
|
||||
|
||||
}, { 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 = {
|
||||
uid: talkParams.uid,
|
||||
talk_type: talkParams.type,
|
||||
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 })
|
||||
}, { 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 })
|
||||
onLoad({ ...objT })
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
dialogueStore.setDialogue({})
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
uni-page-body,
|
||||
|
@ -9,16 +9,19 @@ const request = new Request({
|
||||
interceptors: {
|
||||
//实例的请求拦截器
|
||||
requestInterceptors: (config) => {
|
||||
config.headers['Content-Type'] = config.method === 'get' ?
|
||||
'application/x-www-form-urlencoded' :
|
||||
'application/json';
|
||||
config.headers['Authorization'] = token.value
|
||||
config.headers["Content-Type"] =
|
||||
config.method === "get"
|
||||
? "application/x-www-form-urlencoded"
|
||||
: config.contentType
|
||||
? config.contentType
|
||||
: "application/json";
|
||||
|
||||
config.headers['Authorization'] = token.value || '';
|
||||
if (config.isFormData) {
|
||||
config.headers['Content-Type'] = 'multipart/form-data';
|
||||
}
|
||||
return config;
|
||||
},
|
||||
//实例的响应拦截器
|
||||
responseInterceptors: async (res) => {
|
||||
if(res.data.status===1){
|
||||
message.warning(res.data.msg)
|
||||
|
@ -1,7 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import { createUniAppAxiosAdapter } from '@uni-helper/axios-adapter'
|
||||
axios.defaults.adapter = createUniAppAxiosAdapter()
|
||||
|
||||
class Request {
|
||||
// axios 实例
|
||||
instance;
|
||||
|
BIN
src/static/image/chatList/folder.png
Normal file
BIN
src/static/image/chatList/folder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
src/static/image/chatList/photoAlbum.png
Normal file
BIN
src/static/image/chatList/photoAlbum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
src/static/image/chatList/photoGraph.png
Normal file
BIN
src/static/image/chatList/photoGraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -4,7 +4,7 @@ import {uniStorage} from "@/utils/uniStorage.js"
|
||||
import {ref} from 'vue'
|
||||
export const useAuth = createGlobalState(() => {
|
||||
// const token = useStorage('token', '', uniStorage)
|
||||
const token = ref('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ffea38d3362de44912166520e87a6f38daaa6ef5faf4f643724f7a84be3e96d634f3c10379df86df00c605eb7b1120d2919e9b417e127bfe4828f3b56eb67ed1d62a401a9a4c065cf7b038001dd54637387e965bebe403133e408b3d4224f75952ee7682ff4e8904cce7d7c7e9766e958d253169bf05c6c5272dba34ff43ee1045')
|
||||
const token = ref('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ffea38d3362de44912166520e87a6f38dae8dda9ac1ca35393126a287143c4b6bf18216b911f8a76572c62563bf215ccfbf481b73f0bf6d5275d6ba21139798506790b4824a503c2fbbe8230a1118dd07583187e5c342dd8f8ca02f70dbd2dcea17ac3c5fbcf6e39893a63d2e79b34908949ce628eaede23e241afbcf5b83e01b9')
|
||||
const refreshToken = useStorage('refreshToken', '', uniStorage)
|
||||
const userInfo = useStorage('userInfo', {}, uniStorage)
|
||||
const leaderList = useStorage('leaderList', [], uniStorage)
|
||||
|
@ -5,4 +5,5 @@ export * from '@/store/modules/talk'
|
||||
export * from '@/store/modules/dialogue'
|
||||
export * from '@/store/modules/editor-draft'
|
||||
export * from '@/store/modules/uploads'
|
||||
export * from '@/store/modules/dialogueList'
|
||||
// export * from '@/store/modules/note'
|
||||
|
79
src/store/modules/dialogueList.js
Normal file
79
src/store/modules/dialogueList.js
Normal 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)
|
||||
// },
|
||||
}
|
||||
})
|
@ -82,3 +82,8 @@ export const convertBd09ToWgs84 = (bd_lon, bd_lat) => {
|
||||
latitude: wgs84[1],
|
||||
};
|
||||
}
|
||||
|
||||
export const uniqueId = () => 'id-' + new Date().getTime().toString(36) + '-' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user