583 lines
14 KiB
Vue
583 lines
14 KiB
Vue
<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>
|