fix: 直播样式调整

This commit is contained in:
kaizheng(郑凯) 2025-03-09 15:06:29 +08:00
parent 74d35a6491
commit d1094417d8
16 changed files with 622 additions and 282 deletions

2
.gitignore vendored
View File

@ -2,7 +2,7 @@
node_modules
/dist
/build
/test
# local env files
.env.local

299
src/assets/css/live.css Normal file
View File

@ -0,0 +1,299 @@
body {
--bubble_time: 3s;
--bubble_scale: 3s;
}
.bubble {
position: absolute;
width: 80px;
height: 80px;
left: 50%;
left: 50%;
background-repeat: no-repeat;
background-size: 100%;
transform-origin: bottom;
}
.b1 {
background-image: url(../images/liveIcon/bg1.png);
}
.b2 {
background-image: url(../images/liveIcon/bg2.png);
}
.b3 {
background-image: url(../images/liveIcon/bg3.png);
}
.b4 {
background-image: url(../images/liveIcon/bg4.png);
}
.b5 {
background-image: url(../images/liveIcon/bg5.png);
}
.b6 {
background-image: url(../images/liveIcon/bg6.png);
}
.bl1 {
animation: bubble_1 var(--bubble_time) linear 1 forwards,
bubble_big_1 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl2 {
animation: bubble_2 var(--bubble_time) linear 1 forwards,
bubble_big_2 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl3 {
animation: bubble_3 var(--bubble_time) linear 1 forwards,
bubble_big_1 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl4 {
animation: bubble_4 var(--bubble_time) linear 1 forwards,
bubble_big_2 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl5 {
animation: bubble_5 var(--bubble_time) linear 1 forwards,
bubble_big_1 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl6 {
animation: bubble_6 var(--bubble_time) linear 1 forwards,
bubble_big_3 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl7 {
animation: bubble_7 var(--bubble_time) linear 1 forwards,
bubble_big_1 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl8 {
animation: bubble_8 var(--bubble_time) linear 1 forwards,
bubble_big_3 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl9 {
animation: bubble_9 var(--bubble_time) linear 1 forwards,
bubble_big_2 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl10 {
animation: bubble_10 var(--bubble_time) linear 1 forwards,
bubble_big_1 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
.bl11 {
animation: bubble_11 var(--bubble_time) linear 1 forwards,
bubble_big_2 var(--bubble_scale) linear 1 forwards,
bubble_y var(--bubble_time) linear 1 forwards;
}
@keyframes bubble_11 {
0% {
}
25% {
margin-left: -15px;
}
50% {
margin-left: -15px;
}
100% {
margin-left: -20px;
}
}
@keyframes bubble_10 {
0% {
}
25% {
margin-left: -20px;
}
50% {
margin-left: -20px;
}
100% {
margin-left: -20px;
}
}
@keyframes bubble_9 {
0% {
}
25% {
margin-left: 10px;
}
50% {
margin-left: 10px;
}
100% {
margin-left: 10px;
}
}
@keyframes bubble_8 {
0% {
}
25% {
margin-left: 30px;
}
50% {
margin-left: 30px;
}
100% {
margin-left: 30px;
}
}
@keyframes bubble_7 {
0% {
}
25% {
margin-left: 6px;
}
50% {
margin-left: 2px;
}
75% {
margin-left: 4px;
}
100% {
margin-left: 6px;
}
}
@keyframes bubble_6 {
0% {
}
25% {
margin-left: -6px;
}
50% {
margin-left: -2px;
}
75% {
margin-left: -4px;
}
100% {
margin-left: -6px;
}
}
@keyframes bubble_5 {
0% {
}
25% {
margin-left: 10px;
}
50% {
margin-left: -10px;
}
75% {
margin-left: -20px;
}
100% {
margin-left: -40px;
}
}
@keyframes bubble_4 {
0% {
}
25% {
margin-left: -10px;
}
50% {
margin-left: -10px;
}
75% {
margin-left: 40px;
}
100% {
margin-left: 20px;
}
}
@keyframes bubble_3 {
0% {
}
25% {
margin-left: -40px;
}
50% {
margin-left: 20px;
}
75% {
margin-left: 40px;
}
100% {
margin-left: -20px;
}
}
@keyframes bubble_2 {
0% {
}
25% {
margin-left: 40px;
}
50% {
margin-left: 50px;
}
75% {
margin-left: 20px;
}
100% {
margin-left: 10px;
}
}
@keyframes bubble_1 {
0% {
}
25% {
margin-left: -16px;
}
50% {
margin-left: 16px;
}
75% {
margin-left: -30px;
}
100% {
margin-left: 30px;
}
}
@keyframes bubble_big_1 {
0% {
/* transform: scale(0.3); */
width: 20px;
height: 20px;
}
100% {
width: 80px;
height: 80px;
}
}
@keyframes bubble_big_2 {
0% {
/* transform: scale(0.3); */
width: 15px;
height: 15px;
}
100% {
/* transform: scale(0.9); */
width: 60px;
height: 60px;
}
}
@keyframes bubble_big_3 {
0% {
width: 10px;
height: 10px;
}
100% {
width: 40px;
height: 40px;
}
}
@keyframes bubble_y {
0% {
top: -10px;
}
10% {
top: -40px;
}
75% {
opacity: 1;
}
100% {
top: -400px;
opacity: 0;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,22 @@
export default function useContinuousClickLive() {
const addContinuousClickLive = (boxClass) => {
const b = Math.floor(Math.random() * 6) + 1
const bl = Math.floor(Math.random() * 11) + 1 // bl1~bl11
let d = document.createElement("div")
d.className = `bubble b${b} bl${bl}`
d.dataset.t = String(Date.now())
const likeBox = document.querySelector(boxClass)
likeBox.appendChild(d)
;((d) => {
d.addEventListener("animationend", () => {
debugger
if (likeBox.contains(d)) {
likeBox.removeChild(d)
}
})
})(d)
}
return addContinuousClickLive
}

View File

@ -20,12 +20,11 @@
detail.advisorBasic && detail.advisorBasic.avatar
? detail.advisorBasic.avatar
: defaultPhoto
"
/>
" />
</div>
<div class="news-content-wrap">
<div class="user-info">
<h3>{{ detail.advisorBasic?.showName }} <span>讲师</span></h3>
<h3><span>讲师</span>{{ detail.advisorBasic?.showName }}</h3>
</div>
<div>
<p class="text">
@ -49,16 +48,14 @@
`li${item.id}`,
item.phone === store.state.userInfo.userId ? 'row-reverse' : '',
]"
:key="item.id"
>
:key="item.id">
<div v-if="item.show">
<!-- <div v-if="item.type === 3" class="warn">
{{ item.userName }}进入直播间
</div> -->
<div
class="chat-time"
v-if="(isTg && item.tgChatTime) || (!isTg && item.chatTime)"
>
v-if="(isTg && item.tgChatTime) || (!isTg && item.chatTime)">
{{ isTg ? item.tgChatTime : item.chatTime }}
</div>
<div v-if="item.type === 5" class="warn">
@ -72,8 +69,7 @@
<button
class="share"
@click="sendMsg(5)"
v-if="item.phone !== store.state.userInfo.userId"
>
v-if="item.phone !== store.state.userInfo.userId">
我也要分享
</button>
</div>
@ -82,8 +78,7 @@
:class="[
'flex',
item.phone === store.state.userInfo.userId ? 'row-reverse' : '',
]"
>
]">
<div class="photo">
<img
:src="
@ -95,8 +90,7 @@
? item.imgUrl
: tgDefaultPhoto
"
alt=""
/>
alt="" />
</div>
<div class="news-content-wrap">
<div class="user-info">
@ -160,8 +154,7 @@
v-else
class="img"
@click="imagePreview(item.content.split('upImg-')[1])"
:src="item.content.split('upImg-')[1]"
/>
:src="item.content.split('upImg-')[1]" />
<div v-if="item.advisorId" class="license">
{{ detail.advisorBasic.name
}}<i v-if="detail.advisorBasic.license"
@ -176,8 +169,7 @@
:item="item.productBasic"
:liveProductId="detail.id"
:tgId="detail.advisorBasic?.id"
:isShopCar="true"
/>
:isShopCar="true" />
</div>
<div v-else-if="item.type === 4">
<p>
@ -197,15 +189,13 @@
v-if="
item.phone !== store.state.userInfo.userId &&
detail.isSubAdvisor !== 1
"
>
">
我也关注
</button>
</div>
<div
v-else-if="[6, 7, 9, 10].includes(item.type)"
style="color: #f46946"
>
style="color: #f46946">
<p>{{ item.content }}</p>
</div>
<div v-else-if="item.type === 8" style="color: #f46946">
@ -264,25 +254,25 @@ import {
onMounted,
computed,
defineEmits,
} from "vue";
import { useStore } from "vuex";
import { showToast, showImagePreview } from "vant";
} from "vue"
import { useStore } from "vuex"
import { showToast, showImagePreview } from "vant"
import {
sendFollowMessage,
queryQuestionCheck,
queryLiveTgHisMsg,
queryLiveHisMsg,
} from "@/api/video";
import QuestionnairePopup from "../components/QuestionnairePopup.vue";
import useChatData from "@/hooks/useChatData";
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj";
import ProductItem from "../components/ProductItem.vue";
import { attentionTg } from "@/api/index";
import emitter from "@/utils/emitter";
} from "@/api/video"
import QuestionnairePopup from "../components/QuestionnairePopup.vue"
import useChatData from "@/hooks/useChatData"
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj"
import ProductItem from "../components/ProductItem.vue"
import { attentionTg } from "@/api/index"
import emitter from "@/utils/emitter"
// import dayjs from "dayjs";
const store = useStore();
const $liveStatusObj = useGetLiveStatusObj();
const store = useStore()
const $liveStatusObj = useGetLiveStatusObj()
const props = defineProps({
className: {
@ -302,76 +292,76 @@ const props = defineProps({
type: Boolean,
default: false,
},
});
})
const defaultPhoto = require("@/assets/images/default-photo.png");
const tgDefaultPhoto = require("@/assets/images/tg-p.webp");
const defaultPhoto = require("@/assets/images/default-photo.png")
const tgDefaultPhoto = require("@/assets/images/tg-p.webp")
// const productFloatShow = ref(false);
const scrollClassName = ref(`interact-scroll-${new Date().getTime()}`);
const scrollClassName = ref(`interact-scroll-${new Date().getTime()}`)
const { msgListRef, bs } = useChatData(
Object.assign({ isTg: props.isTg }, props.detail, {
className: `.${scrollClassName.value}`,
})
);
)
const hasNext = computed(() => {
return props.isTg
? store.state.interactMsgObj.tgHasHisMsg
: store.state.interactMsgObj.hasHisMsg;
});
: store.state.interactMsgObj.hasHisMsg
})
watch(hasNext, (value) => {
if (value) bs.value && bs.value.openPullDown();
});
if (value) bs.value && bs.value.openPullDown()
})
watch(
() => props.informMsgList,
() => {
nextTick(() => {
bs.value && bs.value.refresh();
bs.value && bs.value.refresh()
if (
(props.isTg && store.state.interactMsgObj.isTgScrollToBottom) ||
(!props.isTg && store.state.interactMsgObj.isScrollToBottom)
) {
nextTick(() => {
bs.value && bs.value.scrollTo(0, bs.value.maxScrollY, 0);
});
bs.value && bs.value.scrollTo(0, bs.value.maxScrollY, 0)
})
}
});
})
},
{ deep: true }
);
)
let intervalTime = null;
let intervalTime = null
const replenishMsg = async () => {
let fn = props.isTg ? queryLiveTgHisMsg : queryLiveHisMsg;
let fn = props.isTg ? queryLiveTgHisMsg : queryLiveHisMsg
let ret = await fn({
id: props.detail.id,
lastId: "",
size: 20,
status: 1,
});
})
if (ret.code === 0) {
let list = ret.data.list.reverse();
emitter.emit("informMsgListPush", list);
let list = ret.data.list.reverse()
emitter.emit("informMsgListPush", list)
}
};
}
let getNewMsg = (once) => {
if (once) {
replenishMsg();
replenishMsg()
} else {
clearInterval(intervalTime);
intervalTime = setInterval(replenishMsg, 1000 * 30);
clearInterval(intervalTime)
intervalTime = setInterval(replenishMsg, 1000 * 30)
}
};
}
emitter.on("getNewMsg", getNewMsg);
emitter.on("getNewMsg", getNewMsg)
emitter.on("cancelGetNewMsg", () => {
clearInterval(intervalTime);
});
clearInterval(intervalTime)
})
async function subAdvisor() {
let ret = await attentionTg({
@ -379,11 +369,11 @@ async function subAdvisor() {
channel: 2,
option: props.detail.isSubAdvisor === 1 ? 2 : 1,
videoId: props.detail.id,
});
})
if (ret.code === 0) {
emitter.emit("updateVideoDetail", {
isSubAdvisor: props.detail.isSubAdvisor === 1 ? 2 : 1,
});
})
props.detail.isSubAdvisor === 1 &&
[
$liveStatusObj.InLive,
@ -393,86 +383,86 @@ async function subAdvisor() {
sendFollowMessage({
id: props.detail.id,
option: props.detail.isSubAdvisor === 1 ? 2 : 1,
});
})
}
}
const questionnairePopupRef = ref();
const questionId = ref();
const questionnairePopupRef = ref()
const questionId = ref()
const goAnswer = async (item) => {
let ret = await queryQuestionCheck({ questionId: item.questionId });
let ret = await queryQuestionCheck({ questionId: item.questionId })
if (ret.code === 0) {
if (ret.data.result) {
questionId.value = item.questionId;
questionnairePopupRef.value.showPopup = true;
questionId.value = item.questionId
questionnairePopupRef.value.showPopup = true
} else if (ret.data.type === 1) {
return showToast("您已完成问卷任务,无须重复填写!");
return showToast("您已完成问卷任务,无须重复填写!")
} else if (ret.data.type === 2) {
return showToast("问卷已删除!");
return showToast("问卷已删除!")
}
}
};
const emit = defineEmits(["optShare"]);
}
const emit = defineEmits(["optShare"])
const sendMsg = (type, params) => {
if (type === 5) {
emit("optShare");
emit("optShare")
}
emitter.emit("emSendMsg", {
type,
params,
});
};
})
}
onBeforeUnmount(() => {
emitter.off("getNewMsg");
emitter.off("cancelGetNewMsg");
});
emitter.off("getNewMsg")
emitter.off("cancelGetNewMsg")
})
const imagePreview = (url) => {
showImagePreview({
images: [url],
closeable: true,
});
};
})
}
const newMsgNum = computed(() => {
return props.isTg
? store.state.interactMsgObj.temTgInteractShowNum
: store.state.interactMsgObj.temUserInteractShowNum;
});
: store.state.interactMsgObj.temUserInteractShowNum
})
onMounted(() => {
bs.value.on("scrollEnd", () => {
let isBottom = bs.value.maxScrollY + 20 >= bs.value.y;
let isBottom = bs.value.maxScrollY + 20 >= bs.value.y
store.commit("setIsScrollToBottom", {
isTg: props.isTg,
data: isBottom,
});
if (isBottom) resetData();
});
});
})
if (isBottom) resetData()
})
})
const lookNewMsg = () => {
resetData();
};
resetData()
}
const resetData = () => {
store.commit("interactMsgMerge", { isTg: props.isTg });
store.commit("interactMsgMerge", { isTg: props.isTg })
nextTick(() => {
bs.value && bs.value.refresh();
bs.value && bs.value.refresh()
nextTick(() => {
bs.value && bs.value.scrollTo(0, bs.value.maxScrollY, 0);
});
});
};
bs.value && bs.value.scrollTo(0, bs.value.maxScrollY, 0)
})
})
}
const maskUserName = (value) => {
return value.charAt(0) + "**";
};
return value.charAt(0) + "**"
}
defineExpose({
bs,
});
})
</script>
<style scoped lang="scss">
.interact-scroll {
@ -519,18 +509,19 @@ defineExpose({
h3 {
display: flex;
align-items: center;
font-size: 28px;
color: #1b2330;
font-size: 24px;
color: #666666;
line-height: 28px;
margin-bottom: 20px;
font-weight: 600;
span {
color: #ff6f16;
font-size: 22px;
line-height: 22px;
margin-left: 20px;
border: 1px solid #ff3d36;
margin-right: 10px;
background: linear-gradient(90deg, #66adff, #1472ff);
padding: 6px 8px;
border-radius: 8px;
border-radius: 4px;
color: #fff;
}
}
}
@ -568,14 +559,14 @@ defineExpose({
}
.reply-content,
.text {
background: #2e78fa;
color: #fff;
background: #87ee7e;
color: #333;
}
.reply-content,
.text,
& > img,
.share-tip {
border-radius: 20px 4px 20px 20px;
border-radius: 10px 4px 10px 10px;
}
.news-content-wrap > div {
flex-flow: row-reverse;
@ -605,11 +596,11 @@ defineExpose({
.reply-content {
padding: 16px 24px;
background: #fff;
border-radius: 4px 20px 20px 20px;
font-size: 26px;
color: #606877;
border-radius: 4px 10px 10px 10px;
font-size: 28px;
color: #333;
text-align: justify;
line-height: 30px;
line-height: 36px;
}
.license {
padding: 0;

View File

@ -2,37 +2,35 @@
<div class="scroll-wrap">
<div class="detail-desc">
<div class="detail-title fw-5" @click="$egg(3, 1000)">
{{ detail.title }}
<h4>{{ detail.title }}</h4>
<div class="play-count">{{ detail.readCount }}次播放</div>
</div>
<div class="tag-count flex-ac-sb">
<div v-show="detail.infoVO" class="tag">
{{ `${detail.infoVO?.productName} 专属服务` }}
</div>
<div class="count flex-ac">
<!-- <div class="count flex-ac">
<div class="play-count">{{ detail.readCount }}次播放</div>
<div class="star-count flex-ac" v-if="!$shieldConfig.tgLive">
<img
v-if="detail.isFavor === 1"
src="@/assets/images/like-icon3.png"
alt=""
/>
alt="" />
<img
v-else
src="@/assets/images/like-icon.png"
alt=""
@click="sendLikeVideo(1)"
/>
@click="sendLikeVideo(1)" />
<div
class="text-18 fw-5"
:class="detail.isFavor === 1 ? 'tc-f13721' : 'tc-1B2330'"
>
:class="detail.isFavor === 1 ? 'tc-f13721' : 'tc-1B2330'">
{{ detail.favorUserCount ? detail.favorUserCount : "" }}
</div>
</div>
</div>
</div> -->
</div>
<div class="author">
<div class="mb20">
<div>
<div class="author-desc flex-ac">
<img
class="avatar"
@ -41,41 +39,38 @@
? detail.advisorBasic.avatar
: defaultPhoto
"
alt=""
/>
alt="" />
<label>主讲</label>
<div>
<div class="text-28 tc-606877">
{{ detail.advisorBasic?.showName }}
</div>
</div>
<label>主讲</label>
</div>
<div class="handle flex-ac no-wrap">
<button
class="add"
@click.stop="subAdvisor"
v-if="!$shieldConfig.tgLive"
>
v-if="!$shieldConfig.tgLive">
{{ detail.isSubAdvisor === 1 ? "取消关注" : "关注" }}
</button>
</div>
</div>
<div class="author-desc flex-ac" v-if="detail.guestInfo">
<img class="avatar" :src="detail.guestInfo.avatar" alt="" />
<label>嘉宾</label>
<div>
<div class="text-28 tc-606877">
{{ detail.guestInfo.showName }}
</div>
</div>
<label>嘉宾</label>
</div>
</div>
</div>
<div class="line"></div>
<div
v-if="detail.liveStatus === $liveStatusObj.NotStart"
class="countdown-wrapper"
>
class="countdown-wrapper">
<!-- <img class="coupon" src="@/assets/image/coupon@2x.png" alt="" @click="toTicket()" /> -->
<div class="title-time flex-ac-sb">
<div class="text-36 fw-5 tc-1B2330">
@ -93,13 +88,11 @@
detail.isSubscribe !== 1 && detail.allowSubscribe !== 2
? 'to-preview'
: 'previewd'
"
>
">
<div
v-if="detail.isSubscribe !== 1 && detail.allowSubscribe !== 2"
class="flex-ac"
v-preReClick="setSubLiveVideo"
>
v-preReClick="setSubLiveVideo">
<img class="status-icon" src="@/assets/images/status2.png" alt="" />
<div class="desc tc-2E78FA">
{{ detail.playType == 1 ? "预约直播" : "预约视频" }}
@ -116,7 +109,7 @@
</div>
<div class="line"></div>
<div class="watch-focus">
<div class="text-36 tc-1B2330 fw-5">本期看点</div>
<div class="text-36 tc-1B2330 fw-5">视频简介</div>
<div class="watch-content tc-1B2330">
{{ detail.viewPoint }}
</div>
@ -126,25 +119,25 @@
</template>
<script setup>
import { defineProps, onMounted } from "vue";
import { useRoute } from "vue-router";
import { sendFollowMessage, likeVideo } from "@/api/video";
import { attentionTg } from "@/api/index";
import emitter from "@/utils/emitter";
import CountDown from "./components/CountDown.vue";
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj";
import useDisableScroll from "@/hooks/useDisableScroll";
import { subLiveVideo } from "@/api/video";
import useShieldConfig from "@/hooks/useShieldConfig";
import useLoadConsole from "@/hooks/useLoadConsole";
const $egg = useLoadConsole();
const $liveStatusObj = useGetLiveStatusObj();
const route = useRoute();
const $shieldConfig = useShieldConfig();
const { addScrollEvent } = useDisableScroll();
import { defineProps, onMounted } from "vue"
import { useRoute } from "vue-router"
import { sendFollowMessage } from "@/api/video"
import { attentionTg } from "@/api/index"
import emitter from "@/utils/emitter"
import CountDown from "./components/CountDown.vue"
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj"
import useDisableScroll from "@/hooks/useDisableScroll"
import { subLiveVideo } from "@/api/video"
import useShieldConfig from "@/hooks/useShieldConfig"
import useLoadConsole from "@/hooks/useLoadConsole"
const $egg = useLoadConsole()
const $liveStatusObj = useGetLiveStatusObj()
const route = useRoute()
const $shieldConfig = useShieldConfig()
const { addScrollEvent } = useDisableScroll()
onMounted(() => {
addScrollEvent(".scroll-wrap");
});
addScrollEvent(".scroll-wrap")
})
const props = defineProps({
detail: {
@ -152,8 +145,8 @@ const props = defineProps({
ype: Object,
default: () => ({}),
},
});
const defaultPhoto = require("@/assets/images/default-photo.png");
})
const defaultPhoto = require("@/assets/images/default-photo.png")
//
async function subAdvisor() {
@ -162,11 +155,11 @@ async function subAdvisor() {
channel: 2,
option: props.detail.isSubAdvisor === 1 ? 2 : 1,
videoId: props.detail.id,
});
})
if (ret.code === 0) {
emitter.emit("updateVideoDetail", {
isSubAdvisor: props.detail.isSubAdvisor === 1 ? 2 : 1,
});
})
props.detail.isSubAdvisor === 1 &&
[
$liveStatusObj.InLive,
@ -176,37 +169,37 @@ async function subAdvisor() {
sendFollowMessage({
id: props.detail.id,
option: props.detail.isSubAdvisor === 1 ? 2 : 1,
});
})
}
}
const sendLikeVideo = async (liveNum) => {
const ret = await likeVideo({
id: props.detail.id,
option: 1,
num: liveNum,
});
if (ret.code === 0) {
emitter.emit("updateVideoDetail", {
isFavor: 1,
favorUserCount: ret.data.count,
});
}
};
// const sendLikeVideo = async (liveNum) => {
// const ret = await likeVideo({
// id: props.detail.id,
// option: 1,
// num: liveNum,
// })
// if (ret.code === 0) {
// emitter.emit("updateVideoDetail", {
// isFavor: 1,
// favorUserCount: ret.data.count,
// })
// }
// }
const setSubLiveVideo = async () => {
let ret = await subLiveVideo({
id: props.detail.id,
option: props.detail.isSubscribe === 1 ? 2 : 1,
saleUserId: route.query.saleUserId,
});
})
if (ret.code === 0) {
emitter.emit("updateVideoDetail", {
isSubscribe: props.detail.isSubscribe === 1 ? 2 : 1,
subscribeUserCount: ret.data.count,
});
})
}
};
}
</script>
<style scoped lang="scss">
.mb20 {
@ -260,11 +253,25 @@ const setSubLiveVideo = async () => {
background-color: #fff;
text-align: left;
.detail-title {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 36px;
line-height: 54px;
margin-bottom: 8px;
color: #1b2330;
font-weight: 500;
h4 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.play-count {
margin: 0 20px 0 16px;
color: #9aa4b6;
font-size: 24px;
}
}
.tag {
padding: 6px 8px;
@ -276,9 +283,9 @@ const setSubLiveVideo = async () => {
color: #9aa4b6;
font-size: 24px;
}
.play-count {
margin: 0 20px 0 16px;
}
// .play-count {
// margin: 0 20px 0 16px;
// }
.star-count {
img {
width: 32px;
@ -290,6 +297,12 @@ const setSubLiveVideo = async () => {
padding: 24px;
background: #f8f9fa;
border-radius: 12px;
& > div {
margin-bottom: 20px;
}
& > div:last-child {
margin-bottom: 0;
}
.avatar {
margin-right: 12px;
width: 60px;
@ -314,12 +327,13 @@ const setSubLiveVideo = async () => {
}
.author-desc {
label {
font-size: 24px;
color: #2e78fa;
border: 1px solid #2e78fa;
padding: 4px 16px;
border-radius: 12px;
margin-left: 16px;
font-size: 22px;
line-height: 22px;
margin-right: 10px;
background: linear-gradient(90deg, #66adff, #1472ff);
padding: 6px 8px;
border-radius: 4px;
color: #fff;
}
}
}

View File

@ -6,16 +6,18 @@
:overlay="false"
:round="false"
:lock-scroll="false"
:lazy-render="false"
>
:lazy-render="false">
<div class="msg-content">
<ChatFrame
:informMsgList="detail.interactType === 2 ? store.state.interactMsgObj.userInteractMsgList : store.state.interactMsgObj.tgInteractMsgList"
:informMsgList="
detail.interactType === 2
? store.state.interactMsgObj.userInteractMsgList
: store.state.interactMsgObj.tgInteractMsgList
"
:detail="detail"
:isTg="detail.interactType === 1"
@optShare="optShare"
ref="ChatFrameRef"
/>
ref="ChatFrameRef" />
</div>
<div class="input-area">
<input
@ -27,7 +29,7 @@
? '直播结束,暂不支持互动哟~'
: isSpeak
? '互动已关闭...'
: '与老师互动...'
: '说点什么...'
"
v-model.trim="text"
@focus="inputMsg"
@ -36,13 +38,11 @@
isSpeak ||
[$liveStatusObj.FinishPlay].includes(detail.liveStatus)
"
@keyup.enter="sendMsg(1, { text })"
/>
@keyup.enter="sendMsg(1, { text })" />
<button
class="send"
:disabled="!text || sendTextLoading"
@click="sendMsg(1, { text })"
>
@click="sendMsg(1, { text })">
发送
</button>
</div>
@ -51,15 +51,22 @@
</template>
<script setup>
import { ref, defineProps, watch, defineExpose, nextTick, defineEmits } from "vue";
import { showToast } from "vant";
import emitter from "@/utils/emitter";
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj";
import ChatFrame from "./ChatFrame.vue";
import { useStore } from "vuex";
const $liveStatusObj = useGetLiveStatusObj();
import {
ref,
defineProps,
watch,
defineExpose,
nextTick,
defineEmits,
} from "vue"
import { showToast } from "vant"
import emitter from "@/utils/emitter"
import useGetLiveStatusObj from "@/hooks/useGetLiveStatusObj"
import ChatFrame from "./ChatFrame.vue"
import { useStore } from "vuex"
const $liveStatusObj = useGetLiveStatusObj()
const store = useStore();
const store = useStore()
const props = defineProps({
detail: {
//
@ -71,85 +78,85 @@ const props = defineProps({
type: Boolean,
default: false,
},
optShare:{
optShare: {
type: Function,
default: ()=>{}
}
});
default: () => {},
},
})
const show = ref(false);
const show = ref(false)
const showRecommend = ref(false); //
const showRecommend = ref(false) //
watch(
() => props.activityObj,
() => {
if (props.activityObj && Object.keys(props.activityObj).length) {
showRecommend.value = true;
showRecommend.value = true
}
}
);
)
const ChatFrameRef = ref();
const ChatFrameRef = ref()
const emit = defineEmits(['bsRefresh'])
const emit = defineEmits(["bsRefresh"])
watch(
() => show.value,
(val) => {
nextTick(() => {
if (val) {
if (ChatFrameRef.value && ChatFrameRef.value.bs){
ChatFrameRef.value.bs.refresh();
ChatFrameRef.value.bs.scrollTo(0, ChatFrameRef.value.bs.maxScrollY, 0);
if (ChatFrameRef.value && ChatFrameRef.value.bs) {
ChatFrameRef.value.bs.refresh()
ChatFrameRef.value.bs.scrollTo(0, ChatFrameRef.value.bs.maxScrollY, 0)
}
} else {
emit('bsRefresh')
emit("bsRefresh")
}
});
})
}
);
)
function inputMsg() {
let bs = ChatFrameRef.value.bs;
bs && bs.stop();
bs && bs.scrollTo(0, bs.maxScrollY, 0);
let bs = ChatFrameRef.value.bs
bs && bs.stop()
bs && bs.scrollTo(0, bs.maxScrollY, 0)
}
const text = ref();
const sendTextLoading = ref(false);
const text = ref()
const sendTextLoading = ref(false)
const sendMsg = (type, params = {}) => {
if (![1, 4].includes(type)) {
//
emitter.emit("emSendMsg", { type, params });
emitter.emit("emSendMsg", { type, params })
}
switch (type) {
case 1:
if (text.value) {
sendTextLoading.value = true;
sendTextLoading.value = true
emitter.emit("emSendMsg", {
type,
params: {
...params,
callBack: () => {
sendTextLoading.value = false;
text.value = "";
sendTextLoading.value = false
text.value = ""
},
errorBack: () => {
sendTextLoading.value = false;
sendTextLoading.value = false
},
},
});
})
} else {
showToast("请输入互动内容!");
showToast("请输入互动内容!")
}
break;
break
default:
break;
break
}
};
}
defineExpose({
show,
});
})
</script>
<style scoped lang="scss">
::v-deep .van-popup {

View File

@ -9,8 +9,7 @@
"
:detail="detail"
:isTg="detail.interactType === 2"
@optShare="optShare"
/>
@optShare="optShare" />
<div class="operate-wrap">
<div class="operate" @click="showInteractAction">
<img src="@/assets/images/msg-icon.png" alt="" />
@ -18,20 +17,17 @@
<div
class="operate"
@click="changeMsgTip"
v-show="detail.interactType === 2"
>
v-show="detail.interactType === 2">
<img :src="msgTipIcon[msgTipIndex]" alt="" />
</div>
<ul
class="new-msg-list"
v-show="msgTipIndex === 1"
v-if="detail.interactType === 2"
>
v-if="detail.interactType === 2">
<li
v-for="(item, index) in newMsgTree"
:key="index"
@click="showInteractAction"
>
@click="showInteractAction">
<div class="content">
<p>{{ item.content }}</p>
<img :src="item.imgUrl" />
@ -43,8 +39,7 @@
<div
class="recommend-img"
v-show="showRecommend"
@click="toActivityPage(1)"
>
@click="toActivityPage(1)">
<img :src="activityObj.imgUrl" alt="" srcset="" />
<i @click.stop="closeActivity"></i>
</div>
@ -63,7 +58,7 @@
? '直播结束,暂不支持互动哟~'
: isSpeak
? '互动已关闭...'
: '与老师互动...'
: '说点什么...'
"
v-model.trim="text"
@focus="inputMsg"
@ -72,13 +67,11 @@
isSpeak ||
[$liveStatusObj.FinishPlay].includes(detail.liveStatus)
"
@keyup.enter="sendMsg(1, { text })"
/>
@keyup.enter="sendMsg(1, { text })" />
<button
class="send"
:disabled="!text || sendTextLoading"
@click="sendMsg(1, { text })"
>
@click="sendMsg(1, { text })">
发送
</button>
<button
@ -87,13 +80,16 @@
v-if="
terminalType === 'Browser' ||
(terminalType === 'App' && system === 'android')
"
></button>
"></button>
<button
v-if="detail.liveStatus !== $liveStatusObj.NotStart"
:class="['star', favorUserCountObj.isFavor === 1 ? 'active' : '']"
@click.stop="sendMsg(4)"
>
class="star-btn"
v-if="detail.liveStatus !== $liveStatusObj.NotStart">
<div
@click.stop="sendMsg(4)"
:class="[
'star',
favorUserCountObj.isFavor === 1 ? 'active' : '',
]"></div>
<span v-if="favorUserCountObj.favorUserCount">{{
bigNumberTransform(favorUserCountObj.favorUserCount)
}}</span>
@ -105,8 +101,7 @@
couponDetail &&
couponDetail.sendTotalNumber - couponDetail.sendGottenNumber > 0
"
@click="showDiscountCoupon"
>
@click="showDiscountCoupon">
<!-- <p>
仅剩{{ couponDetail.sendTotalNumber - couponDetail.sendGottenNumber }}
</p> -->
@ -122,8 +117,7 @@
ref="InteractRef"
:isSpeak="isSpeak"
:optShare="optShare"
@bsRefresh="bsRefresh"
/>
@bsRefresh="bsRefresh" />
</template>
<script setup>
@ -151,6 +145,7 @@ import ChatFrame from "./ChatFrame.vue"
import Interact from "./Interact.vue"
import { terminalType } from "@/utils/index"
import { getSystem } from "@/utils/index"
import useContinuousClickLive from "@/hooks/useContinuousClickLive"
// import TgChatFrame from "./TgChatFrame.vue";
// import MainInteract from "./MainInteract.vue";
import { queryCartRead } from "@/api/video"
@ -317,12 +312,12 @@ const closeCarPush = () => {
}
let liveIndex = 0
const liveIcon = [
require("@/assets/images/liveIcon/icon1.png"),
require("@/assets/images/liveIcon/icon2.png"),
require("@/assets/images/liveIcon/icon3.png"),
require("@/assets/images/liveIcon/icon4.png"),
]
// const liveIcon = [
// require("@/assets/images/liveIcon/icon1.png"),
// require("@/assets/images/liveIcon/icon2.png"),
// require("@/assets/images/liveIcon/icon3.png"),
// require("@/assets/images/liveIcon/icon4.png"),
// ]
const favorUserCountObj = reactive({
favorUserCount: props.detail.favorUserCount,
isFavor: props.detail.isFavor,
@ -342,18 +337,22 @@ const sendLikeVideo = async (liveNum) => {
}
let sendLiveTime = null
const addContinuousClickLive = useContinuousClickLive()
console.log(addContinuousClickLive)
const dblclickLive = () => {
const img = document.createElement("img")
img.setAttribute("src", liveIcon[liveIndex % 4])
img.setAttribute("class", "live-icon")
document.querySelector(".star").appendChild(img)
liveIndex++
;((img) => {
setTimeout(() => {
let star = document.querySelector(".star")
star && star.removeChild(img)
}, 2000)
})(img)
debugger
// const img = document.createElement("img")
// img.setAttribute("src", liveIcon[liveIndex % 4])
// img.setAttribute("class", "live-icon")
// const starBox = document.querySelector(".star-btn")
// starBox.appendChild(img)
// liveIndex++
// ;((img) => {
// setTimeout(() => {
// starBox && starBox.removeChild(img)
// }, 2000)
// })(img)
addContinuousClickLive(".star-btn")
clearTimeout(sendLiveTime)
sendLiveTime = setTimeout(() => {
sendLikeVideo(liveIndex)
@ -443,6 +442,8 @@ defineExpose({
})
</script>
<style scoped lang="scss">
@import url("@/assets/css/live.css");
::v-deep .user-in-tip {
position: absolute;
top: 40px;
@ -522,20 +523,26 @@ defineExpose({
width: 64px;
height: 64px;
margin-left: 20px;
background: url(../../../assets/images/share1.png) no-repeat center;
background: url(../../../assets/images/share3.png) no-repeat center;
background-size: contain;
border-radius: 50%;
}
.star {
.star-btn {
width: 64px;
height: 64px;
position: relative;
background: url(../../../assets/images/h-like.png) no-repeat center;
background-size: 52px 52px;
margin-left: 20px;
&.active {
background: url(../../../assets/images/h-like1.png) no-repeat center;
background: none;
margin-right: 10px;
.star {
width: 100%;
height: 100%;
background: url(../../../assets/images/h-like.png) no-repeat center;
background-size: 52px 52px;
margin-left: 20px;
&.active {
background: url(../../../assets/images/h-like1.png) no-repeat center;
background-size: 52px 52px;
}
}
span {
position: absolute;