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
4
env/.env.test
vendored
@ -6,8 +6,8 @@ VITE_SHOW_CONSOLE = true
|
|||||||
VITE_SHOW_SOURCEMAP = true
|
VITE_SHOW_SOURCEMAP = true
|
||||||
# baseUrl
|
# baseUrl
|
||||||
# VITE_BASEURL = 'https://warehouse.szjixun.cn/oa_backend'
|
# VITE_BASEURL = 'https://warehouse.szjixun.cn/oa_backend'
|
||||||
VITE_BASEURL = 'http://192.168.88.59:9503'
|
VITE_BASEURL = 'http://172.16.100.93:9503'
|
||||||
#VITE_SOCKET_API
|
#VITE_SOCKET_API
|
||||||
VITE_SOCKET_API = 'ws://192.168.88.59:9504'
|
VITE_SOCKET_API = 'ws://172.16.100.93:9504'
|
||||||
# EPRAPI baseUrl
|
# EPRAPI baseUrl
|
||||||
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
|
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { fileFormatSize } from '@/utils/strings'
|
import { fileFormatSize } from '@/utils/strings'
|
||||||
|
import { computed } from 'vue'
|
||||||
import { download, getFileNameSuffix } from '@/utils/functions'
|
import { download, getFileNameSuffix } from '@/utils/functions'
|
||||||
import { ITalkRecordExtraFile, ITalkRecord } from '@/types/chat'
|
import { ITalkRecordExtraFile, ITalkRecord } from '@/types/chat'
|
||||||
import filePaperPDF from '@/static/image/chat/file-paper-pdf.png'
|
import filePaperPDF from '@/static/image/chat/file-paper-pdf.png'
|
||||||
@ -13,46 +14,51 @@ import filePaperWordBlank from '@/static/image/chat/file-paper-word-blank.png'
|
|||||||
import filePaperExcelBlank from '@/static/image/chat/file-paper-excel-blank.png'
|
import filePaperExcelBlank from '@/static/image/chat/file-paper-excel-blank.png'
|
||||||
import filePaperOtherBlank from '@/static/image/chat/file-paper-other-blank.png'
|
import filePaperOtherBlank from '@/static/image/chat/file-paper-other-blank.png'
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
extra: ITalkRecordExtraFile
|
extra: ITalkRecordExtraFile
|
||||||
data: ITalkRecord
|
data: ITalkRecord
|
||||||
maxWidth?: Boolean
|
maxWidth?: Boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const getFileTypeIMG = (fileName: string) => {
|
const getFileTypeIMG = computed(() => {
|
||||||
const suffix = fileName.split('.').pop()?.toLowerCase() || ''
|
const suffix = props.extra.name.split('.').pop()?.toLowerCase() || ''
|
||||||
let objT = {
|
let objT = {
|
||||||
finishedImg: '',
|
finishedImg: '',
|
||||||
blankImg: ''
|
blankImg: '',
|
||||||
|
progressColor: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (suffix) {
|
switch (suffix) {
|
||||||
case 'pdf':
|
case 'pdf':
|
||||||
objT.finishedImg = filePaperPDF
|
objT.finishedImg = filePaperPDF
|
||||||
objT.blankImg = filePaperPDFBlank
|
objT.blankImg = filePaperPDFBlank
|
||||||
|
objT.progressColor = '#DE4E4E'
|
||||||
break;
|
break;
|
||||||
case 'doc':
|
case 'doc':
|
||||||
case 'docx':
|
case 'docx':
|
||||||
objT.finishedImg = filePaperWord
|
objT.finishedImg = filePaperWord
|
||||||
objT.blankImg = filePaperWordBlank
|
objT.blankImg = filePaperWordBlank
|
||||||
|
objT.progressColor = '#2750B2'
|
||||||
break;
|
break;
|
||||||
case 'xls':
|
case 'xls':
|
||||||
case 'xlsx':
|
case 'xlsx':
|
||||||
objT.finishedImg = filePaperExcel
|
objT.finishedImg = filePaperExcel
|
||||||
objT.blankImg = filePaperExcelBlank
|
objT.blankImg = filePaperExcelBlank
|
||||||
|
objT.progressColor = '#3C7F4B'
|
||||||
break;
|
break;
|
||||||
case 'ppt':
|
case 'ppt':
|
||||||
case 'pptx':
|
case 'pptx':
|
||||||
objT.finishedImg = filePaperPPT
|
objT.finishedImg = filePaperPPT
|
||||||
objT.blankImg = filePaperPPTBlank
|
objT.blankImg = filePaperPPTBlank
|
||||||
|
objT.progressColor = '#B74B2B'
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
objT.finishedImg = filePaperOther
|
objT.finishedImg = filePaperOther
|
||||||
objT.blankImg = filePaperOtherBlank
|
objT.blankImg = filePaperOtherBlank
|
||||||
|
objT.progressColor = '#747474'
|
||||||
}
|
}
|
||||||
return objT
|
return objT
|
||||||
}
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -63,12 +69,18 @@ const getFileTypeIMG = (fileName: string) => {
|
|||||||
{{ extra.name }}
|
{{ extra.name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="w-[95rpx]">
|
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="w-[95rpx]">
|
||||||
<tm-image :width="95" :height="95" :src="getFileTypeIMG(extra.name).finishedImg"></tm-image>
|
<tm-image :width="95" :height="95" :src="getFileTypeIMG.finishedImg"></tm-image>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="data.uploadStatus === 1 || data.uploadStatus === 3" class="w-[95rpx]">
|
<div v-if="data.uploadStatus === 1 || data.uploadStatus === 3" class="w-[95rpx]">
|
||||||
<tm-image :width="95" :height="95" :src="getFileTypeIMG(extra.name).blankImg"></tm-image>
|
<tm-image :width="95" :height="95" :src="getFileTypeIMG.blankImg"></tm-image>
|
||||||
<tm-progress class="circleProgress" linear="right" model="circle" bgColor="#E3E3E3" color="#DE4E4E" :height="3" :width="40"
|
<wd-circle
|
||||||
:percent="data.uploadCurrent"></tm-progress>
|
customClass="circleProgress"
|
||||||
|
:modelValue="data.uploadCurrent"
|
||||||
|
layerColor="#E3E3E3"
|
||||||
|
:color="getFileTypeIMG.progressColor"
|
||||||
|
:strokeWidth="3"
|
||||||
|
:size="20"
|
||||||
|
></wd-circle>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider mt-[28rpx]"></div>
|
<div class="divider mt-[28rpx]"></div>
|
||||||
@ -202,5 +214,7 @@ const getFileTypeIMG = (fileName: string) => {
|
|||||||
right: 52rpx;
|
right: 52rpx;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
width: 40rpx !important;
|
||||||
|
height: 40rpx !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getImageInfo } from '@/utils/functions'
|
import { getImageInfo } from '@/utils/functions'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
extra: any
|
extra: any
|
||||||
@ -7,19 +8,19 @@ const props = defineProps<{
|
|||||||
maxWidth?: Boolean
|
maxWidth?: Boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const img = (src: string, width = 200) => {
|
const img = computed(() => {
|
||||||
// console.log(props.extra);
|
// console.log(props.extra);
|
||||||
let info = {
|
let info = {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0
|
height: 0
|
||||||
}
|
}
|
||||||
if (src.includes('blob:http://')) {
|
if (props.extra.url.includes('blob:http://')) {
|
||||||
info = {
|
info = {
|
||||||
width: props.extra.width,
|
width: props.extra.width,
|
||||||
height: props.extra.height
|
height: props.extra.height
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
info = getImageInfo(src)
|
info = getImageInfo(props.extra.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.width == 0 || info.height == 0) {
|
if (info.width == 0 || info.height == 0) {
|
||||||
@ -35,7 +36,7 @@ const img = (src: string, width = 200) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.width < width) {
|
if (info.width < 350) {
|
||||||
return {
|
return {
|
||||||
width: info.width,
|
width: info.width,
|
||||||
height: info.height
|
height: info.height
|
||||||
@ -43,10 +44,10 @@ const img = (src: string, width = 200) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: width,
|
width: 350,
|
||||||
height: info.height / (info.width / width)
|
height: info.height / (info.width / 350)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<section
|
<section
|
||||||
@ -57,9 +58,9 @@ const img = (src: string, width = 200) => {
|
|||||||
<!-- <image
|
<!-- <image
|
||||||
:src="extra.url"
|
:src="extra.url"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
:width="img(extra.url,350).width" :height="img(extra.url,350).height"
|
:width="img.width" :height="img.height"
|
||||||
/> -->
|
/> -->
|
||||||
<tm-image preview :width="img(extra.url,350).width" :height="img(extra.url,350).height" :src="extra.url" />
|
<tm-image preview :width="img.width" :height="img.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>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, nextTick,getCurrentInstance } from 'vue'
|
import { ref, nextTick, getCurrentInstance, computed, onMounted } from 'vue'
|
||||||
import { getImageInfo } from '@/utils/functions'
|
import { getImageInfo } from '@/utils/functions'
|
||||||
import playCircle from "@/static/image/chatList/playCircle@2x.png";
|
import playCircle from "@/static/image/chatList/playCircle@2x.png";
|
||||||
import { useStatus } from "@/store/status";
|
import { useStatus } from "@/store/status";
|
||||||
@ -13,24 +13,38 @@ const props = defineProps<{
|
|||||||
maxWidth?: Boolean
|
maxWidth?: Boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const videoRef = ref()
|
const videoRef = ref({})
|
||||||
const videoContext = ref()
|
const videoContext = ref()
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
|
|
||||||
const img = (src: string, width = 200) => {
|
const img = computed(() => {
|
||||||
const info: any = getImageInfo(src)
|
let info = {
|
||||||
|
width: 0,
|
||||||
if (info.width == 0 || info.height == 0) {
|
height: 0
|
||||||
if (props.extra.width) {
|
}
|
||||||
return {
|
if (props.extra.url.includes('blob:http://')) {
|
||||||
|
info = {
|
||||||
width: props.extra.width,
|
width: props.extra.width,
|
||||||
height: props.extra.height
|
height: props.extra.height
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
return {}
|
info = getImageInfo(props.extra.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.width < width) {
|
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 < 350) {
|
||||||
return {
|
return {
|
||||||
width: info.width,
|
width: info.width,
|
||||||
height: info.height
|
height: info.height
|
||||||
@ -38,10 +52,10 @@ const img = (src: string, width = 200) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: width,
|
width: 350,
|
||||||
height: info.height / (info.width / width)
|
height: info.height / (info.width / 350)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const fullscreenchange = (e) => {
|
const fullscreenchange = (e) => {
|
||||||
if (!e.detail.fullScreen) {
|
if (!e.detail.fullScreen) {
|
||||||
@ -51,24 +65,63 @@ const fullscreenchange = (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 视频播放 获取第一帧 */
|
||||||
|
const canplay = (e) => {
|
||||||
|
console.log('Video can play:', e);
|
||||||
|
|
||||||
|
if (e.target) {
|
||||||
|
setTimeout(() => {
|
||||||
|
e.target.pause();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function onPlay() {
|
async function onPlay() {
|
||||||
videoContext.value = uni.createVideoContext(props.extra.url, instance);
|
videoContext.value = uni.createVideoContext(props.extra.url, instance);
|
||||||
videoContext.value.requestFullScreen({ direction: 2 });
|
videoContext.value.requestFullScreen({ direction: 2 });
|
||||||
videoContext.value.play()
|
videoContext.value.play()
|
||||||
open.value = true
|
open.value = true
|
||||||
}
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
videoRef.value = uni.createVideoContext(props.data.msg_id);
|
||||||
|
videoRef.value.play()
|
||||||
|
setTimeout(() => {
|
||||||
|
videoRef.value.pause()
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<section class="im-message-video" :class="{ left: data.float === 'left' }" @click="onPlay">
|
<section class="im-message-video" :class="{ left: data.float === 'left' }" @click="onPlay">
|
||||||
<wd-img :width="`${img(data.extra.cover, 350).width}rpx`" :height="`${img(data.extra.cover, 350).height}rpx`" :src="data.extra.cover" />
|
<div class="coverVideo" :style="{
|
||||||
|
width: img.width + 'rpx',
|
||||||
|
height: img.height + 'rpx'
|
||||||
|
}" v-if="props.extra.url.includes('blob:http://')">
|
||||||
|
<video :id="data.msg_id" :autoplay="false" disablepictureinpicture muted :src="props.extra.url" width="100%"
|
||||||
|
height="100%" playsinline preload="auto" controls="false" x5-playsinline
|
||||||
|
webkit-playsinline style="object-fit: cover; pointer-events: none;">
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
<wd-img v-else :width="`${img.width}rpx`" :height="`${img.height}rpx`" :src="data.extra.cover" />
|
||||||
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="btn-video" :style="{
|
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="btn-video" :style="{
|
||||||
width: img(data.extra.cover, 350).width + 'rpx',
|
width: img.width + 'rpx',
|
||||||
height: img(data.extra.cover, 350).height + 'rpx'
|
height: img.height + 'rpx'
|
||||||
}">
|
}">
|
||||||
<tm-image :src="playCircle" :width="80" :height="80" />
|
<tm-image :src="playCircle" :width="80" :height="80" />
|
||||||
</div>
|
</div>
|
||||||
<tm-progress
|
<div v-else class="btn-video" :style="{
|
||||||
v-if="props.data.uploadCurrent"
|
width: img.width + 'rpx',
|
||||||
|
height: img.height + 'rpx'
|
||||||
|
}" >
|
||||||
|
<wd-circle
|
||||||
|
v-model="props.data.uploadCurrent"
|
||||||
|
customClass="circleProgress"
|
||||||
|
layerColor="#E3E3E3"
|
||||||
|
color="#FFFFFF"
|
||||||
|
:strokeWidth="6"
|
||||||
|
:size="40"
|
||||||
|
></wd-circle>
|
||||||
|
<!-- <tm-progress
|
||||||
class="circleProgress"
|
class="circleProgress"
|
||||||
linear="right"
|
linear="right"
|
||||||
model="circle"
|
model="circle"
|
||||||
@ -77,10 +130,10 @@ async function onPlay() {
|
|||||||
:height="8"
|
:height="8"
|
||||||
:width="70"
|
:width="70"
|
||||||
:percent="props.data.uploadCurrent">
|
:percent="props.data.uploadCurrent">
|
||||||
</tm-progress>
|
</tm-progress> -->
|
||||||
|
</div>
|
||||||
<div v-show="open">
|
<div v-show="open">
|
||||||
<video ref="videoRef" :src="props.extra.url" controls @fullscreenchange="fullscreenchange"
|
<video :src="props.extra.url" controls @fullscreenchange="fullscreenchange" :id="props.extra.url">
|
||||||
:id="props.extra.url">
|
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -126,12 +179,31 @@ async function onPlay() {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.circleProgress {
|
// .circleProgress {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 50%;
|
// top: 50%;
|
||||||
left: 50%;
|
// left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
// transform: translate(-50%, -50%);
|
||||||
z-index: 1;
|
// z-index: 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.coverVideo {
|
||||||
|
:deep(uni-video) {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.uni-video-cover) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.uni-video-bar) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.circleProgress {
|
||||||
|
width: 80rpx !important;
|
||||||
|
height: 80rpx !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Base from './base'
|
import Base from './base'
|
||||||
import { useDialogueStore, useTalkStore } from '@/store'
|
import { useDialogueStore, useTalkStore } from '@/store'
|
||||||
import { parseTime } from '@/utils/datetime'
|
import { parseTime } from '@/utils/datetime'
|
||||||
|
import { useDialogueListStore } from '@/store'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 好友状态事件
|
* 好友状态事件
|
||||||
@ -67,21 +68,21 @@ class Revoke extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handle() {
|
handle() {
|
||||||
|
const {updateDialogueRecord} = useDialogueListStore()
|
||||||
useTalkStore().updateItem({
|
useTalkStore().updateItem({
|
||||||
index_name: this.getIndexName(),
|
index_name: this.getIndexName(),
|
||||||
msg_text: this.resource.text,
|
msg_text: this.resource.text,
|
||||||
updated_at: parseTime(new Date())
|
updated_at: parseTime(new Date())
|
||||||
})
|
})
|
||||||
|
|
||||||
// 判断当前是否正在和好友对话
|
|
||||||
if (!this.isTalk(this.talk_type, this.receiver_id, this.sender_id)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
useDialogueStore().updateDialogueRecord({
|
useDialogueStore().updateDialogueRecord({
|
||||||
msg_id: this.msg_id,
|
msg_id: this.msg_id,
|
||||||
is_revoke: 1
|
is_revoke: 1
|
||||||
})
|
})
|
||||||
|
updateDialogueRecord({
|
||||||
|
msg_id: this.msg_id,
|
||||||
|
is_revoke: 1
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ export const useTalkRecord = (uid) => {
|
|||||||
addDialogueRecord(params.direction=='down'?reverseItems:items,params.direction=='down'?'add':'unshift')
|
addDialogueRecord(params.direction=='down'?reverseItems:items,params.direction=='down'?'add':'unshift')
|
||||||
zpagingComplete(dialogueStore.index_name)
|
zpagingComplete(dialogueStore.index_name)
|
||||||
const dialogueList = getDialogueList(dialogueStore.index_name)
|
const dialogueList = getDialogueList(dialogueStore.index_name)
|
||||||
loadConfig.status = dialogueList.records[0].sequence > 1 ? 1 : 2
|
loadConfig.status = dialogueList.records?.[0]?.sequence > 1 ? 1 : 2
|
||||||
// loadConfig.cursor = data.cursor
|
// loadConfig.cursor = data.cursor
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@ -155,9 +155,9 @@ export const useTalkRecord = (uid) => {
|
|||||||
}else{
|
}else{
|
||||||
let filterList = dialogueList.records.filter(item=>item.sequence !== -1)
|
let filterList = dialogueList.records.filter(item=>item.sequence !== -1)
|
||||||
if(params.direction=='up'){
|
if(params.direction=='up'){
|
||||||
loadConfig.cursor = filterList[0].sequence
|
loadConfig.cursor = filterList?.[0]?.sequence
|
||||||
}else{
|
}else{
|
||||||
loadConfig.cursor = filterList[filterList.length-1].sequence
|
loadConfig.cursor = filterList?.[filterList.length-1]?.sequence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadConfig.receiver_id = params.receiver_id
|
loadConfig.receiver_id = params.receiver_id
|
||||||
|
@ -22,8 +22,14 @@
|
|||||||
"enablePullDownRefresh":false
|
"enablePullDownRefresh":false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/chooseChat/index",
|
||||||
|
"type": "page",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"enablePullDownRefresh":false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/login/index",
|
"path": "pages/login/index",
|
||||||
"type": "page",
|
"type": "page",
|
||||||
|
128
src/pages/chooseChat/components/chatItem.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div @click="cellClick" :class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']">
|
||||||
|
<div>
|
||||||
|
<tm-checkbox v-if="props.isMultiSelect" :round="10" :modelValue="props.data.isCheck"
|
||||||
|
@update:modelValue="props.data.isCheck = !props.data.isCheck" :size="42" :color="props.data.isCheck ? '#46299D' : '#BABABA'"></tm-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="avatarImg">
|
||||||
|
<tm-image :width="96" :height="96" :round="12" :src="props.data.avatar"></tm-image>
|
||||||
|
</div>
|
||||||
|
<div class="chatInfo">
|
||||||
|
<div class="chatInfo_1">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="text-[#000000] text-[32rpx] font-bold opacity-90">{{ props.data.name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="props.index !== talkStore.talkItems.length - 1" class="divider"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, defineProps, defineEmits } from "vue"
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { beautifyTime } from '@/utils/datetime'
|
||||||
|
import { ServeClearTalkUnreadNum } from '@/api/chat'
|
||||||
|
import { useTalkStore, useDialogueStore } from '@/store'
|
||||||
|
import { useSessionMenu } from '@/hooks'
|
||||||
|
|
||||||
|
const talkStore = useTalkStore()
|
||||||
|
const {
|
||||||
|
onToTopTalk
|
||||||
|
} = useSessionMenu()
|
||||||
|
const dialogueStore = useDialogueStore()
|
||||||
|
const emit = defineEmits(['itemClick'])
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isMultiSelect: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const cellClick = () => {
|
||||||
|
emit('itemClick', props.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.chatItem {
|
||||||
|
width: 100%;
|
||||||
|
height: 154rpx;
|
||||||
|
padding: 30rpx 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.isTop {
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarImg {
|
||||||
|
height: 96rpx;
|
||||||
|
width: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfo {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfo_1 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfo_2 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfo_2_1 {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
opacity: 40%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.companyTag {
|
||||||
|
width: 76rpx;
|
||||||
|
height: 38rpx;
|
||||||
|
border: 1px solid #7A58DE;
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
color: #7A58DE;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textEllipsis {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
background-color: rgba(243, 243, 243, 1);
|
||||||
|
height: 1rpx;
|
||||||
|
margin: 0 18rpx;
|
||||||
|
}
|
||||||
|
</style>
|
228
src/pages/chooseChat/index.vue
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<div class="outer-layer">
|
||||||
|
<div>
|
||||||
|
<tm-navbar :hideBack="true" hideHome :leftWidth="320">
|
||||||
|
<template v-slot:left>
|
||||||
|
<div @click="handleClose" class="ml-[48rpx] text-[34rpx] leading-[40rpx]">
|
||||||
|
关闭
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-slot:default>
|
||||||
|
<div class="text-[34rpx] font-bold ml-[-80rpx] leading-[40rpx]">
|
||||||
|
选择一个聊天
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-slot:right>
|
||||||
|
<div v-if="!isMultiSelect" @click="isMultiSelect = true" class="mr-[36rpx] text-[34rpx] leading-[40rpx]">
|
||||||
|
多选
|
||||||
|
</div>
|
||||||
|
<div v-else class="mr-[36rpx] btnBox">
|
||||||
|
<tm-button @click="handleFinish" color="#5B3FB1" :fontSize="34" :disabled="!selectItems.length"
|
||||||
|
:margin="[10]" :shadow="0" size="small" :width="selectItems.length ? 162 : 116"
|
||||||
|
:label="selectItems.length ? `完成(${selectItems.length})` : '完成'">
|
||||||
|
</tm-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tm-navbar>
|
||||||
|
</div>
|
||||||
|
<div class="root">
|
||||||
|
<div class="searchRoot">
|
||||||
|
<tm-input placeholder="请输入…" color="#F9F9FD" :round="1" prefix="tmicon-search" prefixColor="#46299D"></tm-input>
|
||||||
|
</div>
|
||||||
|
<div class="contentRoot">
|
||||||
|
<chatItem v-for="(item, index) in items" :key="item.index_name" :index="index" :data="item"
|
||||||
|
:isMultiSelect="isMultiSelect" @itemClick="itemClick" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<tm-modal v-model:show="showWin" :height="forwardModalHeight" :round="4" okColor="#FFFFFF" @ok="handleOk"
|
||||||
|
@cancel="handleCancel">
|
||||||
|
<template v-slot:title>
|
||||||
|
<div class="w-full pl-[30rpx] leading-[48rpx] mt-[30rpx] font-bold text-[32rpx]">
|
||||||
|
发送给:
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-slot:default>
|
||||||
|
<div class="pl-[22rpx] pr-[32rpx] pt-[10rpx] flex items-center justify-start flex-wrap">
|
||||||
|
<div v-if="selectItemsModal.length > 1" v-for="item in selectItemsModal"
|
||||||
|
class="w-1/4 mt-[20rpx] flex flex-col items-center justify-center">
|
||||||
|
<div class="w-[96rpx] h-[96rpx] rounded-[60rpx] flex-center">
|
||||||
|
<tm-image :width="96" :height="96" :round="12" :src="item.avatar"></tm-image>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[8rpx] text-[#666666] text-[24rpx] w-[94rpx] truncate">{{ item.name }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="w-full flex items-center justify-start">
|
||||||
|
<div class="w-[96rpx] h-[96rpx] rounded-[60rpx] flex-center">
|
||||||
|
<tm-image :width="96" :height="96" :round="12" :src="selectItemsModal[0].avatar"></tm-image>
|
||||||
|
</div>
|
||||||
|
<div class="ml-[16rpx] text-[#161616] text-[32rpx] font-bold ">{{ selectItemsModal[0].name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tm-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||||
|
import { useChatList } from "@/store/chatList/index.js";
|
||||||
|
import { useAuth } from "@/store/auth";
|
||||||
|
import { useTalkStore, useUserStore, useDialogueStore } from '@/store'
|
||||||
|
import chatItem from './components/chatItem.vue';
|
||||||
|
import addCircle from "@/static/image/chatList/addCircle.png";
|
||||||
|
import lodash from 'lodash'
|
||||||
|
|
||||||
|
const talkStore = useTalkStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { userInfo } = useAuth()
|
||||||
|
const dialogueStore = useDialogueStore()
|
||||||
|
|
||||||
|
const isMultiSelect = ref(false)
|
||||||
|
const showWin = ref(false)
|
||||||
|
const forwardModalHeight = ref(402)
|
||||||
|
|
||||||
|
const topItems = computed(() => talkStore.topItems)
|
||||||
|
const items = ref([])
|
||||||
|
// const items = computed(() => {
|
||||||
|
// return lodash.cloneDeep(talkStore.talkItems)
|
||||||
|
// })
|
||||||
|
|
||||||
|
const selectItems = computed(() => {
|
||||||
|
return items.value.filter(item => item.isCheck)
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectItemsModal = ref([])
|
||||||
|
|
||||||
|
const itemClick = (item) => {
|
||||||
|
console.log(item);
|
||||||
|
if (isMultiSelect.value) {
|
||||||
|
item.isCheck = !item.isCheck
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
selectItemsModal.value = [item]
|
||||||
|
forwardModalHeight.value = 402
|
||||||
|
showWin.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFinish = () => {
|
||||||
|
console.log(selectItems.value);
|
||||||
|
selectItemsModal.value = selectItems.value
|
||||||
|
if (selectItemsModal.value.length > 4) {
|
||||||
|
forwardModalHeight.value = 560
|
||||||
|
} else {
|
||||||
|
forwardModalHeight.value = 402
|
||||||
|
}
|
||||||
|
showWin.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
dialogueStore.setForwardType('')
|
||||||
|
dialogueStore.setForwardMessages([])
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
let msg_ids = dialogueStore.forwardMessages.map((item) => item.msg_id)
|
||||||
|
|
||||||
|
|
||||||
|
let user_ids = []
|
||||||
|
let group_ids = []
|
||||||
|
|
||||||
|
for (let o of selectItems.value) {
|
||||||
|
if (o.talk_type == 1) {
|
||||||
|
user_ids.push(o.receiver_id)
|
||||||
|
} else {
|
||||||
|
group_ids.push(o.receiver_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogueStore.ApiForwardRecord({
|
||||||
|
mode: dialogueStore.forwardType,
|
||||||
|
message_ids: msg_ids,
|
||||||
|
uids: user_ids,
|
||||||
|
gids: group_ids
|
||||||
|
})
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
console.log('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => talkStore, (newValue, oldValue) => {
|
||||||
|
console.log(talkStore);
|
||||||
|
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
talkStore.loadTalkList()
|
||||||
|
items.value = lodash.cloneDeep(talkStore.talkItems)
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
dialogueStore.setForwardType('')
|
||||||
|
dialogueStore.setForwardMessages([])
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
uni-page-body,
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer-layer {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||||
|
background-size: cover;
|
||||||
|
padding: 0 32rpx 20rpx 32rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchRoot {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 22rpx 18rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentRoot {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnBox {
|
||||||
|
:deep(.button) {
|
||||||
|
&[disabled="true"] {
|
||||||
|
background-color: #F3F3F3 !important;
|
||||||
|
color: #BABABA !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.noNvueBorder.round-bl-4) {
|
||||||
|
border-top: 1rpx solid #E7E7E7 !important;
|
||||||
|
background-color: #FFFFFF !important;
|
||||||
|
|
||||||
|
uni-text {
|
||||||
|
color: #1A1A1A !important;
|
||||||
|
font-size: 32rpx !important;
|
||||||
|
line-height: 48rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.noNvueBorder.round-br-4) {
|
||||||
|
border-top: 1rpx solid #E7E7E7 !important;
|
||||||
|
border-left: 1rpx solid #E7E7E7 !important;
|
||||||
|
background-color: #FFFFFF !important;
|
||||||
|
|
||||||
|
uni-text {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #46299D !important;
|
||||||
|
font-size: 32rpx !important;
|
||||||
|
line-height: 48rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,11 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="emojiRoot">
|
<div class="emojiRoot">
|
||||||
<div @click="choosePhoto" class="flex flex-col items-center">
|
<div @click="()=>photoActionsSelect(0)" class="flex flex-col items-center">
|
||||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||||
<tm-image :width="53" :height="44" :src="photoAlbum"></tm-image>
|
<tm-image :width="53" :height="44" :src="photoAlbum"></tm-image>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">照片</div>
|
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">照片</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div @click="()=>photoActionsSelect(1)" 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="videoImg"></tm-image>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">视频</div>
|
||||||
|
</div>
|
||||||
<div @click="takePhoto" class="flex flex-col items-center">
|
<div @click="takePhoto" class="flex flex-col items-center">
|
||||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||||
<tm-image :width="53" :height="44" :src="photoGraph"></tm-image>
|
<tm-image :width="53" :height="44" :src="photoGraph"></tm-image>
|
||||||
@ -24,37 +30,68 @@
|
|||||||
import { ref, reactive, defineProps, defineEmits } from "vue"
|
import { ref, reactive, defineProps, defineEmits } from "vue"
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { beautifyTime } from '@/utils/datetime'
|
import { beautifyTime } from '@/utils/datetime'
|
||||||
import { useDialogueListStore, useDialogueStore, useUserStore } from '@/store'
|
import { useDialogueListStore, useDialogueStore, useUserStore,useUploadsStore } from '@/store'
|
||||||
import { useSessionMenu } from '@/hooks'
|
import { useSessionMenu } from '@/hooks'
|
||||||
import photoAlbum from '@/static/image/chatList/photoAlbum.png'
|
import photoAlbum from '@/static/image/chatList/photoAlbum.png'
|
||||||
import photoGraph from '@/static/image/chatList/photoGraph.png'
|
import photoGraph from '@/static/image/chatList/photoGraph.png'
|
||||||
|
import videoImg from '@/static/image/chatList/video@2x.png'
|
||||||
import folder from '@/static/image/chatList/folder.png'
|
import folder from '@/static/image/chatList/folder.png'
|
||||||
import { uploadImg } from '@/api/chat'
|
import { uploadImg } from '@/api/chat'
|
||||||
import { uniqueId } from '@/utils'
|
import { uniqueId } from '@/utils'
|
||||||
import { getFileFromUrl } from '@/utils'
|
|
||||||
import { getVideoImage } from '@/utils/common'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
sendUserInfo: {
|
sendUserInfo: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
talkParams: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const uploadsStore = useUploadsStore()
|
||||||
const { addDialogueRecord, virtualList, updateUploadProgress } = useDialogueListStore()
|
const { addDialogueRecord, virtualList, updateUploadProgress } = useDialogueListStore()
|
||||||
const dialogueStore = useDialogueStore()
|
const dialogueStore = useDialogueStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const emit = defineEmits(['selectImg'])
|
const emit = defineEmits(['selectImg'])
|
||||||
|
|
||||||
const onProgressFn = (progress, file, id) => {
|
const onProgressFn = (progress, id) => {
|
||||||
console.log(progress.loaded / progress.total * 100, 'progress');
|
console.log(progress.loaded / progress.total * 100, 'progress');
|
||||||
|
|
||||||
updateUploadProgress(id, progress.loaded / progress.total * 100)
|
updateUploadProgress(id, progress.loaded / progress.total * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUploadImageVideo = async (file, type = 'image') => {
|
const photoActionsSelect = (index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
uni.chooseImage({
|
||||||
|
sourceType: ['album'],
|
||||||
|
count: 9,
|
||||||
|
success: async (res) => {
|
||||||
|
console.log(res,'res');
|
||||||
|
res.tempFiles.forEach(async (file) => {
|
||||||
|
let data = await onUploadImageVideo(file, 'image')
|
||||||
|
emit('selectImg', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
uni.chooseVideo({
|
||||||
|
sourceType: ['album'],
|
||||||
|
success: async (res) => {
|
||||||
|
console.log(res,'res');
|
||||||
|
let data = await onUploadImageVideo(res.tempFile, 'video',res.tempFilePath)
|
||||||
|
emit('selectImg', data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUploadImageVideo = async (file, type = 'image',fileUrl) => {
|
||||||
console.log(file, 'file');
|
console.log(file, 'file');
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
if (type === 'image') {
|
if (type === 'image') {
|
||||||
@ -93,7 +130,7 @@ const onUploadImageVideo = async (file, type = 'image') => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtualList.value.unshift(newItem)
|
virtualList.value.unshift(newItem)
|
||||||
uploadImg(form, (e, file) => onProgressFn(e, file, randomId)).then(({ status, data, msg }) => {
|
uploadImg(form, (e) => onProgressFn(e, randomId)).then(({ status, data, msg }) => {
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
resolve({
|
resolve({
|
||||||
type: 'image',
|
type: 'image',
|
||||||
@ -109,8 +146,10 @@ const onUploadImageVideo = async (file, type = 'image') => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let resp = await getVideoImage(file)
|
uni.getVideoInfo({
|
||||||
const form = new FormData()
|
src:fileUrl,
|
||||||
|
success:(resp)=>{
|
||||||
|
console.log(resp);
|
||||||
form.append('file', file)
|
form.append('file', file)
|
||||||
form.append("source", "fonchain-chat");
|
form.append("source", "fonchain-chat");
|
||||||
form.append("type", "video");
|
form.append("type", "video");
|
||||||
@ -123,8 +162,7 @@ const onUploadImageVideo = async (file, type = 'image') => {
|
|||||||
duration: parseInt(resp.duration),
|
duration: parseInt(resp.duration),
|
||||||
height: resp.height,
|
height: resp.height,
|
||||||
name: "",
|
name: "",
|
||||||
url: '',
|
url: fileUrl,
|
||||||
cover: resp.url,
|
|
||||||
width: resp.width
|
width: resp.width
|
||||||
},
|
},
|
||||||
float: "right",
|
float: "right",
|
||||||
@ -143,7 +181,7 @@ const onUploadImageVideo = async (file, type = 'image') => {
|
|||||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||||
}
|
}
|
||||||
virtualList.value.unshift(newItem)
|
virtualList.value.unshift(newItem)
|
||||||
uploadImg(form, (e, file) => onProgressFn(e, file, randomId)).then(({ status, data, msg }) => {
|
uploadImg(form, (e) => onProgressFn(e, randomId)).then(({ status, data, msg }) => {
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
resolve({
|
resolve({
|
||||||
@ -160,6 +198,9 @@ const onUploadImageVideo = async (file, type = 'image') => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const form = new FormData()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const base64ToFile = (base64) => {
|
const base64ToFile = (base64) => {
|
||||||
@ -177,9 +218,10 @@ const base64ToFile = (base64) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const choosePhoto = () => {
|
const choosePhoto = (filter = 'none', maximum = 9, multiple = true) => {
|
||||||
window.plus?.gallery.pick((res) => {
|
window.plus?.gallery.pick((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
|
res.files.reverse()
|
||||||
res.files.forEach(async (filePath) => {
|
res.files.forEach(async (filePath) => {
|
||||||
const suffix = filePath.split('.').pop()?.toLowerCase() || ''
|
const suffix = filePath.split('.').pop()?.toLowerCase() || ''
|
||||||
if (['jpg', 'png'].includes(suffix)) {
|
if (['jpg', 'png'].includes(suffix)) {
|
||||||
@ -203,47 +245,87 @@ const choosePhoto = () => {
|
|||||||
}
|
}
|
||||||
if (['mp4', 'flv'].includes(suffix)) {
|
if (['mp4', 'flv'].includes(suffix)) {
|
||||||
console.log(filePath,"进入视频")
|
console.log(filePath,"进入视频")
|
||||||
window.plus?.io?.resolveLocalFileSystemURL(filePath, async (entry) => {
|
// const localUrl = plus.io.convertLocalFileSystemURL(filePath)
|
||||||
entry.file((file) => {
|
// console.log(localUrl);
|
||||||
console.log(file,'file');
|
|
||||||
const fileReader = new plus.io.FileReader();
|
plus.io.getVideoInfo({
|
||||||
fileReader.readAsDataURL(file);
|
filePath:filePath,
|
||||||
fileReader.onloadend = async (e) => {
|
success:(event)=>{
|
||||||
const base64Url = e.target.result;
|
console.log(event);
|
||||||
const fileObj = base64ToFile(base64Url);
|
|
||||||
let data = await onUploadImageVideo(fileObj, 'video')
|
|
||||||
emit('selectImg', data)
|
|
||||||
};
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
(err) => {
|
fail:(err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
)
|
});
|
||||||
|
// window.plus?.io?.resolveLocalFileSystemURL(localUrl, async (entry) => {
|
||||||
|
// entry.file((file) => {
|
||||||
|
// console.log(file,'file');
|
||||||
|
// const fileReader = new plus.io.FileReader();
|
||||||
|
// fileReader.readAsDataURL(file);
|
||||||
|
// fileReader.onloadend = async (e) => {
|
||||||
|
// const base64Url = e.target.result;
|
||||||
|
// const fileObj = base64ToFile(base64Url);
|
||||||
|
// let data = await onUploadImageVideo(fileObj, 'video')
|
||||||
|
// emit('selectImg', data)
|
||||||
|
// };
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// (err) => {
|
||||||
|
// console.log(err);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filter: 'none',
|
filter: filter,
|
||||||
maximum: 9,
|
maximum: maximum,
|
||||||
multiple: true,
|
multiple: multiple,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const takePhoto = () => {
|
const takePhoto = () => {
|
||||||
|
|
||||||
}
|
}
|
||||||
const chooseFile = () => {
|
const chooseFile = () => {
|
||||||
window.plus?.io.requestFileSystem(
|
uni.chooseFile({
|
||||||
3,
|
count: 1,
|
||||||
(res) => {
|
extension:[''],
|
||||||
console.log(res);
|
success: (res) => {
|
||||||
}, (err) => {
|
let randomId = uniqueId()
|
||||||
console.log(err);
|
let newItem = {
|
||||||
|
avatar: userStore.avatar,
|
||||||
|
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
extra: {
|
||||||
|
drive: 3,
|
||||||
|
name: res.tempFiles[0].name,
|
||||||
|
size: res.tempFiles[0].size,
|
||||||
|
path: res.tempFilePaths[0],
|
||||||
},
|
},
|
||||||
)
|
float: "right",
|
||||||
|
isCheck: false,
|
||||||
|
is_mark: 0,
|
||||||
|
is_read: 0,
|
||||||
|
is_revoke: 0,
|
||||||
|
msg_id: randomId,
|
||||||
|
msg_type: 6,
|
||||||
|
nickname: userStore.nickname,
|
||||||
|
receiver_id: dialogueStore.talk.receiver_id,
|
||||||
|
sequence: -1,
|
||||||
|
talk_type: dialogueStore.talk.talk_type,
|
||||||
|
user_id: userStore.uid,
|
||||||
|
uploadCurrent: 0,
|
||||||
|
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||||
|
}
|
||||||
|
virtualList.value.unshift(newItem)
|
||||||
|
uploadsStore.initUploadFile(res.tempFiles[0], props.talkParams,randomId)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -253,7 +335,7 @@ const chooseFile = () => {
|
|||||||
.emojiRoot {
|
.emojiRoot {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 232rpx;
|
height: 232rpx;
|
||||||
padding: 20rpx 130rpx 0 130rpx;
|
padding: 20rpx 62rpx 0 62rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@ -15,23 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="dialogBox">
|
<div class="dialogBox">
|
||||||
<ZPaging
|
<ZPaging :fixed="false" use-chat-record-mode :use-page-scroll="false" :refresher-enabled="false"
|
||||||
:fixed="false"
|
:show-scrollbar="false" :loading-more-enabled="false" :hide-empty-view="true" height="100%" ref="zpagingRef"
|
||||||
use-chat-record-mode
|
:use-virtual-list="true" :preload-page="1" cell-height-mode="dynamic"
|
||||||
:use-page-scroll="false"
|
:loading-more-custom-style="{ display: 'none', height: '0' }" @virtualListChange="virtualListChange"
|
||||||
:refresher-enabled="false"
|
@scrolltolower="onRefreshLoad">
|
||||||
: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>
|
<!-- <template #top>
|
||||||
<div class="load-toolbar pointer">
|
<div class="load-toolbar pointer">
|
||||||
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
||||||
@ -41,13 +29,8 @@
|
|||||||
</template> -->
|
</template> -->
|
||||||
|
|
||||||
<!-- 数据加载状态栏 -->
|
<!-- 数据加载状态栏 -->
|
||||||
<div
|
<div class="message-item" v-for="item in virtualList" :id="`zp-id-${item.zp_index}`" :key="item.zp_index"
|
||||||
class="message-item"
|
style="transform: scaleY(-1);">
|
||||||
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" />
|
||||||
@ -64,9 +47,11 @@
|
|||||||
'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> -->
|
<tm-checkbox :round="10" :defaultChecked="item.isCheck"
|
||||||
|
@update:modelValue="item.isCheck = !item.isCheck" :size="42" color="#46299D"></tm-checkbox>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<!-- 头像信息 -->
|
<!-- 头像信息 -->
|
||||||
<aside class="avatar-column">
|
<aside class="avatar-column">
|
||||||
@ -85,13 +70,38 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="talk-content" :class="{ pointer: dialogueStore.isOpenMultiSelect }">
|
<div class="talk-content" :class="{ pointer: dialogueStore.isOpenMultiSelect }">
|
||||||
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra"
|
<tm-popover :position="item.float == 'left' ? 'tl' : 'tr'" :width="526" color="#333333">
|
||||||
:data="item" :max-width="true" :source="'panel'"
|
<component :key="item.zp_index" :is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||||
@contextmenu.prevent="onContextMenu($event, item)" />
|
:extra="item.extra" :data="item" :max-width="true" :source="'panel'"
|
||||||
|
@click="onContextMenu(item)" />
|
||||||
|
<template v-slot:label>
|
||||||
|
<div class="w-full h-[132rpx] text-[24rpx] text-[#FFFFFF] flex items-center justify-around">
|
||||||
|
<div @click="() => actionCopy(item)" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="40" :height="40" :src="copy07"></tm-image>
|
||||||
|
<div>复制</div>
|
||||||
|
</div>
|
||||||
|
<div @click="() => multipleChoose(item)" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="40" :height="40" :src="multipleChoices"></tm-image>
|
||||||
|
<div>多选</div>
|
||||||
|
</div>
|
||||||
|
<div @click="() => actionCite(item)" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="40" :height="40" :src="cite"></tm-image>
|
||||||
|
<div>引用</div>
|
||||||
|
</div>
|
||||||
|
<div @click="() => actionWithdraw(item)" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="40" :height="40" :src="withdraw"></tm-image>
|
||||||
|
<div>撤回</div>
|
||||||
|
</div>
|
||||||
|
<div @click="() => actionDelete(item)" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="40" :height="40" :src="delete07"></tm-image>
|
||||||
|
<div>删除</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</tm-popover>
|
||||||
<!-- <div class="talk-tools">
|
<!-- <div class="talk-tools">
|
||||||
<template v-if="talkParams.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" :sxtrokeWidth="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>
|
||||||
@ -123,6 +133,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footBox">
|
<div class="footBox">
|
||||||
|
<div v-if="!dialogueStore.isOpenMultiSelect">
|
||||||
<div class="mt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between">
|
<div class="mt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between">
|
||||||
<div class="flex-1 quillBox">
|
<div class="flex-1 quillBox">
|
||||||
<QuillEditor ref="editor" id="editor" :options="editorOption" @editorChange="onEditorChange"
|
<QuillEditor ref="editor" id="editor" :options="editorOption" @editorChange="onEditorChange"
|
||||||
@ -132,23 +143,51 @@
|
|||||||
</div>
|
</div>
|
||||||
<tm-image :margin="[10, 0]" @click="handleEmojiPanel" :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 @click="handleFilePanel" :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"
|
||||||
<tm-button @click="onSendMessageClick" :margin="[0,0]" :padding="[0,30]" color="#46299D" :fontSize="28" size="mini" :shadow="0" label="发送"></tm-button>
|
: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>
|
||||||
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
|
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
|
||||||
<emojiPanel @on-select="onEmoticonEvent" />
|
<emojiPanel @on-select="onEmoticonEvent" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="state.isOpenFilePanel" class="mt-[16rpx]">
|
<div v-if="state.isOpenFilePanel" class="mt-[16rpx]">
|
||||||
<filePanel @selectImg="handleSelectImg" />
|
<filePanel @selectImg="handleSelectImg" :talkParams="talkParams" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="h-[232rpx]">
|
||||||
|
<div class="flex items-center justify-center mt-[12rpx] text-[24rpx] text-[#747474] leading-[44rpx]">
|
||||||
|
<div class="mr-[8rpx]">已选中:</div>
|
||||||
|
<div>{{ selectedMessage.length }}条消息</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-around pl-[128rpx] pr-[128rpx] mt-[18rpx] text-[20rpx] text-[#737373] ">
|
||||||
|
<div @click="handleMergeForward" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="68" :height="68" :src="zu6050"></tm-image>
|
||||||
|
<div class="mt-[6rpx]">合并转发</div>
|
||||||
|
</div>
|
||||||
|
<div @click="handleSingleForward" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="68" :height="68" :src="zu6051"></tm-image>
|
||||||
|
<div class="mt-[6rpx]">逐条转发</div>
|
||||||
|
</div>
|
||||||
|
<div @click="handleWechatForward" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="68" :height="68" :src="zu6052"></tm-image>
|
||||||
|
<div class="mt-[6rpx]">微信</div>
|
||||||
|
</div>
|
||||||
|
<div @click="handleDelete" class="flex flex-col items-center justify-center">
|
||||||
|
<tm-image :width="68" :height="68" :src="zu6053"></tm-image>
|
||||||
|
<div class="mt-[6rpx]">删除</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!--底部安全区-->
|
<!--底部安全区-->
|
||||||
<div class="content-placeholder"></div>
|
<div class="content-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, watch, computed, onMounted,onUnmounted } from 'vue';
|
import { ref, reactive, watch, computed, onMounted, onUnmounted, nextTick } 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";
|
||||||
@ -168,6 +207,16 @@ import emojiPanel from './components/emojiPanel.vue'
|
|||||||
import filePanel from './components/filePanel.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 copy07 from "@/static/image/chatList/copy07@2x.png"
|
||||||
|
import multipleChoices from "@/static/image/chatList/multipleChoices@2x.png"
|
||||||
|
import cite from "@/static/image/chatList/cite@2x.png"
|
||||||
|
import withdraw from "@/static/image/chatList/withdraw@2x.png"
|
||||||
|
import delete07 from "@/static/image/chatList/delete@2x.png"
|
||||||
|
import zu6050 from "@/static/image/chatList/zu6050@2x.png"
|
||||||
|
import zu6051 from "@/static/image/chatList/zu6051@2x.png"
|
||||||
|
import zu6052 from "@/static/image/chatList/zu6052@2x.png"
|
||||||
|
import zu6053 from "@/static/image/chatList/zu6053@2x.png"
|
||||||
|
|
||||||
|
|
||||||
Quill.register('formats/emoji', EmojiBlot)
|
Quill.register('formats/emoji', EmojiBlot)
|
||||||
|
|
||||||
@ -194,7 +243,7 @@ const talkParams = reactive({
|
|||||||
|
|
||||||
const state = ref({
|
const state = ref({
|
||||||
isOpenEmojiPanel: false,
|
isOpenEmojiPanel: false,
|
||||||
isOpenFilePanel: false
|
isOpenFilePanel: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleEmojiPanel = () => {
|
const handleEmojiPanel = () => {
|
||||||
@ -241,10 +290,12 @@ const onSendMessageClick = () => {
|
|||||||
if (data.items[0].content.length > 1024) {
|
if (data.items[0].content.length > 1024) {
|
||||||
return message.info('发送内容超长,请分条发送')
|
return message.info('发送内容超长,请分条发送')
|
||||||
}
|
}
|
||||||
onSendTextEvent({data,callBack:(ok) => {
|
onSendTextEvent({
|
||||||
|
data, callBack: (ok) => {
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
getQuill().setContents([], Quill.sources.USER)
|
getQuill().setContents([], Quill.sources.USER)
|
||||||
}})
|
}
|
||||||
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,6 +349,10 @@ const getQuill = () => {
|
|||||||
return editor.value?.getQuill()
|
return editor.value?.getQuill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedMessage = computed(() => {
|
||||||
|
return virtualList.value.filter(item => item.isCheck)
|
||||||
|
})
|
||||||
|
|
||||||
// 编辑器事件
|
// 编辑器事件
|
||||||
const onEditorEvent = (msg) => {
|
const onEditorEvent = (msg) => {
|
||||||
evnets[msg.event] && evnets[msg.event](msg)
|
evnets[msg.event] && evnets[msg.event](msg)
|
||||||
@ -433,6 +488,85 @@ const virtualListChange = (vList) => {
|
|||||||
virtualList.value = vList
|
virtualList.value = vList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onContextMenu = (item) => {
|
||||||
|
console.log(item, 'item');
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionCopy = (item) => {
|
||||||
|
console.log('复制');
|
||||||
|
let content = ''
|
||||||
|
switch (item.msg_type) {
|
||||||
|
case 1:
|
||||||
|
content = item.extra.content
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
content = item.extra.url
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
content = item.extra.url
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: content,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const multipleChoose = (item) => {
|
||||||
|
item.isCheck = true
|
||||||
|
dialogueStore.setMultiSelect(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionCite = () => {
|
||||||
|
console.log('引用');
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionWithdraw = () => {
|
||||||
|
console.log('撤回');
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionDelete = () => {
|
||||||
|
console.log('删除');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMergeForward = () => {
|
||||||
|
if (selectedMessage.value.length == 0) {
|
||||||
|
return message.warning('未选择消息')
|
||||||
|
}
|
||||||
|
console.log('合并转发');
|
||||||
|
dialogueStore.setForwardType(2)
|
||||||
|
dialogueStore.setForwardMessages(selectedMessage.value)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/chooseChat/index',
|
||||||
|
success: function (res) {
|
||||||
|
clearMultiSelect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSingleForward = () => {
|
||||||
|
if (selectedMessage.value.length == 0) {
|
||||||
|
return message.warning('未选择消息')
|
||||||
|
}
|
||||||
|
console.log('逐条转发');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWechatForward = () => {
|
||||||
|
if (selectedMessage.value.length == 0) {
|
||||||
|
return message.warning('未选择消息')
|
||||||
|
}
|
||||||
|
console.log('微信转发');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (selectedMessage.value.length == 0) {
|
||||||
|
return message.warning('未选择消息')
|
||||||
|
}
|
||||||
|
console.log('删除');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
watch(() => zpagingRef.value, (newValue, oldValue) => {
|
watch(() => zpagingRef.value, (newValue, oldValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
@ -440,6 +574,13 @@ watch(() => zpagingRef.value, (newValue, oldValue) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const clearMultiSelect = () => {
|
||||||
|
dialogueStore.setMultiSelect(false)
|
||||||
|
virtualList.value.forEach(item => {
|
||||||
|
item.isCheck = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
const dialogueList = getDialogueList(talkParams.index_name)
|
const dialogueList = getDialogueList(talkParams.index_name)
|
||||||
|
|
||||||
@ -461,6 +602,7 @@ onMounted(async() => {
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
dialogueStore.setDialogue({})
|
dialogueStore.setDialogue({})
|
||||||
|
clearMultiSelect()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@ -547,10 +689,11 @@ page {
|
|||||||
.checkbox-column {
|
.checkbox-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 35rpx;
|
width: 42rpx;
|
||||||
order: 1;
|
order: 1;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
padding-top: 12rpx;
|
margin-top: 20rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-column {
|
.avatar-column {
|
||||||
@ -569,7 +712,7 @@ page {
|
|||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16rpx 20rpx 14rpx 20rpx;
|
padding: 16rpx 20rpx 14rpx 20rpx;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
|
|
||||||
.talk-title {
|
.talk-title {
|
||||||
@ -730,6 +873,7 @@ page {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
line-height: 44rpx;
|
line-height: 44rpx;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -737,6 +881,7 @@ page {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
.ed-emoji {
|
.ed-emoji {
|
||||||
width: 44rpx;
|
width: 44rpx;
|
||||||
height: 44rpx;
|
height: 44rpx;
|
||||||
@ -745,4 +890,12 @@ page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.wd-action-sheet) {
|
||||||
|
background-color: #8b8b8b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.wd-action-sheet__panel-title) {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div @click="cellClick" :class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']">
|
<div @click="cellClick" :class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']">
|
||||||
<div class="avatarImg">
|
<div class="avatarImg">
|
||||||
<tm-badge :count="props.data.unread_num" :maxCount="99" color="#D03050">
|
<tm-badge :count="props.data.unread_num" :maxCount="99" color="#D03050">
|
||||||
<tm-image preview :width="96" :height="96" :round="12" :src="props.data.avatar"></tm-image>
|
<tm-image :width="96" :height="96" :round="12" :src="props.data.avatar"></tm-image>
|
||||||
</tm-badge>
|
</tm-badge>
|
||||||
</div>
|
</div>
|
||||||
<div class="chatInfo">
|
<div class="chatInfo">
|
||||||
|
BIN
src/static/image/chatList/cite@2x.png
Normal file
After Width: | Height: | Size: 804 B |
BIN
src/static/image/chatList/copy07@2x.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
src/static/image/chatList/delete@2x.png
Normal file
After Width: | Height: | Size: 572 B |
BIN
src/static/image/chatList/multipleChoices@2x.png
Normal file
After Width: | Height: | Size: 468 B |
BIN
src/static/image/chatList/video@2x.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
src/static/image/chatList/withdraw@2x.png
Normal file
After Width: | Height: | Size: 779 B |
BIN
src/static/image/chatList/zu6050@2x.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/image/chatList/zu6051@2x.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/image/chatList/zu6052@2x.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
src/static/image/chatList/zu6053@2x.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
@ -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('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ff6a0563074f3084b41c1bc8dc0733d06bfbb433a8d5a1d13eb6227adbf50a5da566a4cacdbf91899e563f10864fe2acfe8edb3a186766d1fb247c2e3c6bb3c014a610243d87b0460c692d5bea2442fe553a9cf98e29ff7be27c364ab2047c67957e28df8f7cdd90366fd8f94967d5f8e037fab5f9e3c3a342f4830875841d5a4361ab087bd4205321e6ad9db918811786bd9e1be7df913383eef6f47f436073ca')
|
const token = ref('30119d9978a6f3321fb4779c0040e997df4dd0dd0cf6b71119657617d2249ed783f940b0050d5be7e758740ea467afdf3eeb4d28fb5ea234af60ebe51fb218ff6a0563074f3084b41c1bc8dc0733d06bfbb433a8d5a1d13eb6227adbf50a5da566a4cacdbf91899e563f10864fe2acfeee36e90ceb1aa03bbcca63f1bf5514d416827d0ea543bd8e02552fbd612d801b4b827977f6fe7d6201838456049083706a6e48004fc0ad99eddea3a8875f6583e959fd172a7cfb40f877bc0741a259520e9b6f524d04ee175bdc99c32eef4ef52c1ed38ed1034df8695c62ff44933644')
|
||||||
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)
|
||||||
|
@ -56,7 +56,11 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
read_sequence: 0, // 当前已读的最后一条记录
|
read_sequence: 0, // 当前已读的最后一条记录
|
||||||
records: []
|
records: []
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
// 转发消息类型
|
||||||
|
forwardType: 1,
|
||||||
|
// 合并转发消息
|
||||||
|
forwardMessages: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@ -164,6 +168,11 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 设置多选模式
|
||||||
|
setMultiSelect(value) {
|
||||||
|
this.isOpenMultiSelect = value
|
||||||
|
},
|
||||||
|
|
||||||
// 关闭多选模式
|
// 关闭多选模式
|
||||||
closeMultiSelect() {
|
closeMultiSelect() {
|
||||||
this.isOpenMultiSelect = false
|
this.isOpenMultiSelect = false
|
||||||
@ -214,7 +223,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
|
|
||||||
ServePublishMessage(data).then((res) => {
|
ServePublishMessage(data).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
this.closeMultiSelect()
|
// this.closeMultiSelect()
|
||||||
|
message.success('转发成功')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -226,6 +236,14 @@ export const useDialogueStore = defineStore('dialogue', {
|
|||||||
useEditorStore().loadUserEmoticon()
|
useEditorStore().loadUserEmoticon()
|
||||||
message && message.success('收藏成功')
|
message && message.success('收藏成功')
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
// 设置转发消息类型
|
||||||
|
setForwardType(type) {
|
||||||
|
this.forwardType = type
|
||||||
|
},
|
||||||
|
// 设置合并转发消息
|
||||||
|
setForwardMessages(messages) {
|
||||||
|
this.forwardMessages = messages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -52,6 +52,25 @@ export const useDialogueListStore = createGlobalState(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateDialogueRecord=(record)=>{
|
||||||
|
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||||
|
const item = getDialogueList(dialogue.index_name)
|
||||||
|
const recordIndex = item.records.findIndex(item=>item.msg_id===record.msg_id)
|
||||||
|
if(recordIndex!==-1){
|
||||||
|
item.records[recordIndex] = {
|
||||||
|
...item.records[recordIndex],
|
||||||
|
...record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const virtualIndex = virtualList.value.findIndex(item=>item.msg_id===record.msg_id)
|
||||||
|
if(virtualIndex!==-1){
|
||||||
|
virtualList.value[virtualIndex] = {
|
||||||
|
...virtualList.value[virtualIndex],
|
||||||
|
...record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteDialogueRecord=(record)=>{
|
const deleteDialogueRecord=(record)=>{
|
||||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||||
const item = getDialogueList(dialogue.index_name)
|
const item = getDialogueList(dialogue.index_name)
|
||||||
@ -89,11 +108,12 @@ export const useDialogueListStore = createGlobalState(() => {
|
|||||||
zpagingRef,
|
zpagingRef,
|
||||||
getDialogueList,
|
getDialogueList,
|
||||||
addDialogueRecord,
|
addDialogueRecord,
|
||||||
|
updateDialogueRecord,
|
||||||
deleteDialogueRecord,
|
deleteDialogueRecord,
|
||||||
updateUploadProgress,
|
updateUploadProgress,
|
||||||
updateZpagingRef,
|
updateZpagingRef,
|
||||||
zpagingComplete,
|
zpagingComplete,
|
||||||
addChatRecord,
|
addChatRecord,
|
||||||
virtualList
|
virtualList,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
|
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
|
||||||
import { ServeSendTalkFile } from '@/api/chat'
|
import { ServeSendTalkFile } from '@/api/chat'
|
||||||
|
import { useDialogueListStore } from '@/store/modules/dialogueList'
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const message = window.$message
|
const message = window.$message
|
||||||
|
const {updateUploadProgress} = useDialogueListStore()
|
||||||
|
|
||||||
// 处理拆分上传文件
|
// 处理拆分上传文件
|
||||||
function fileSlice(file: File, uploadId: string, eachSize: number) {
|
function fileSlice(file: File, uploadId: string, eachSize: number) {
|
||||||
@ -47,7 +49,8 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 初始化上传
|
// 初始化上传
|
||||||
initUploadFile(file: File, talkType: number, receiverId: number, username: string) {
|
initUploadFile(file: File, talkParams: any, msgId: string) {
|
||||||
|
const {type:talkType,receiver_id:receiverId,username} = talkParams;
|
||||||
ServeFindFileSplitInfo({
|
ServeFindFileSplitInfo({
|
||||||
file_name: file.name,
|
file_name: file.name,
|
||||||
file_size: file.size
|
file_size: file.size
|
||||||
@ -69,7 +72,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
username: username
|
username: username
|
||||||
})
|
})
|
||||||
|
|
||||||
this.triggerUpload(upload_id)
|
this.triggerUpload(upload_id,msgId)
|
||||||
this.isShow = true
|
this.isShow = true
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message)
|
message.error(res.message)
|
||||||
@ -83,7 +86,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 触发上传
|
// 触发上传
|
||||||
triggerUpload(uploadId: string) {
|
triggerUpload(uploadId: string,msgId: string) {
|
||||||
const item = this.findItem(uploadId)
|
const item = this.findItem(uploadId)
|
||||||
|
|
||||||
const form = item.files[item.uploadIndex]
|
const form = item.files[item.uploadIndex]
|
||||||
@ -98,11 +101,18 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
if (item.uploadIndex === item.files.length) {
|
if (item.uploadIndex === item.files.length) {
|
||||||
item.status = 2
|
item.status = 2
|
||||||
item.percentage = 100
|
item.percentage = 100
|
||||||
|
console.log(msgId,'msgId');
|
||||||
|
|
||||||
|
updateUploadProgress(msgId,100)
|
||||||
this.sendUploadMessage(item)
|
this.sendUploadMessage(item)
|
||||||
} else {
|
} else {
|
||||||
const percentage = (item.uploadIndex / item.files.length) * 100
|
const percentage = (item.uploadIndex / item.files.length) * 100
|
||||||
item.percentage = percentage.toFixed(1)
|
item.percentage = percentage.toFixed(1)
|
||||||
this.triggerUpload(uploadId)
|
console.log(msgId,'msgId');
|
||||||
|
console.log(percentage,'percentage');
|
||||||
|
|
||||||
|
updateUploadProgress(msgId,percentage)
|
||||||
|
this.triggerUpload(uploadId,msgId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.status = 3
|
item.status = 3
|
||||||
|
@ -167,7 +167,7 @@ export function getVideoImage(file, time = 1) {
|
|||||||
video.autoplay = true
|
video.autoplay = true
|
||||||
video.muted = true
|
video.muted = true
|
||||||
|
|
||||||
video.oncanplay = () => {
|
video.onloadeddata = () => {
|
||||||
let canvas = document.createElement('canvas')
|
let canvas = document.createElement('canvas')
|
||||||
canvas.width = video.videoWidth
|
canvas.width = video.videoWidth
|
||||||
canvas.height = video.videoHeight
|
canvas.height = video.videoHeight
|
||||||
|
@ -144,7 +144,7 @@ export const getFileType = (url) => {
|
|||||||
export const getFileFromUrl = (url, fileName) => {
|
export const getFileFromUrl = (url, fileName) => {
|
||||||
return new Promise(async(resolve, reject) => {
|
return new Promise(async(resolve, reject) => {
|
||||||
const osName = uni.getSystemInfoSync().osName;
|
const osName = uni.getSystemInfoSync().osName;
|
||||||
if (osName === "android") {
|
// if (osName === "android") {
|
||||||
var blob = null;
|
var blob = null;
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", url);
|
xhr.open("GET", url);
|
||||||
@ -165,12 +165,12 @@ export const getFileFromUrl = (url, fileName) => {
|
|||||||
};
|
};
|
||||||
// 发送
|
// 发送
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}else{
|
// }else{
|
||||||
const response = await fetch(url);
|
// const response = await fetch(url);
|
||||||
const blob = await response.blob();
|
// const blob = await response.blob();
|
||||||
const file = new File([blob], fileName, { type: getFileType(url) });
|
// const file = new File([blob], fileName, { type: getFileType(url) });
|
||||||
resolve(file);
|
// resolve(file);
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { storage } from './storage'
|
import { storage } from './storage'
|
||||||
|
import { useAuth } from '@/store/auth'
|
||||||
|
|
||||||
const AccessToken = 'AUTH_TOKEN'
|
const AccessToken = 'AUTH_TOKEN'
|
||||||
|
const {token} = useAuth()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证是否登录
|
* 验证是否登录
|
||||||
@ -17,7 +19,7 @@ export function isLoggedIn() {
|
|||||||
* @returns token
|
* @returns token
|
||||||
*/
|
*/
|
||||||
export function getAccessToken() {
|
export function getAccessToken() {
|
||||||
return storage.get(AccessToken) || ''
|
return token.value || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,7 @@ import { delAccessToken, getAccessToken } from '@/utils/upload/auth'
|
|||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
// API 请求的默认前缀
|
// API 请求的默认前缀
|
||||||
baseURL: import.meta.env.VITE_BASE_API,
|
baseURL: import.meta.env.VITE_BASEURL,
|
||||||
|
|
||||||
// 请求超时时间
|
// 请求超时时间
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
@ -21,7 +21,7 @@ const errorHandler = (error) => {
|
|||||||
// 判断是否是响应错误信息
|
// 判断是否是响应错误信息
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
if (error.response.status == 401) {
|
if (error.response.status == 401) {
|
||||||
delAccessToken()
|
// delAccessToken()
|
||||||
|
|
||||||
if (!once) {
|
if (!once) {
|
||||||
once = true
|
once = true
|
||||||
|