fix: 新增圈子模块页面

This commit is contained in:
kaizheng(郑凯) 2025-01-31 22:11:22 +08:00
parent b19cdb11c9
commit f1370ded8b
12 changed files with 1721 additions and 12 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/assets/images/set.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -161,6 +161,36 @@ const routes = [
noLogin: true, // 不需要登录
},
},
{
path: "/circle",
name: "Circle",
component: () =>
import(/* webpackChunkName: "about" */ "../views/Circle/index.vue"),
meta: {
title: "圈子",
noLogin: true, // 不需要登录
},
},
{
path: "/circle/interact",
name: "CircleInteract",
component: () =>
import(/* webpackChunkName: "about" */ "../views/Circle/interact.vue"),
meta: {
title: "圈子",
noLogin: true, // 不需要登录
},
},
{
path: "/circle/set",
name: "CircleSet",
component: () =>
import(/* webpackChunkName: "about" */ "../views/Circle/set.vue"),
meta: {
title: "圈子",
noLogin: true, // 不需要登录
},
},
{
path: "/:pathMatch(.*)*",
redirect: "/",

View File

@ -9,7 +9,7 @@ export default createStore({
token:
process.env.NODE_ENV !== "development"
? terminalType === "Browser" && localStorage.getItem("token")
: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiYWNrZW5kVXNlciI6IntcImNsaWVudFR5cGVcIjo0LFwiY29va2llXCI6XCJiNDhhOTk5NWZiZjJlMWYxYzk0YzExZWUyODY1YzJmMFwiLFwiaW1nVXJsXCI6XCJodHRwczovL3RoaXJkd3gucWxvZ28uY24vbW1vcGVuL3ZpXzMyL3Q1TzZidFZsaWJDZWhveHRaV1hjNEFZZWZwOTJweEdXb3JDTnNrQm82aExzajNNSmNsa1BweW9JMGQ1T1c1UURzcWZhblM1SWRpY2ZQZkV2WXZRNlM3dVEvMTMyXCIsXCJyZUNvb2tpZVwiOlwiMzREMUMxRDc2RDg5MjAyRTMxMEExRUJEQjEwOTMzMzlcIixcInVzZXJJZFwiOlwiY3NodDAwM1wiLFwidXNlck5hbWVcIjpcImNzaHQwMDNcIn0iLCJleHAiOjE3Mzc1MzY3ODB9.9ATOVeQJ2ZUVIEmrFFq3JmtHbr2Vg9dP9VVa5XnYcOA",
: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJiYWNrZW5kVXNlciI6IntcImNsaWVudFR5cGVcIjo0LFwiY29va2llXCI6XCJjZjNjZWFlZjFmOGM5Y2I3MGE4OWQzMmM3ZTE5MmQyN1wiLFwiaW1nVXJsXCI6XCJodHRwczovL3RoaXJkd3gucWxvZ28uY24vbW1vcGVuL3ZpXzMyL3Q1TzZidFZsaWJDZWhveHRaV1hjNEFZZWZwOTJweEdXb3JDTnNrQm82aExzajNNSmNsa1BweW9JMGQ1T1c1UURzcWZhblM1SWRpY2ZQZkV2WXZRNlM3dVEvMTMyXCIsXCJyZUNvb2tpZVwiOlwiNDQ3MTM3QUJGMjY5NEFDMDYzMTI2NDdENjQ5Nzk0N0FcIixcInVzZXJJZFwiOlwiY3NodDAwM1wiLFwidXNlck5hbWVcIjpcImNzaHQwMDNcIn0iLCJleHAiOjE3Mzg0MDUxNzV9.pCk-eYx_RXL23oFW9LMfJ3Gp76dzJVjVuU1tFqFoHE0",
userInfo:
process.env.NODE_ENV !== "development"
? terminalType === "Browser" && localStorage.getItem("userInfo")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
import { ref, onMounted, nextTick } from "vue";
import { useStore } from "vuex";
import { queryLiveHisMsg, queryLiveTgHisMsg } from "@/api/video";
import BScroll from "@better-scroll/core";
import PullDown from "@better-scroll/pull-down";
import MouseWheel from "@better-scroll/mouse-wheel";
import useDisableScroll from "@/hooks/useDisableScroll";
BScroll.use(PullDown);
BScroll.use(MouseWheel);
export default function useChatData({ id, className, isTg }) {
const { addScrollEvent } = useDisableScroll();
const store = useStore();
const hasNext = ref(true);
let bs = ref();
let firsLoad = ref("");
const isPullingDown = ref(false);
const msgListRef = ref();
const getLiveHisMsg = async () => {
if (isPullingDown.value) return;
isPullingDown.value = true;
let preHisHeight
let ret = isTg
? await queryLiveTgHisMsg({
id: id,
lastId: store.state.interactMsgObj.tgInteractMsgList[0]?.id,
size: 20,
status: 1,
})
: await queryLiveHisMsg({
id: id,
lastId: store.state.interactMsgObj.userInteractMsgList[0]?.id,
size: 20,
status: 1,
});
if (ret.code === 0) {
firsLoad.value = true
let list = ret.data.list || []
hasNext.value = ret.data.hasNext;
preHisHeight = msgListRef.value.clientHeight;
store.commit('setInteractMsg', {
type: 2,
isTg,
hasNext: hasNext.value,
data: list
})
if (!hasNext.value) {
bs.value && bs.value.closePullDown();
}
}
isPullingDown.value = false;
finishPullDown(preHisHeight);
};
const finishPullDown = (preHisHeight) => {
bs.value && bs.value.finishPullDown();
nextTick(() => {
bs.value && bs.value.refresh();
if (!firsLoad.value) {
bs.value && bs.value.scrollTo(0, bs.value.maxScrollY, 0);
} else {
const curHisHeight = msgListRef.value.clientHeight;
const toY = preHisHeight - curHisHeight;
preHisHeight && bs.value && bs.value.scrollTo(0, toY, 0);
}
});
};
let wrapper;
onMounted(async () => {
wrapper = document.querySelector(
className ? className : ".interact-scroll"
);
console.log(".wrapper", wrapper);
bs.value = new BScroll(wrapper, {
scrollY: true,
bounceTime: 0,
disableMouse: false,
disableTouch: false,
useTransition: false,
pullDownRefresh: {
threshold: 0,
stop: 0,
},
bounce: {
top: true,
bottom: false,
},
preventDefault: false,
mouseWheel: {
speed: 20,
invert: false,
easeTime: 300,
},
});
bs.value && bs.value.refresh();
bs.value.on("pullingDown", getLiveHisMsg);
bs.value.autoPullDownRefresh();
addScrollEvent(className);
});
return {
isPullingDown,
msgListRef,
hasNext,
bs,
};
}

145
src/views/Circle/index.vue Normal file
View File

@ -0,0 +1,145 @@
<template>
<div>
<Nav title="直播视频"></Nav>
<div class="circle-base-info">
<img src="" class="banner">
<div class="circle-name">
<h3>独立圈子</h3>
<span>中风险</span>
</div>
<div class="tg-info">
<div class="flex">
<img class="photo" src="" alt="" srcset="">
<label>六七</label>
</div>
<span>名字</span>
</div>
</div>
<div class="introduce">
<h4>交易圈简介</h4>
<p>1.奥斯卡的骄傲卡的萨克反馈说发的是个电视柜</p>
<p>2.水电费克里斯发的是个发的是水电费快乐时光反馈说</p>
<p>3.水电费克里斯发的是个发的是水电费快乐时光反馈说</p>
</div>
<div class="introduce">
<h4>使用人群</h4>
<p>零基础小白用户</p>
</div>
<div class="tip">风险提示投资顾问提供的观点和投资建议仅供参考不作为客户投资决策依据客户需独立做出投资决策风险自担市场有风险投资须谨慎</div>
<div class="footer">
<div class="buy-list">
<ul>
<li><img src="https://gips2.baidu.com/it/u=1651586290,17201034&fm=3028&app=3028&f=JPEG&fmt=auto&q=100&size=f600_800"></li>
<li><img src="https://gips2.baidu.com/it/u=1651586290,17201034&fm=3028&app=3028&f=JPEG&fmt=auto&q=100&size=f600_800"></li>
<li><img src="https://gips2.baidu.com/it/u=1651586290,17201034&fm=3028&app=3028&f=JPEG&fmt=auto&q=100&size=f600_800"></li>
</ul>
<p><span>谁谁</span>正在火热抢购中</p>
</div>
<div class="sub">
<button>立即开通</button>
</div>
</div>
</div>
</template>
<script setup>
import Nav from "@/components/NavBar.vue";
</script>
<style lang="scss" scoped>
.circle-base-info{
padding: 32px 0;
margin: 0 32px;
border-bottom: 1px solid rgba(245, 245, 245, 0.7);
.banner{
height: 422px;
width: 100%;
}
.circle-name{
display: flex;
justify-content: space-between;
line-height: 96px;
h3{
font-size: 32px;
}
span{
font-size: 24px;
}
}
.tg-info{
display: flex;
justify-content: space-between;
align-items: center;
.flex{
display: flex;
align-items: center;
}
img{
width: 48px;
height: 48px;
border-radius: 50%;
}
label {
font-size: 28px;
margin-left: 8px;
}
span{
font-size: 28px;
}
}
}
.introduce{
text-align: left;
padding: 0 32px;
margin-bottom: 20px;
h4 {
font-size: 32px;
padding: 24px 0;
}
p{
font-size: 28px;
line-height: 40px;
}
}
.tip{
padding: 32px 32px 200px 32px;
background: #f5f6fa;
font-size: 24px;
color: #9aa4b6;
line-height: 36px;
}
.footer{
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0px -4px 15px #eff2f1;
background: #fff;
.buy-list{
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: #999;
padding: 20px 0;
ul{
display: flex;
}
img {
width: 40px;
height: 40px;
border-radius: 50%;
}
}
button {
width: 100%;
height: 88px;
background-image: linear-gradient(270deg, #2e78fa 0%, #45acff 100%);
font-weight: 500;
font-size: 32px;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,134 @@
<template>
<Nav title="圈子互动">
<img class=set src="@/assets/images/set.png" @click="router.push('./set')"/>
</Nav>
<van-tabs v-model:active="active" line-width="0.4rem">
<van-tab title="互动" :name="0">
<ChatFrame/>
</van-tab>
<van-tab title="老师" :name="1">
</van-tab>
<van-tab title="私聊" :name="2">
</van-tab>
<van-tab title="精选" :name="3">
</van-tab>
</van-tabs>
<div class="input-area">
<input
type="text"
maxlength="200"
@keyup.enter="sendMsg(1, { text })"
/>
<button
class="send"
:disabled="!text || sendTextLoading"
@click="sendMsg(1, { text })"
>
发送
</button>
</div>
</template>
<script setup>
import { ref } from "vue"
import Nav from "@/components/NavBar.vue";
import ChatFrame from "./components/ChatFrame.vue";
import { useRouter } from "vue-router";
const active = ref(0)
const router = useRouter();
</script>
<style lang="scss" scoped>
.set{
width: 50px;
}
::v-deep .van-nav-bar {
background: #fff;
}
.van-tabs{
height: calc(100% - 200px);
::v-deep .van-tabs__content {
height: calc(100% - 156px);
.van-tab__panel {
height: 100%;
}
}
}
.input-area {
display: flex;
align-items: center;
background: #fff;
padding: 20px 20px;
input {
height: 64px;
background: #9aa4b61a;
border-radius: 32px;
font-weight: 500;
font-size: 24px;
letter-spacing: 0;
line-height: 24px;
padding: 0 16px;
flex: 1;
color: #666;
margin: 0 20px;
}
input::placeholder,
input::-webkit-input-placeholder {
color: #666; /* 设置颜色为灰色 */
}
button.send {
height: 64px;
padding: 0 20px;
font-size: 28px;
border-radius: 32px;
color: #fff;
background-image: linear-gradient(270deg, #2e78fa 0%, #45acff 100%);
&[disabled] {
opacity: 0.3;
}
}
.share {
width: 64px;
height: 64px;
margin-left: 20px;
background: url(../../assets/images/share1.png) no-repeat center;
background-size: contain;
border-radius: 50%;
}
.star {
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-size: 52px 52px;
}
span {
position: absolute;
top: 0;
right: 0;
min-width: 20px;
transform: translateX(18px) translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
flex-direction: column;
border-radius: 13px 13px 13px 4px;
background: linear-gradient(
128deg,
#fff2dc -25.5%,
#ffd9a7 89.14%,
#605a52 89.14%
);
color: rgb(142, 81, 5);
font-size: 18px;
font-style: normal;
font-weight: 500;
}
}
}
</style>

45
src/views/Circle/set.vue Normal file
View File

@ -0,0 +1,45 @@
<template>
<Nav title="圈子设置"></Nav>
<div class="item">
<label>消息提醒</label>
<van-switch v-model="checked" size="16px"/>
</div>
<div class="item">
<div class="flex">
<label>到期时间</label>
<span>2023/10/22</span>
</div>
<p>立即续费<van-icon name="arrow"/></p>
</div>
<div class="item">
<label>客服电话</label>
<a href="tel:16888888888">16888888888</a>
</div>
</template>
<script setup>
import Nav from "@/components/NavBar.vue";
</script>
<style scoped lang='scss'>
::v-deep .van-nav-bar {
background: #fff;
}
.item{
display: flex;
padding: 32px 0;
margin: 0 32px;
justify-content: space-between;
font-size: 28px;
border-bottom: 1px solid rgba(156,156,156,0.1);
.flex{
display: flex;
}
label{
margin-right: 20px;
}
a{
color: #03a9f4;
}
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model:loading="loading"
v-model:error="loadError"
:finished="finished"
finished-text="没有更多了"
error-text="加载错误,请点击重新加载"
@load="onLoad"
>
<CircleItem/>
</van-list>
</van-pull-refresh>
</template>
<script setup>
import { ref, defineProps } from "vue";
import { queryLivePlan } from "@/api/index";
import CircleItem from "./components/CircleItem.vue";
const props = defineProps({
id: {
type: String,
},
});
console.log(props);
const list = ref([]);
const loading = ref(false);
const loadError = ref(false);
const finished = ref(false);
const refreshing = ref(false);
const current = ref(1);
const onRefresh = () => {
current.value = 1;
//
finished.value = false;
//
// loading true
loading.value = true;
onLoad();
};
const onLoad = async () => {
//
let ret = await queryLivePlan({
type: 3,
size: 10,
current: current.value,
lastAuditTime:
refreshing.value || !list.value.length
? ""
: list.value[list.value.length - 1].auditTime,
lastStartTime:
refreshing.value || !list.value.length
? ""
: list.value[list.value.length - 1].startTime,
lastStatus:
refreshing.value || !list.value.length
? ""
: list.value[list.value.length - 1].liveStatus,
advisorId: props.id,
lastIsRecommend:
refreshing.value || !list.value.length
? ""
: list.value[list.value.length - 1].isRecommend,
lastLibCount:
refreshing.value || !list.value.length
? ""
: list.value[list.value.length - 1].libraryList
? list.value[list.value.length - 1].libraryList.length
: 0,
});
if (ret.code === 0) {
if (refreshing.value) {
list.value = [];
refreshing.value = false;
}
current.value++;
list.value = list.value.concat(ret.data.list);
finished.value = !ret.data.hasNext;
} else {
loadError.value = true;
}
loading.value = false;
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,84 @@
<template>
<div class="circle-item">
<img class="cover" src="" alt="">
<div class="circle-info">
<h3>主力行为读心术</h3>
<p>收款方都开始到放款是否都开始开发三方的考试费打开时</p>
<div class="price">
<div class="tg-info">
<img class="photo" src="" alt="" srcset="">
<span>六七</span>
</div>
<!-- <h5><strong>19333</strong>/6个月</h5> -->
<h5>免费</h5>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
<style lang="scss" scoped>
.circle-item{
display: flex;
.cover{
display: block;
width: 168px;
height: 200px;
margin-right: 20px;
}
.circle-info{
display: flex;
flex-direction: column;
justify-content: space-between;
h3{
line-height: 58px;
font-size: 36px;
text-align: left;
}
p{
font-size: 28px;
color: #999;
line-height: 36px;
text-align: left;
overflow:hidden;
text-overflow:ellipsis;
display:-webkit-box;
-webkit-line-clamp:2; //
-webkit-box-orient:vertical; //
}
.price{
display: flex;
justify-content: space-between;
.tg-info{
display: flex;
align-items: center;
img{
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 4px;
}
span{
font-size: 28px;
color: #999;
}
}
h5{
color: #2196f3;
font-size: 28px;
strong {
font-size: 36px;
}
}
}
}
}
</style>

View File

@ -48,17 +48,8 @@
:title="item.showName"
>
<LivePlayList v-if="active === 3" :id="route.query.id" />
<CourseList
:list="list"
v-else-if="active === 32"
:id="route.query.id"
/>
<ShotVideoList v-else-if="active === 35" :id="route.query.id" />
<!-- <ServerPackList
:list="list"
v-else-if="active === 1"
:id="route.query.id"
/> -->
<CircleList v-else-if="active === 1"/>
<Empty text="暂无内容" v-else />
</van-tab>
</van-tabs>
@ -72,7 +63,6 @@ import { useRoute } from "vue-router";
// import Nav from "@/components/NavBar.vue";
import LivePlayList from "./components/LivePlayList.vue";
// import ServerPackList from "./components/ServerPackList";
import CourseList from "./components/CourseList";
import ShotVideoList from "./components/ShotVideoList.vue";
import { queryTgInfo, queryTgTab } from "@/api/tg";
import { queryNotPlay, subLiveVideo } from "@/api/video";
@ -80,6 +70,7 @@ import { queryNotPlay, subLiveVideo } from "@/api/video";
import { showToast } from "vant";
// import useShieldConfig from "@/hooks/useShieldConfig";
import Empty from "@/components/Empty.vue";
import CircleList from "./components/CircleList.vue";
// const $shieldConfig = useShieldConfig();
@ -101,6 +92,12 @@ const getTgTab = async () => {
let ret = await queryTgTab({ id: route.query.id });
if (ret.code === 0) {
tabList.value = ret.data;
tabList.value.push({
"productType": 1,
"showName": "圈子",
"sort": 3,
"status": 1
})
}
};