zbH5/src/views/Circle/components/ChatFrame.vue
2025-02-15 17:38:39 +08:00

583 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="date-select" @click="showCalendar = true">
<p>日期2024-19-22</p>
<van-icon name="arrow" />
</div>
<div :class="[`interact-scroll`, `interact-scroll${props.type}`]">
<div class="p20">
<div class="pulldown-wrapper">
<div v-if="hasNext">
<span>加载中...</span>
</div>
<div class="top-tip" v-else-if="!hasNext">
<span class="refresh-hook">暂无更多互动消息</span>
</div>
</div>
<!-- isOpen 2: 不公开 1公开 -->
<ul class="interact-msg" ref="msgListRef">
<li
v-for="item in msgList"
:class="[
`li${item.id}`,
item.userId === store.state.userInfo.account ? 'row-reverse' : '',
]"
:key="item.id"
>
<div
v-if="
item.status === 2 ||
(item.userType === 2 &&
item.userId === store.state.userInfo.account)
"
>
<div class="chat-time" v-if="item.chatTime">
{{ item.chatTime }}
</div>
<div
:class="[
'flex',
item.userId === store.state.userInfo.account
? 'row-reverse'
: '',
]"
>
<div class="photo">
<img
v-if="item.userType === 1"
:src="
item.advisor ? item.advisor.avatar : defaultAvatar.teacher
"
/>
<img
v-else-if="item.userType === 3"
:src="defaultAvatar.assistant"
/>
<img
v-else-if="item.userType === 2"
:src="defaultAvatar.student"
/>
</div>
<div class="news-content-wrap">
<div class="user-info">
<h3>
{{
item.userType === 2
? item.userId === store.state.userInfo.account
? "我"
: detail.showNickName === 2
? maskUserName(item.userName)
: item.userName
: item.userName
}}
<span v-if="item.userType === 1">讲师</span>
<span v-else-if="item.userType === 3">助教</span>
</h3>
</div>
<div v-if="[1, 2].includes(item.contentType) && item.replyId">
<div>
<div class="reply-content">
<div class="reply">
<span style="color: rgba(154, 164, 182, 1)">回复</span
><label
>{{
item.replyMessage?.userId ===
store.state.userInfo.account
? "我"
: detail.showNickName === 2
? maskUserName(item.replyMessage?.userName)
: item.replyMessage?.userName
}}</label
>
<template v-if="item.contentType === 1">
{{ item.content }}
</template>
<template v-else-if="item.contentType === 2">
<img
class="img"
@click="imagePreview(item.content)"
:src="item.content"
/>
</template>
</div>
<div class="reply" style="color: rgba(154, 164, 182, 1)">
{{
item.replyMessage?.userId ===
store.state.userInfo.account
? "我"
: detail.showNickName === 2
? maskUserName(item.replyMessage?.userName)
: item.replyMessage?.userName
}}
<template v-if="item.replyMessage?.contentType === 1">
{{ item.replyMessage?.content }}
</template>
<template
v-else-if="item.replyMessage?.contentType === 2"
>
<img
class="reply-img"
@click="imagePreview(item.replyMessage.content)"
:src="item.replyMessage.content"
/>
</template>
</div>
</div>
</div>
</div>
<div v-else-if="item.contentType === 1 && !item.replyId">
<div>
<p class="text">
{{ item.content }}
</p>
</div>
</div>
<div v-else-if="item.contentType === 2 && !item.replyId">
<div>
<img
class="img"
@click="imagePreview(item.content)"
:src="item.content"
/>
</div>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
<van-calendar v-model:show="showCalendar" />
<img
:class="['refresh', !msgList.length && loading ? 'loading' : '']"
@click="refreshMsg"
src="@/assets/images/refresh.png"
/>
</template>
<script setup>
import { ref, defineProps, watch, nextTick } from "vue";
import { useRoute } from "vue-router";
import { useStore } from "vuex";
import { showImagePreview } from "vant";
import useChatData from "../hooks/useChatData";
const store = useStore();
const route = useRoute();
const props = defineProps({
type: {
// // 1:互动 2:老师 5:私聊 4:精选
type: Number,
default: 1,
},
bsRefresh: {
type: Boolean,
default: false,
},
detail: {
type: Object,
default: () => {},
},
newMsg: {
type: Object,
default: () => {},
},
stompClient: {
type: Object,
default: null,
},
});
const defaultAvatar = {
teacher: require("@/assets/images/defaultAvatar/teacher.png"),
student: require("@/assets/images/defaultAvatar/student.png"),
assistant: require("@/assets/images/defaultAvatar/assistant.png"),
};
watch(
() => props.bsRefresh,
(value) => {
if (value && bs) {
nextTick(() => {
bs.value.refresh();
});
}
}
);
watch(
() => props.newMsg,
(data) => {
if (msgIdsObj.value[[data.id]]) {
Object.assign(msgIdsObj.value[data.id], data);
} else if (
(props.type === 1 && data.interactiveType === 1) ||
(props.type === 5 && data.interactiveType === 2) ||
(props.type === 2 && data.userType === 1) ||
(props.type === 4 && data.isRecommend === 1)
) {
pushNewMsg(data);
}
}
);
const imagePreview = (url) => {
showImagePreview({
images: [url],
closeable: true,
});
};
const showCalendar = ref(false);
const {
bs,
msgListRef,
msgList,
msgIdsObj,
hasNext,
pushNewMsg,
refreshMsg,
loading,
} = useChatData({
className: `.interact-scroll${props.type}`,
id: route.query.id,
type: props.type,
stompClient: props.stompClient,
});
const maskUserName = (value) => {
return value.charAt(0) + "**";
};
</script>
<style scoped lang="scss">
.interact-scroll {
position: relative;
overflow: hidden;
text-align: left;
height: 100%;
box-sizing: border-box;
background: #f5f6fa;
.p20 {
padding: 20px;
}
li {
margin-bottom: 20px;
.chat-time {
color: #888;
font-size: 24px;
text-align: center;
}
.img {
max-width: 100%;
height: 200px;
}
.warn {
text-align: center;
color: #999;
flex: 1;
margin-bottom: 0;
}
.flex {
display: flex;
}
.photo {
margin-right: 20px;
img {
display: block;
width: 88px;
height: 88px;
border-radius: 50%;
}
}
.user-info {
display: flex;
align-items: center;
h3 {
display: flex;
align-items: center;
font-size: 28px;
color: #1b2330;
line-height: 28px;
margin-bottom: 20px;
span {
color: #ff6f16;
font-size: 22px;
line-height: 22px;
margin-left: 20px;
border: 1px solid #ff3d36;
padding: 6px 8px;
border-radius: 8px;
}
}
}
.news-content-wrap {
flex: 1;
overflow: hidden;
& > div {
display: flex;
}
.align-center {
align-items: center;
}
}
}
li:last-child {
margin-bottom: 0;
}
.row-reverse {
justify-content: space-between;
flex-flow: row-reverse;
.user-info,
h3 {
justify-content: space-between;
flex-flow: row-reverse;
}
.photo {
margin-right: 0;
margin-left: 20px;
}
h3 {
span {
margin-left: 0;
margin-right: 20px;
}
}
.reply-content,
.text {
background: #2e78fa;
color: #fff;
}
.reply-content,
.text,
& > img,
.share-tip {
border-radius: 20px 4px 20px 20px;
}
.news-content-wrap > div {
flex-flow: row-reverse;
}
}
li > div {
font-size: 24px;
font-weight: 500;
letter-spacing: 0;
text-align: left;
color: #1b2330;
margin-bottom: 12px;
.question-item,
.product-item {
display: block;
p {
background: none;
padding-left: 0;
padding-top: 0;
}
}
label {
color: #2e78fa;
}
}
p,
.reply-content {
padding: 16px 24px;
background: #fff;
border-radius: 4px 20px 20px 20px;
font-size: 26px;
color: #606877;
text-align: justify;
line-height: 30px;
}
.license {
padding: 0;
color: #c6c7c9;
line-height: 24px;
font-size: 24px;
border-radius: 0 20px 0 20px;
margin: 12px 0 1px 1px;
}
.reply {
display: flex;
align-items: start;
line-height: 40px;
margin-right: 10px;
}
button {
height: 44px;
line-height: 44px;
border: none;
border-radius: 20px;
background-color: rgba(154, 164, 182, 0.1);
background-repeat: no-repeat;
background-position: left 10px center;
background-size: 20px auto;
padding-left: 32px;
font-size: 24px;
padding: 0 20px 0 30px;
&.share {
color: #ff3d36;
background-image: url(../../../assets/images/share-icon2.png);
}
&.attention {
color: #ff7d26;
background-image: url(../../../assets/images/attention.png);
}
}
}
.pulldown-wrapper {
position: absolute;
width: 100%;
padding: 20px;
box-sizing: border-box;
transform: translateY(-100%) translateZ(0);
text-align: center;
color: #999;
font-size: 24px;
}
.product-item {
.item-wrap {
width: 600px;
}
}
.question {
width: 600px;
background: #fff;
border-radius: 8px;
color: #000;
padding: 16px;
box-sizing: border-box;
background: url(../../../assets/images/chat/question-bg.png) no-repeat center;
background-size: 100% 100%;
height: 174px;
box-sizing: border-box;
h5 {
font-size: 28px;
margin-bottom: 20px;
color: #1e3566;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
span {
color: #8ea4b6;
font-size: 24px;
line-height: 36px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
.hp {
::v-deep .item-wrap,
.question {
height: 140px;
}
.question {
h5 {
margin-bottom: 8px;
}
}
::v-deep .item-wrap {
padding: 16px;
h4 {
margin-bottom: 0;
}
.intro {
line-height: 1.2em;
}
}
}
.product-float {
position: absolute;
left: 0;
bottom: 0;
width: 470px;
height: 180px;
::v-deep .item-wrap {
background: url(../../../assets/images/chat/product-float-bg.png) no-repeat
center;
background-size: 100% 100%;
}
img {
display: block;
position: absolute;
width: 24px;
height: 24px;
right: 20px;
top: 20px;
}
}
@media screen and (min-width: 480px) {
.interact-scroll .question {
height: 230px !important;
padding: 20px 30px;
h5 {
line-height: 50px !important;
}
}
.hp .question {
height: 200px !important;
padding: 20px 20px;
}
.interact-scroll h4 {
line-height: 48px !important;
}
.hp ::v-deep .item-wrap {
height: 170px !important;
h4,
p {
line-height: 50px;
}
}
}
.new-msg-tip {
position: absolute;
left: 0;
bottom: 0px;
color: #2e78fa;
background: #fff;
border: 1px solid rgba(204, 204, 204, 0.2);
font-size: 24px;
padding: 8px 12px;
border-radius: 20px;
}
.refresh {
position: absolute;
right: 24px;
bottom: 100px;
width: 64px;
}
.date-select {
display: flex;
justify-content: space-between;
font-size: 28px;
padding: 20px;
}
.loading {
animation: loadMove 1s;
-webkit-animation: loadMove 1s; /*Safari and Chrome*/
}
@keyframes loadMove {
0% {
transform: rotate(0);
opacity: 1;
}
100% {
transform: rotate(360deg);
opacity: 0;
}
}
@-webkit-keyframes loadMove /*Safari and Chrome*/ {
from {
transform: rotate(0);
opacity: 1;
}
to {
transform: rotate(360deg);
opacity: 0;
}
}
</style>