删除无用组件

This commit is contained in:
谢宇宁
2026-10-09 16:39:38 +07:00
parent c957b45bad
commit f1824c6e83
53 changed files with 0 additions and 11518 deletions
@@ -1,105 +0,0 @@
<template>
<div class="accountCredentialsPop">
<!-- 账号凭证弹窗 -->
<van-overlay v-model:show="state.showActTip" z-index="2001">
<div class="wrapper">
<div class="actTipBox">
<div class="title">防止账号丢失</div>
<div class="info">1.受行业因素,请绑定手机或保存好您的账号凭证(ios用户务必保存)</div>
<div class="info">2.如账号不小心遗失,可再设置里通过凭证找回账号</div>
<div class="actTipBtn flex-center-center">
<div class="btn" @click="saved">我已保存</div>
<div class="btn pink" @click="$router.push('/accountCertificate')">立即保存</div>
</div>
<div class="close" @click="close">
<SvgIcon name="close" class="closeIcon" />
</div>
</div>
</div>
</van-overlay>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { useAppStore } from '@/stores/app';
import { useUserStore } from '@/stores/user';
const appStore = useAppStore();
const userStore = useUserStore();
const state = reactive({
showActTip: !appStore.isCertificate && !userStore.isSessionCertificate,
});
const saved = () => {
appStore.setCertificate(true);
state.showActTip = false;
};
const close = () => {
userStore.setSessionCertificate(true);
state.showActTip = false;
};
</script>
<style lang="scss" scoped>
.accountCredentialsPop {
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
.actTipBox {
width: 308px;
height: 258px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
position: relative;
background-color: rgb(255, 255, 255);
padding: 16px 25px 0;
border-radius: 6px;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
margin-bottom: 24px;
font-weight: bold;
}
.info {
width: 100%;
font-size: 14px;
color: rgb(38, 38, 38);
padding: 0 10px;
line-height: 22px;
margin-bottom: 15px;
}
.actTipBtn {
position: absolute;
bottom: 40px;
.btn {
width: 129px;
height: 38px;
line-height: 38px;
font-size: 14px;
border-radius: 10px;
text-align: center;
color: rgb(153, 153, 153);
border: 1.5px solid rgb(219, 219, 219);
}
.pink {
background-color: $themeColor;
color: white;
border: unset;
margin-left: 10px;
}
}
.close {
position: absolute;
bottom: -60px;
left: 50%;
transform: translate(-50%, 0);
.closeIcon {
height: 36px;
width: 36px;
}
}
}
}
}
</style>
-133
View File
@@ -1,133 +0,0 @@
<template>
<div class="sharePopup">
<van-popup v-model:show="state.isRewardShow" :style="{ backgroundColor: 'rgba(0, 0, 0, 0)' }">
<div class="popupBox">
<LazyImg class="cover" :src="state.videoInfo?.horizontalCover || state.videoInfo?.verticalCover || state.videoInfo?.cover" />
<div class="bottom"></div>
<SvgIcon name="title2" class="title" />
<div class="qrcode">
<qrcode-vue :value="state.qrCodeUrl" :size="90" level="L" />
</div>
<div class="desc">{{ state.videoInfo?.title }}</div>
</div>
<div class="btnBox flex-center-between">
<div class="btn" @click="showToast('请手动截图保存')">保存图片</div>
<div class="btn copyBtn" @click="copyLink">复制链接</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { reactive,nextTick } from 'vue';
import { showToast } from 'vant';
import QrcodeVue from 'qrcode.vue';
import { useUserStore } from '@/stores/user';
import useClipboard from 'vue-clipboard3';
const { toClipboard } = useClipboard();
const userStore = useUserStore();
const userInfo: any = userStore.userInfo;
const props = defineProps<{
videoInfo?: any;
}>();
const state = reactive({
isRewardShow: false,
qrCodeUrl: userInfo.promoteURL,
promotionCode: userInfo.promotionCode,
videoInfo: props.videoInfo,
})
const openPop = () => {
state.isRewardShow = true;
};
const copyLink = async () => {
try {
if (!state.qrCodeUrl) {
showToast('复制失败');
return;
}
await toClipboard(state.qrCodeUrl); //实现复制
showToast('复制成功');
} catch (e: any) {
console.log(e);
}
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.sharePopup {
background-color: rgba(255,255,255);
.popupBox {
background: rgba(255,255,255);
border-radius: 6px;
height: 271px;
width: 301px;
overflow: hidden;
position: relative;
.cover {
height: 191px;
width: 100%;
}
.bottom {
height: 109px;
width: 100%;
}
.title {
height: 35px;
width: 124.6px;
position: absolute;
left: 15px;
top: 15px;
}
.qrcode {
height: 90px;
width: 90px;
position: absolute;
left: 19px;
top: 147px;
border-radius: 6px;
// background: rgba(238,118,47);
// padding:0 5px 3px 0;
}
.desc {
position: absolute;
right: 17.3px;
bottom: 29px;
width: 157px;
height: 37px;
color: rgba(38, 38, 38);
font-size: 13px;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
.btnBox {
margin: 23px auto;
.btn {
height: 48px;
width: 144px;
border-radius: 24px;
text-align: center;
line-height: 48px;
background-color: white;
}
.copyBtn {
color: white;
background-color: $themeColor;
}
}
:deep(.van-popup) {
border-radius: 8px;
}
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
}
</style>
-93
View File
@@ -1,93 +0,0 @@
<template>
<div>
<div class="swiperContent2">
<swiper
:modules="modules"
:loop="true"
effect="coverflow"
slides-per-view="auto"
:centeredSlides="true"
:speed="300"
:autoplay="{ delay: 3000, disableOnInteraction: false }"
:coverflowEffect="{ rotate: 0, stretch: 0, depth: 150, modifier: 1 }"
:observer="true"
:observeParents="true"
:style="{ height }"
>
<swiper-slide v-for="(item, index) in props.list" :key="index" :data-index="index" @click="open(item)">
<LazyImg class="swiperImg" :src="item.horizontalCover || item.verticalCover" />
</swiper-slide>
</swiper>
</div>
</div>
</template>
<script setup lang="ts">
import { EffectCoverflow, Autoplay } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/swiper.min.css';
import { addVideoHistory, jumpAdv } from '@/utils';
import { HistoryVideoType } from '@/enums/app';
import { useRouter } from 'vue-router';
const router = useRouter();
const modules = [EffectCoverflow, Autoplay];
const props = withDefaults(
defineProps<{
list: any;
height?: string;
isShowIndicators?: boolean;
}>(),
{
list: () => <any>[],
height: '156px',
isShowIndicators: true,
},
);
const open = async (item: any) => {
if (item.kind == 1 || item.kind == 4) {
router.push({
path: '/novelDetailsPage',
query: {
id: item.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
},
});
} else if (item.kind == 5) {
router.push({
path: '/portrayPage',
query: {
id: item.id,
},
});
}
};
</script>
<style scoped lang="scss">
.swiperContent1 {
width: 600px;
margin-left: -130px;
}
.swiperContent2 {
// width: 100%;
// margin-left: -88px;
.swiper-slide {
width: 320px !important;
}
// .swiper-slide-visible {
// width: 320px !important;
// }
.swiperImg {
// width: 320px;
:deep(.animationImg) {
border-radius: 12px;
}
}
}
:deep(.swiper-slide) {
width: 320px !important;
}
</style>
-495
View File
@@ -1,495 +0,0 @@
<template>
<div class="adv_swiper">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="(item, index) in props.list">
<!-- <lazyImg class="cover" :src="item.cover" :key="item.id" />-->
<AnimationImg class="cover" :src="item.cover" :key="item.id" />
</div>
</div>
</div>
<!-- <div class="swiper">-->
<!-- <div class="swiper-wrapper">-->
<!-- <div class="swiper-slide" v-for="(item, index) in props.list">-->
<!-- <lazyImg-->
<!-- :src="item.cover"-->
<!-- :key="item.id"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="swiperBox">-->
<!-- <div class="carousel">-->
<!-- <div-->
<!-- class="carousel-item"-->
<!-- @touchstart="handleTouchstart"-->
<!-- @touchmove="handleTouchmove"-->
<!-- @touchend="handleTouchend"-->
<!-- v-for="(item, index) in state.items"-->
<!-- :key="index"-->
<!-- :style="{-->
<!-- transform: `scale(${item.scale}) translate(${item.translateX}px, ${item.translateY}px)`,-->
<!-- zIndex: item.zIndex,-->
<!-- width: `${item.width}px`,-->
<!-- height: `${item.height}px`,-->
<!-- }"-->
<!-- >-->
<!-- <DecryptImg-->
<!-- :src="props.list[index].cover"-->
<!-- :key="item.id"-->
<!-- :style="{ width: '100%', height: '100%' }"-->
<!-- :alt="'ImageError ' + (index + 1)"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
</template>
<script setup lang="ts">
import { jumpAdv } from '@/utils/index';
import { nextTick, reactive } from 'vue';
import Swiper from 'Swiper';
import 'swiper/swiper.min.css';
import AnimationImg from '@/components/AnimationImg/index.vue';
const props = withDefaults(
defineProps<{
list: [];
height?: string;
isShowIndicators?: boolean;
}>(),
{
list: () => [],
height: '156px',
isShowIndicators: true,
},
);
const state = reactive({
startX: 0, // 触摸开始时的X坐标
currentX: 0, // 当前轮播图的X坐标
currentIndex: 0,
items: [
{
width: 300,
height: 140,
scale: 1,
translateX: -25,
translateY: 0,
zIndex: 1,
},
{
width: 300,
height: 160,
scale: 1,
translateX: 0,
translateY: 0,
zIndex: 2,
},
{
width: 300,
height: 140,
scale: 1,
translateX: 25,
translateY: 0,
zIndex: 1,
},
],
});
const handleTouchstart = (event: any) => {
state.startX = event.touches[0].clientX;
};
const handleTouchmove = (event: any) => {
const dx = event.touches[0].clientX - state.startX; // 计算滑动距离
state.currentX += dx; // 更新轮播图位置
state.startX = event.touches[0].clientX; // 更新触摸位置
};
const handleTouchend = () => {
if (state.currentX < 0) {
next();
} else if (state.currentX > 0) {
previous();
}
state.currentX = 0; // 重置currentX
};
/**
* 轮播图下一张
*/
const next = () => {
state.currentIndex = (state.currentIndex + 1) % state.items.length;
// console.log(state.currentIndex);
// if (state.currentIndex == 0) {
// state.items = [
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: -25,
// translateY: 0,
// zIndex: 1,
// },
// {
// width: 300,
// height: 160,
// scale: 1,
// translateX: 0,
// translateY: 0,
// zIndex: 2,
// },
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: 25,
// translateY: 0,
// zIndex: 1,
// },
// ];
// } else if (state.currentIndex == 1) {
// state.items = [
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: 25,
// translateY: 0,
// zIndex: 1,
// },
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: -25,
// translateY: 0,
// zIndex: 1,
// },
// {
// width: 300,
// height: 160,
// scale: 1,
// translateX: 0,
// translateY: 0,
// zIndex: 2,
// },
// ];
// } else if (state.currentIndex == 2) {
// state.items = [
// {
// width: 300,
// height: 160,
// scale: 1,
// translateX: 0,
// translateY: 0,
// zIndex: 2,
// },
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: 25,
// translateY: 0,
// zIndex: 1,
// },
// {
// width: 300,
// height: 140,
// scale: 1,
// translateX: -25,
// translateY: 0,
// zIndex: 1,
// },
// ];
// }
};
const previous = () => {
state.currentIndex = (state.currentIndex - 1 + state.items.length) % state.items.length;
console.log(state.currentIndex);
if (state.currentIndex == 0) {
state.items = [
{
width: 300,
height: 140,
scale: 1,
translateX: -25,
translateY: 0,
zIndex: 1,
},
{
width: 300,
height: 160,
scale: 1,
translateX: 0,
translateY: 0,
zIndex: 2,
},
{
width: 300,
height: 140,
scale: 1,
translateX: 25,
translateY: 0,
zIndex: 1,
},
];
} else if (state.currentIndex == 1) {
state.items = [
{
width: 300,
height: 160,
scale: 1,
translateX: 0,
translateY: 0,
zIndex: 2,
},
{
width: 300,
height: 140,
scale: 1,
translateX: 25,
translateY: 0,
zIndex: 1,
},
{
width: 300,
height: 140,
scale: 1,
translateX: -25,
translateY: 0,
zIndex: 1,
},
];
} else if (state.currentIndex == 2) {
state.items = [
{
width: 300,
height: 160,
scale: 1,
translateX: 25,
translateY: 0,
zIndex: 0,
},
{
width: 300,
height: 160,
scale: 1,
translateX: -25,
translateY: 0,
zIndex: 1,
},
{
width: 300,
height: 160,
scale: 1,
translateX: 0,
translateY: 0,
zIndex: 2,
},
];
}
};
nextTick(() => {
// props.list;
// var mySwiper = document.querySelector('.swiper');
// debugger
// var mySwiper = new Swiper('.swiper', {
// autoplay: true,//可选选项,自动滑动
// });
var swiper = new Swiper('.swiper-container', {
slidesPerView: 3,
centeredSlides: true,
loop: true,
});
});
</script>
<style lang="scss" scoped>
.adv_swiper {
position: relative;
height: 100%;
//width: 165px;
}
//.swiper-container {
// width: 100%;
// height: 100%;
//}
.swiper-container {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
transition: 300ms;
transform: scale(0.8);
.cover{
width: 240px;
height: 120px;
border-right: 12px;
:deep(.animationImg){
border-right: 12px;
}
}
}
.swiper-slide-active,.swiper-slide-duplicate-active{
z-index: 1;
transform: scale(2);
}
//.swiper-slide-active{
// width: 320px !important;
// height: 160px;
// flex-shrink: 0;
// left: 29px;
//}
//
//.swiper-slide-prev{
// width: 262.5px;
// height: 140px;
// flex-shrink: 0;
// //left: 0;
//}
//
//.swiper-slide-duplicate-next{
// width: 262.5px;
// height: 140px;
// flex-shrink: 0;
// //right: 0;
//}
//.swiper-slide{
// width: 320px;
// height: 160px;
//}
//
//:deep(.swiper-slide-active){
// width: 320px;
// height: 160px;
//}
//:deep(.van-swipe__indicator--active) {
// width: 17px;
// height: 4px;
// border-radius: 2px;
//}
//:deep(.van-swipe__indicators) {
// left: 85%;
//}
//.adv_swiper {
// overflow: hidden;
//
// .swiperBox {
// height: 180px;
// padding: 0px 16px;
// .carousel {
// position: relative;
// width: 100%;
// height: 160px;
// }
// .bgIMage {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 210px;
// background-repeat: no-repeat;
// background-size: cover;
// }
// .bgIMage::before {
// content: '';
// position: absolute;
// bottom: 0;
// left: 0;
// width: 100%;
// height: 210px;
// background: linear-gradient(180deg, rgba(26, 26, 26, 0) 0%, #1b1b1b 80%);
// background-repeat: no-repeat;
// background-size: cover;
// }
// .carousel-item {
// position: absolute;
// // width: 100% !important;
// height: 100%;
// bottom: 0;
// left: 6px;
// border-radius: 8px;
// transition: transform 1s, width 1s, height 1s;
// :deep(.van-image__img) {
// border-radius: 8px;
// }
// :deep(.img-decypt) {
// z-index: 99999 !important;
// }
// }
// .carousel-item::after {
// content: '';
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
// width: 40px;
// height: 40px;
// //background-image: url('~@/assets/png/originalPlay.png');
// background-repeat: no-repeat;
// background-size: 100% 100%;
// }
// .carousel-indicators {
// position: absolute;
// left: 0;
// bottom: -40px;
// width: 100%;
// text-align: center;
// }
// .carousel-indicators span {
// display: inline-block;
// width: 5px;
// height: 5px;
// margin: 0 5px;
// background: #94d6da;
// opacity: 0.5;
// border-radius: 50%;
// }
// .carousel-indicators span.active {
// width: 38px;
// border-radius: 20px;
// background: #94d6da;
// opacity: 1;
// }
// }
//
// .adv-container {
// width: 100%;
// overflow: hidden;
// .adv-swiper-slide {
// width: 100%;
// border-radius: 6px;
// overflow: hidden;
// }
// }
//}
</style>
-57
View File
@@ -1,57 +0,0 @@
<template>
<div class="adv_swiper">
<div class="adv-container" v-if="props.list.length > 0">
<van-swipe class="my-swipe" :show-indicators="isShowIndicators" :autoplay="autoplay" indicator-color="white">
<van-swipe-item
@click="jumpAdv(item, 1)"
v-for="item in props.list"
:key="item.id"
:style="{ height: height }"
class="adv-swiper-slide"
>
<DecryptImg v-if="item.cover" :src="item.cover" />
</van-swipe-item>
</van-swipe>
</div>
</div>
</template>
<script setup lang="ts">
import { jumpAdv } from '@/utils/index';
const props = withDefaults(
defineProps<{
list: Types.App.AdsInfoListItem[];
height?: string;
isShowIndicators?: boolean;
autoplay?: number;
}>(),
{
list: () => [],
height: '156px',
isShowIndicators: false,
autoplay: 5000,
},
);
</script>
<style lang="scss" scoped>
:deep(.van-swipe__indicator--active) {
width: 17px;
height: 4px;
border-radius: 2px;
}
:deep(.van-swipe__indicators) {
left: 85%;
}
.adv_swiper {
overflow: hidden;
.adv-container {
width: 100%;
overflow: hidden;
.adv-swiper-slide {
width: 100%;
border-radius: 6px;
overflow: hidden;
}
}
}
</style>
-116
View File
@@ -1,116 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper">
<div class="content">
<div class="title">解锁会员</div>
<h2>免费次数不足~</h2>
<p>
充值
<span>VIP</span>
{{ state.tip }}
</p>
<div class="btn-box flex-center-between">
<div class="btn share" @click.stop="$router.push('/promotionCode')">分享邀请</div>
<div class="btn" @click.stop="joinMembership">立即充值</div>
</div>
</div>
<SvgIcon name="close" class="close" @click="emit('close')" />
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue';
import { useRouter } from 'vue-router';
const props = defineProps<{
videoInfo?: Types.Video.VInfosItem;
isOpen: boolean;
tip?: string;
}>();
const emit = defineEmits(['close']);
const isShow = computed(() => {
return props.isOpen;
});
const router = useRouter();
const state = reactive({
tip: props.tip || '获取脱衣次数',
});
/**
* @description 开通会员
*/
const joinMembership = () => {
router.push({
path: '/recharge',
query: {
name: 'member',
id: props.videoInfo?.id,
title: props.videoInfo?.title || '无标题',
},
});
};
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.content {
height: 187px;
width: 312px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
margin: 0 auto;
text-align: center;
border-radius: 6px;
padding-top: 19px;
box-sizing: border-box;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
margin-bottom: 14px;
font-weight: bold;
}
h2 {
font-size: 14px;
color: rgb(153, 153, 153);
}
p {
font-size: 14px;
color: rgb(38, 38, 38);
line-height: 26px;
margin-top: 10px;
span {
color: rgb(255, 160, 1);
}
}
.btn-box {
padding: 0 22px;
.btn {
height: 38px;
width: 129px;
text-align: center;
line-height: 38px;
border-radius: 10px;
font-size: 14px;
background-color: $themeColor;
color: white;
margin-top: 26px;
}
.share {
border: 1.5px solid rgb(153, 153, 153);
background-color: unset;
color: rgb(153, 153, 153);
}
}
}
.close {
height: 32px;
width: 32px;
margin-top: 20px;
}
}
</style>
-117
View File
@@ -1,117 +0,0 @@
<template>
<div class="announcementPopup">
<div v-for="(item, index) in state.announList" :key="item.id">
<van-overlay :show="item.type == 0" z-index="2001" @click="close(index)">
<div class="wrapper">
<div class="title">APP公告</div>
<div class="content">
<p>
{{ item.content }}
</p>
</div>
<div class="btn">确认</div>
</div>
</van-overlay>
<van-overlay :show="item.type == 1" z-index="2001" @click="close(index)">
<div class="imgWrapper">
<lazyImg @click.stop="jumpAdv(item)" class="imgBox" :src="item.cover" />
<div class="close"></div>
</div>
</van-overlay>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { jumpAdv } from '@/utils/index';
const props = withDefaults(
defineProps<{
announList: Types.App.AnnounListItem[];
}>(),
{
announList: () => [],
},
);
const emit = defineEmits(['close']);
const state = reactive({
announList: <Array<Types.App.AnnounListItem>>props.announList,
});
const close = (index: number) => {
if (index == 0) {
emit('close');
}
state.announList.pop();
};
</script>
<style lang="scss" scoped>
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.9);
}
.announcementPopup {
height: 100%;
width: 100%;
position: relative;
.wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 265px;
width: 308px;
border-radius: 8px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
box-sizing: border-box;
padding-top: 16px;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
font-weight: bold;
}
.content {
padding: 0 24px 0;
height: 150px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
p {
font-size: 12px;
color: rgb(153, 153, 153);
line-height: 20px;
margin: 15px 0;
}
}
.btn {
height: 38px;
width: 227px;
background-color: rgb(255, 160, 1);
text-align: center;
line-height: 38px;
color: white;
font-size: 16px;
border-radius: 10px;
margin: 20px auto 0;
}
}
.imgWrapper {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.imgBox {
height: 380px;
width: 285px;
}
.close {
height: 36px;
width: 36px;
background: url('./../../assets/images/png/closeDialog.png') no-repeat;
background-size: 100% 100%;
margin: 10px auto;
}
}
}
</style>
-124
View File
@@ -1,124 +0,0 @@
<template>
<div class="ApplicationPop">
<van-popup
v-model:show="state.showApplication"
round
position="bottom"
:style="{ height: '50%', backgroundColor: 'rgb(0, 0, 0)' }"
>
<div class="popupBox flex-center-center">
<div class="logoBox">
<img src="@/assets/images/png/homeLogo.png" />
</div>
<div class="topSite">
<div class="line"></div>
<h1>官方中文站</h1>
<div class="line"></div>
</div>
<div class="qrCode flex-center-center">
<div class="qrCodeBox flex-center-center">
<qrcode-vue id="qrCode" class="qrCodeImg" v-if="state.qrUrl" :value="state.qrUrl" level="L" />
<van-loading size="50px" type="spinner" color="#c0c9ea" v-else />
</div>
</div>
<div class="tipText">扫码进行APP下载获取极致观影体验</div>
<div class="tipText">官方域名:{{ state.url }}</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { reactive, nextTick } from 'vue';
import { useConfigStore } from '@/stores/config';
import UserApi from '@/service/user';
import { getDevType } from '@/utils';
const configStore = useConfigStore();
const state = reactive({
showApplication: false,
url: configStore.sourceInfo.LAND,
qrUrl: <string>'',
showIos: false,
});
nextTick(async () => {
/**
* 获取落地页链接
*/
if (getDevType() == 'ios') {
let ret: any = await UserApi.queryCertificate();
state.qrUrl = ret.content;
} else {
let ret: any = await UserApi.queryCertificate();
state.qrUrl = ret.content;
}
});
const openPop = () => {
state.showApplication = true;
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.ApplicationPop {
.popupBox {
width: 100%;
height: 100%;
color: $titleColor;
text-align: center;
flex-direction: column;
.topSite {
display: flex;
justify-content: space-between;
align-items: center;
h1 {
font-size: 16px;
font-weight: 600;
margin: 0 6px;
}
.line {
width: 32px;
height: 2px;
background-color: $titleColor;
}
}
.logoBox {
width: 130px;
height: 45px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 4px;
img {
width: 100%;
height: 100%;
}
}
.qrCode {
width: 165px;
height: 165px;
border: 3px solid $themeColor;
border-radius: 8.92px;
margin: 12px 0;
.qrCodeBox {
background-color: white;
width: 150px;
height: 150px;
border-radius: 10px;
.qrCodeImg {
width: 140px !important;
height: 140px !important;
}
}
}
.tipText {
font-size: 14px;
line-height: 22px;
font-weight: 500;
}
}
}
</style>
-318
View File
@@ -1,318 +0,0 @@
<template>
<van-popup v-model:show="isShow" position="bottom" z-index="2001" @close="emit('close')">
<div class="wrapper">
<div class="header flex-center-between">
<div class="left">
<div class="title"></div>
<div class="total">{{ videoInfo.commentCount }}条评论</div>
</div>
<div class="close" @click="emit('close')"></div>
</div>
<div class="main overflow-y-auto">
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh">
<div class="commentBox flex-center-between" v-for="item in state.list" :key="item.id">
<div class="commentInfo">
<lazyImg class="avatar" round :src="item.userPortrait" />
<div class="info">
<div class="name">
{{ item.userName }}
<SvgIcon class="vipIcon" name="vip" v-if="item.level" />
<SvgIcon class="genderIcon" name="male" v-if="item.gender == 'male'" />
<SvgIcon class="genderIcon" name="female" v-else />
</div>
<div class="text">{{ item.content }}</div>
<div class="address">{{ item.city }}•{{ moment(item.createdAt).format('YYYY-MM-DD') }}</div>
</div>
</div>
<div class="zanBox">
<svg-icon name="zan" class="zanIcon" v-if="item.isLike" @click="clickZan(item)" />
<svg-icon name="unzan" class="zanIcon" v-else @click="clickZan(item)" />
<div class="zanNumber">{{ item.likeCount }}</div>
</div>
</div>
</PullRefresh>
</div>
<div class="footer">
<div class="addComment flex-center-center">
<van-field
v-model="state.content"
autosize
rows="1"
type="textarea"
placeholder="留下你的精彩评论吧~"
class="commentInputBox"
></van-field>
<SvgIcon name="commentIcon" class="commentIcon" @click="submintComment" />
</div>
</div>
</div>
</van-popup>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue';
import CommunityApi from '@/service/community';
import { showToast } from 'vant';
import moment from 'moment';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
isOpen: boolean;
}>();
const state = reactive({
content: '',
pageNumber: 1, // 一级评论页码
pageSize: 30, // 一级评论一页的数量
list: <Array<Types.Community.CommentItem>>[], // 一级评论列表
});
// 上拉加载及下拉刷新参数
const moreParams = reactive({
loading: true,
refreshing: false,
finished: false,
isNoData: false,
error: false,
isHigehtMax: false,
});
const emit = defineEmits(['addComment', 'close']);
const isShow = computed(() => {
return props.isOpen;
});
// 获取评论数据
const getFirstList = async (type: string) => {
try {
let req = {
objID: String(props.videoInfo.id),
curTime: new Date().toISOString(),
pageNumber: String(state.pageNumber),
pageSize: String(state.pageSize),
};
let res: any = await CommunityApi.getCommentList(req);
moreParams.loading = false;
moreParams.refreshing = false;
let list = res.list || [];
if (type === 'refresh') {
state.list = list;
} else {
state.list = state.list.concat(list);
}
if (state.pageNumber == 1 && state.list.length == 0) {
moreParams.isNoData = true;
}
if (!res.hasNext) {
moreParams.finished = true;
}
} catch (e) {
moreParams.loading = false;
moreParams.error = true;
}
};
// 点赞
const clickZan = async (item: Types.Community.CommentItem) => {
try {
let req = {
type: 'comment',
objID: item.id,
};
if (!item.isLike) {
await CommunityApi.zan(req);
item.isLike = true;
item.likeCount++;
showToast('点赞成功');
} else {
await CommunityApi.cancleZan(req);
item.isLike = false;
item.likeCount--;
showToast('取消成功');
}
} catch (e) {
showToast(`${item.isLike ? '取消失败' : '点赞失败'}`);
}
};
// 确认提交评论
const submintComment = async () => {
try {
let req = {};
req = {
objID: props.videoInfo.id,
level: 1,
content: state.content,
};
let res: any = await CommunityApi.sendComment(req);
state.content = ''; // 评论输入框内容清空
state.list.unshift(res);
emit('addComment');
showToast('评论成功');
} catch (e) {
console.log(e);
}
};
const onLoad = () => {
moreParams.loading = true;
state.pageNumber++;
getFirstList('');
};
const onRefresh = () => {
state.pageNumber = 1;
moreParams.refreshing = true;
moreParams.finished = false;
getFirstList('refresh');
};
const getList = () => {
state.pageNumber = 1;
moreParams.loading = false;
moreParams.finished = false;
state.list = <Array<Types.Community.CommentItem>>[];
getFirstList('');
};
defineExpose({
getList,
moreParams,
});
</script>
<style lang="scss" scoped>
:deep(.van-popup) {
background: transparent !important;
}
:deep(.van-cell) {
border: 1px solid rgb(112, 112, 112);
border-radius: 5px;
padding: unset;
overflow: hidden;
background: transparent;
.van-field__body {
padding-left: 10px;
.van-field__control {
color: $titleColor;
font-weight: 500;
}
::placeholder {
color: $titleColor;
}
}
}
:deep(.van-pull-refresh) {
.van-pull-refresh__track {
min-height: 300px;
}
}
:deep(.lazy_img) {
.laz_com {
.animationImg {
background: linear-gradient(to right, rgba(253, 191, 80, 0.4), rgba(255, 224, 167, 0.4));
border-radius: 50%;
.loading {
background-size: 20px 20px;
}
}
}
}
.wrapper {
padding: 20px 14px 15px;
height: 480px;
background: url('./../../assets/images/png/commentBg.png') no-repeat;
background-size: 100% 100%;
.header {
.left {
padding-left: 10px;
.title {
width: 58px;
height: 22px;
background: url('./../../assets/images/png/commentTitle.png') no-repeat;
background-size: 100% 100%;
}
.total {
font-size: 12px;
color: rgb(153, 153, 153);
margin-top: 6px;
}
}
.close {
height: 40px;
width: 40px;
}
}
.main {
height: 320px;
margin-top: 40px;
.commentBox {
margin-bottom: 15px;
.commentInfo {
display: flex;
.avatar {
height: 33px;
width: 33px;
border-radius: 50%;
margin-right: 12px;
}
.info {
color: rgb(102, 102, 102);
.name {
font-size: 14px;
display: flex;
justify-content: flex-start;
align-items: center;
.vipIcon {
height: 13.4px;
width: 24.4px;
margin-left: 10px;
}
.genderIcon {
height: 20px;
width: 20px;
margin-left: 5px;
}
}
.text {
font-size: 12px;
margin: 7px 0 7px;
}
.address {
font-size: 11px;
}
}
}
.zanBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.zanIcon {
height: 22px;
width: 22px;
margin-bottom: 5px;
}
.zanNumber {
font-size: 12px;
}
}
}
}
.footer {
.addComment {
width: 100%;
margin-top: 10px;
.commentInputBox {
height: 34px;
width: 290px;
background-color: rgb(244, 244, 248);
border: unset;
border-radius: 34px;
:deep(.van-field__body) {
padding-top: 5px;
padding-left: 20px;
::placeholder {
color: rgb(184, 184, 209);
}
}
}
.commentIcon {
height: 28px;
width: 28px;
margin-left: 18px;
}
}
}
}
</style>
@/service/entertainment
-986
View File
@@ -1,986 +0,0 @@
<template>
<van-popup
v-if="props.renderType === 'popup'"
v-model:show="isShow"
position="bottom"
z-index="2001"
teleport="body"
@close="emits('close')"
>
<div ref="CommentBox" class="comment-wrap"></div>
</van-popup>
<div v-else ref="CommentBox" class="comment-wrap"></div>
<Teleport v-if="CommentBox" :to="CommentBox">
<div class="wrapper" :class="[props.renderType || '', { blackBackground }]">
<div class="headerBox">
<div class="horizontalLineBlock"></div>
<div class="title">评论({{ state.lfCount }})</div>
</div>
<div class="main overflow-y-auto">
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh">
<div class="qiuckSearchBox flex-vcenter" v-if="state.quickSearchObj.title">
<span>大家都在搜索:</span>
<van-notice-bar
v-if="state.quickSearchObj.title"
@click.stop="jumpAdv(state.quickSearchObj)"
color="#f25477"
background="transparent"
scrollable
:text="state.quickSearchObj.title"
/>
<!-- <div class="qiuckText" @click.stop="jumpAdv(state.quickSearchObj)">-->
<!-- {{ state.quickSearchObj.title }}-->
<!-- <SvgIcon name="qiuckSearchIcon" />-->
<!-- </div>-->
<!-- <SvgIcon name="qiuckSearchIcon" class="qiuckSearchIcon" />-->
</div>
<div class="commentBox" v-for="item in state.list" :key="item.id">
<div v-if="item.isGodComment" class="goldComment"></div>
<div class="pinToTopComment" v-if="item.pinToTop"></div>
<LazyImg
:src="item.userPortrait"
class="avatar"
:key="item.userPortrait"
@click="toUser(item, item.isOfficial)"
/>
<div class="commentInfoBox">
<div class="name flex-vcenter">
<span class="ellipsis">
{{ item.userName || '未知用户' }}
</span>
<SvgIcon v-if="[1, 2].includes(item.vipLevel)" name="cmVip" />
<SvgIcon v-else-if="item.vipLevel === 3" name="cmSvip" />
</div>
<div class="content" v-html="rearrangeText(item.content)"></div>
<div class="commentInfo flex-center-between" v-if="!item.isOfficial && !item.pinToTop">
<div class="time">{{ moment(item.createdAt).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="optBox flex-vcenter">
<img class="icon" src="@/assets/images/png/msgIcon.png" @click="reply(item, undefined, undefined)" />
<div class="zanBox">
<!-- <SvgIcon @click="clickLike(item)" v-if="item.isLike" name="movieZan" />-->
<!-- <SvgIcon @click="clickLike(item)" v-else name="movieUnZan" />-->
<img
class="icon"
@click="clickLike(item)"
v-if="item.isLike"
src="@/assets/images/png/movieZan.png"
/>
<img class="icon" @click="clickLike(item)" v-else src="@/assets/images/png/movieUnZan.png" />
</div>
</div>
</div>
<!-- 子评论区域 -->
<div class="subCommentArea">
<div class="subShow" v-if="item.Info && item.Info.length">
<div class="subCommentBox" v-for="subItem in item.Info" :key="subItem.id">
<LazyImg :src="subItem.userPortrait" class="avatar" :key="subItem.userPortrait" />
<div class="commentInfoBox">
<div class="nameBox flex-vcenter">
<div class="name">{{ subItem.userName || '未知用户' }}</div>
<SvgIcon name="replyArrow" v-if="subItem.toUserName" />
<div class="name">{{ subItem.toUserName }}</div>
</div>
<div class="content" v-html="rearrangeText(subItem.content)"></div>
<div class="commentInfo flex-center-between">
<div class="time">{{ moment(subItem.createdAt).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="optBox flex-vcenter">
<img
class="icon"
src="@/assets/images/png/msgIcon.png"
@click="reply(subItem, subItem.userID, item.id)"
/>
<div class="zanBox">
<!-- <SvgIcon @click="clickLike(subItem)" v-if="subItem.isLike" name="movieZan" />-->
<!-- <SvgIcon @click="clickLike(subItem)" v-else name="movieUnZan" />-->
<img
class="icon"
@click="clickLike(subItem)"
v-if="subItem.isLike"
src="@/assets/images/png/movieZan.png"
/>
<img
class="icon"
@click="clickLike(subItem)"
v-else
src="@/assets/images/png/movieUnZan.png"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="subShow pdunset" v-if="item.subShow">
<div class="subCommentBox" v-for="subItem in item.subList" :key="subItem.id">
<LazyImg
:src="subItem.userPortrait"
class="avatar"
:key="subItem.userPortrait"
@click="toUser(subItem, subItem.isOfficial)"
/>
<div class="commentInfoBox">
<div class="nameBox flex-vcenter">
<div class="name">{{ subItem.userName || '未知用户' }}</div>
<SvgIcon name="replyArrow" v-if="subItem.toUserName" />
<div class="name">{{ subItem.toUserName }}</div>
</div>
<div class="content" v-html="rearrangeText(subItem.content)"></div>
<div class="commentInfo flex-center-between">
<div class="time">{{ moment(subItem.createdAt).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="optBox flex-vcenter">
<!-- <SvgIcon-->
<!-- name="msg"-->
<!-- @click="reply(subItem, subItem.userID, item.id)"-->
<!-- :color="blackBackground ? '#fff' : '#000'"-->
<!-- />-->
<img
class="icon"
@click="reply(subItem, subItem.userID, item.id)"
src="@/assets/images/png/msgIcon.png"
/>
<div class="zanBox">
<img
class="icon"
@click="clickLike(subItem)"
v-if="subItem.isLike"
src="@/assets/images/png/movieZan.png"
/>
<img
class="icon"
@click="clickLike(subItem)"
v-else
src="@/assets/images/png/movieUnZan.png"
/>
</div>
</div>
</div>
</div>
</div>
<Loading v-if="item.subLoading" />
</div>
<div class="expandBox flex-vcenter" v-if="item.commCount - 1 > 0">
<div
class="expandMore flex-vcenter"
@click="expandComment(item)"
v-if="!(item.subList && item.subList.length) && !item.subHasNext"
>
<div class="flex-vcenter">
—— 展开{{ item.commCount - 1 }}条回复
<SvgIcon name="packDown" />
</div>
</div>
<div class="expandMore flex-vcenter" @click="loadMore(item)" v-if="item.subHasNext">
<div class="flex-vcenter">查看更多></div>
</div>
<div class="closeBox" @click="expandComment(item)" v-if="item.subList && item.subList.length">
<div class="close flex-center-center">
收起
<SvgIcon name="packUp" />
</div>
</div>
</div>
</div>
</div>
</div>
</PullRefresh>
</div>
<div class="mask" v-if="state.childrenCommentObj" @click="cancelReply"></div>
<!-- 评论 -->
<div class="bottomBox">
<div class="commentBtnBox flex-center-between">
<div class="commentInput flex-vcenter">
<van-field
ref="contentRef"
v-model="state.content"
:placeholder="state.placeholder"
input-align="left"
:clearable="false"
:border="false"
@blur="blur"
/>
</div>
<div class="commentBtn" @click.stop="sendBtn"></div>
</div>
</div>
</div>
</Teleport>
<!-- 会员提示框 -->
<VipTipPopup ref="vipTipPopupRef" :isCancelReturn="true" />
</template>
<script lang="ts">
export default {
name: 'commentBox',
};
</script>
<script lang="ts" setup>
import { computed, reactive, ref, PropType, nextTick } from 'vue';
import { showToast } from 'vant';
import { useGlobalStore } from '@/stores/global';
import { addVideoHistory } from '@/utils';
import { HistoryVideoType } from '@/enums/app';
import { jumpAdv } from '@/utils/index';
import { useRoute, useRouter } from 'vue-router';
import useLoading from '@/hooks/loading';
import moment from 'moment';
import CommunityApi from '@/service/community';
import VideoApi from '@/service/video';
import { useUserStore } from '@/stores/user';
const router = useRouter();
const route = useRoute();
const CommentBox = ref();
const vipTipPopupRef = ref();
const userStore = useUserStore();
const props = defineProps({
isOpen: {
type: Boolean,
default: false,
},
objType: {
type: String,
default: 'video',
},
blackBackground: {
type: Boolean,
default: false,
},
renderType: {
type: String as PropType<'popup' | 'normal'>, //popup弹窗形式,normal正常布局流
default: 'popup',
},
});
const { openLoading, closeLoading } = useLoading();
const isShow = computed(() => {
return props.isOpen;
});
const globalStore = useGlobalStore();
const emits = defineEmits(['addComment', 'close']);
const contentRef = ref<HTMLElement>();
const state = reactive({
content: '',
list: <Array<any>>[], // 评论列表
pageNumber: 1, // 评论页码
pageSize: 15, // 评论一页的数量
subPageNumber: 1, // 子评论页码
subPageSize: 15, // 子评论一页的数量
videoInfo: <any>{},
lfCount: 0,
placeholder: '请输入评论内容',
childrenCommentObj: <any>null,
quickSearchObj: <any>{}, // 大家都在搜对象
});
// 上拉加载及下拉刷新参数
const moreParams = reactive({
loading: true,
refreshing: false,
finished: false,
isNoData: false,
error: false,
isHigehtMax: false,
});
// 获取评论数据
const getList = async (type: string) => {
try {
let req = {
objID: String(state.videoInfo.id),
objType: props.objType,
curTime: new Date().toISOString(),
pageNumber: String(state.pageNumber),
pageSize: String(state.pageSize),
};
let res: any = await CommunityApi.getCommentList(req);
state.lfCount = res.total || 0;
moreParams.loading = false;
moreParams.refreshing = false;
let list = res.list || [];
if (type === 'refresh') {
state.list = list;
} else {
state.list = state.list.concat(list);
}
if (state.pageNumber == 1 && res.quickSearchList && res.quickSearchList.length) {
res.quickSearchList.map((item: any) => {
if (item.type == 2) {
state.quickSearchObj = item;
} else {
// 评论对象
const _reg = new RegExp(item.searchKeyword, 'g');
let obj = {
id: item.id,
content: item.title.replace(
_reg,
`<span style="color:rgba(11, 153, 255, 1)" onclick="jumpKeywords('${item.link},${item.id}')">${item.searchKeyword}</span>`,
),
userName: item.userName,
userID: item.userID,
Info: [],
userPortrait: item.userPortrait,
pinToTop: true,
};
state.list.splice(1, 0, obj);
}
});
}
if (state.pageNumber == 1 && state.list.length == 0) {
moreParams.isNoData = true;
}
if (!res.hasNext) {
moreParams.finished = true;
}
} catch (e) {
moreParams.loading = false;
moreParams.error = true;
}
};
const rearrangeText = (content: string) => {
if (content && content.indexOf('#') != -1) {
let startIndex = content.indexOf('#');
let endIndex = content.indexOf('#', startIndex + 1);
let jsonText = content.substring(startIndex, endIndex + 1);
const _reg = new RegExp(jsonText, 'g');
return content.replace(
_reg,
`<span style="color:#f25477" onclick="jumpSerialNoVideo('${jsonText.substr(
1,
jsonText.length - 2,
)}')">${jsonText}</span>`,
);
} else {
return content;
}
};
// // 番号跳转视频
// const jumpSerialNoVideo = async (serialNo: any) => {
// if (!serialNo) return showToast('番号数据不存在');
// try {
// let req = {
// seedId: serialNo,
// };
// let res: any = await CommunityApi.getCheckInfo(req);
// if (res && res.id) {
// router.push({
// path: '/movieDetails',
// query: {
// vid: res.vidInfo.id,
// uid: res.vidInfo.publisher?.uid,
// },
// });
// }
// } catch (e) {
// console.log(e);
// }
// };
// 内链跳转
const jumpKeywords = (str: any) => {
const strArr = str.split(',');
const link = strArr[0];
const id = strArr[1];
let obj = {
link: link,
id: id,
};
jumpAdv(obj);
};
const toUser = (item: any, isOfficial: boolean) => {
if(!isOfficial){
router.push({ path: '/userInfo', query: { uid: item.userID } });
}
};
// window.jumpKeywords = jumpKeywords;
// window.jumpSerialNoVideo = jumpSerialNoVideo;
const onLoad = () => {
moreParams.loading = true;
state.pageNumber++;
getList('');
};
const onRefresh = () => {
state.pageNumber = 1;
moreParams.refreshing = true;
moreParams.finished = false;
moreParams.isNoData = false;
getList('refresh');
};
// 评论成功后增加评论数据
const addComment = (res: any, childrenCommentObj: any) => {
moreParams.isNoData = false;
if (childrenCommentObj) {
state.list.map((comment: any) => {
if (comment.id == childrenCommentObj.id) {
if (!comment.subList) {
comment.subLoading = false;
comment.subList = [];
comment.subHasNext = true;
comment.subShow = true;
}
comment.Info.unshift(res);
}
return comment;
});
} else {
state.list.unshift(res);
}
};
// 点赞
const clickLike = async (comment: any) => {
try {
let req = {
type: 'comment',
objID: comment.id,
};
if (!comment.isLike) {
await CommunityApi.zan(req);
comment.isLike = true;
comment.likeCount++;
showToast('点赞成功');
} else {
await CommunityApi.cancleZan(req);
comment.isLike = false;
comment.likeCount--;
showToast('取消成功');
}
} catch (e) {
console.log(e);
}
};
// 回复二级评论
const reply = (comment: any, toUserID: any, parentId: any) => {
comment.toUserID = toUserID ? toUserID : undefined;
comment.rid = toUserID ? comment.id : undefined;
comment.id = toUserID ? parentId : comment.id;
state.childrenCommentObj = comment;
state.placeholder = `回复@${comment.userName}`;
nextTick(() => {
contentRef.value?.focus();
});
};
// 展开评论
const expandComment = async (comment: any) => {
if (!comment.subShow) {
comment.subShow = true;
comment.subHasNext = true; // 子评论是否还有下一页数据
comment.subList = []; // 子评论列表
comment.subLoading = true; // 子评论加载loading
state.subPageNumber = 1;
const resObj: any = await getSubList(comment);
comment.subLoading = false;
comment.subHasNext = resObj.subHasNext;
if (resObj.list.length) {
if (!comment.subList) {
comment.subList = [];
}
comment.subList = comment.subList.concat(resObj.list);
}
} else {
comment.subLoading = false;
comment.subList = []; // 清空子评论列表
comment.subHasNext = false; // 子评论是否还有下一页数据
comment.subShow = false;
}
};
// 获取更多评论
const loadMore = async (comment: any) => {
comment.subLoading = true;
state.subPageNumber++;
const resObj: any = await getSubList(comment);
comment.subLoading = false;
comment.subList = comment.subList.concat(resObj.list);
comment.subHasNext = resObj.subHasNext;
};
// 获取二级评论
const getSubList = async (comment: any) => {
let resObj = {
subHasNext: true,
list: <any>[],
};
let req = {
objID: comment.objID,
cmtId: comment.id,
fstID: comment.Info[0].id, // 第一条评论的ID
curTime: new Date().toISOString(),
pageNumber: state.subPageNumber,
pageSize: state.subPageSize,
};
let res: any = await CommunityApi.getSecCommentList(req);
if (res.list && res.list.length > 0) {
resObj.list = res.list;
}
if (!res.hasNext) {
resObj.subHasNext = false;
}
return resObj;
};
const sendBtn = async () => {
if (!userStore.isVip) {
vipTipPopupRef.value?.openPop();
return;
}
if (!state.content) {
showToast('请输入评论内容');
return;
}
try {
let req: any = {};
if (!state.childrenCommentObj) {
req = {
objID: state.videoInfo.id,
objType: props.objType,
level: 1,
content: state.content,
};
} else {
// 二级评论参数
req = {
objID: state.videoInfo.id,
objType: props.objType,
// objType: state.videoInfo.newsType,
level: 2,
content: state.content,
cid: state.childrenCommentObj.id,
// userID: state.childrenCommentObj.userID,
userID: userStore.userInfo.uid,
rid: state.childrenCommentObj.toUserID ? state.childrenCommentObj.rid : undefined,
toUserID: state.childrenCommentObj.toUserID,
};
}
// if (req.objType == 'POST' || req.objType == 'SP' || req.objType == 'SHARE_POST' || req.objType == 'SHOP_POST') {
// req.objType = 'video';
// }
openLoading();
let res: any = await CommunityApi.sendComment(req);
state.content = ''; // 评论输入框内容清空
if (res != 'success') {
state.lfCount++;
addComment(res, state.childrenCommentObj);
}
cancelReply();
showToast('评论成功 请耐心等待审核');
emits('addComment');
} catch (e) {
console.log(e);
} finally {
closeLoading();
}
};
// 回复一级评论
const cancelReply = () => {
state.childrenCommentObj = <any>null;
state.placeholder = '请输入评论内容';
state.content = '';
};
const getCommentList = () => {
state.pageNumber = 1;
moreParams.loading = false;
moreParams.finished = false;
state.list = <Array<Types.Video.CommentItem>>[];
getList('');
};
const blur = () => {
window.scrollTo(0, 0);
};
defineExpose({
getCommentList,
moreParams,
state,
});
</script>
<style lang="scss" scoped>
@import '@/assets/styles/mixins.scss';
:deep(.van-popup) {
background: transparent !important;
}
.wrapper {
height: 70vh;
font-weight: 400 !important;
.headerBox {
//height: 66px;
background: #f5f5f5;
border-radius: 20px 20px 0 0;
//padding-top: 18px;
margin-bottom: 18px;
border-bottom: 1px solid #e0e0e0;
.horizontalLineBlock {
height: 5px;
width: 36px;
background: rgba(51, 51, 51, 1);
border-radius: 5px;
margin: 0 auto;
}
.title {
font-size: 16px;
font-weight: 500;
color: rgba(51, 51, 51, 1);
//padding-left: 20px;
margin-top: 15px;
}
}
.main {
height: 55vh;
//padding: 15px;
background: #f5f5f5;
.qiuckSearchBox {
color: #333333;
font-size: 14px;
margin: 3px 0 15px;
white-space: nowrap;
position: relative;
:deep(.van-notice-bar__content) {
color: #f25477 !important;
font-weight: 500 !important;
}
:deep(.van-notice-bar) {
width: 100%;
padding: 0;
}
.qiuckSearchIcon {
height: 10px;
width: 10px;
position: absolute;
right: -10px;
top: 2px;
}
.qiuckText {
color: #f25477;
font-weight: 500;
position: relative;
> svg {
height: 10px;
width: 10px;
position: absolute;
right: -10px;
top: 2px;
}
}
}
.commentBox {
display: flex;
//margin-bottom: 14px;
border-bottom: 1px solid rgba(0, 0, 0, 0.02);
position: relative;
.goldComment {
position: absolute;
top: -10px;
right: -10px;
height: 69.25px;
width: 76px;
background: url('@/assets/images/png/goldComment.png') no-repeat;
background-size: 100% 100%;
}
.pinToTopComment {
position: absolute;
top: -10px;
right: -10px;
height: 69.25px;
width: 76px;
background: url('@/assets/images/png/pinToTopComment.png') no-repeat;
background-size: 100% 100%;
}
.avatar {
height: 40px;
width: 40px;
border-radius: 50%;
overflow: hidden;
position: relative;
transform: translateZ(0px);
:deep(.loading) {
background-size: 18px 18px;
}
}
.commentInfoBox {
flex: 1;
margin-left: 10px;
font-size: 14px;
.name {
color: #666666;
font-weight: 500;
flex: 1;
span {
max-width: 240px;
}
> svg {
height: 18px;
width: 18px;
margin-left: 4px;
}
}
.content {
margin-top: 10px;
line-height: 20px;
color: #ffffff;
font-size: 14px;
white-space: pre-line;
}
.commentInfo {
margin-top: 8px;
color: rgba(153, 153, 153, 1);
.time {
font-size: 12px;
color: #999999;
}
.optBox {
color: rgba(51, 51, 51, 1);
svg {
height: 20px;
width: 20px;
}
.icon {
height: 20px;
width: 20px;
}
.zanBox {
margin-left: 20px;
display: flex;
}
}
}
}
.subCommentArea {
margin-top: 11px;
.subShow {
// padding: 4px 12px;
.subCommentBox {
display: flex;
margin: 10px 0 0;
.avatar {
height: 24px;
width: 24px;
border-radius: 50%;
overflow: hidden;
position: relative;
transform: translateZ(0px);
:deep(.loading) {
background-size: 13px 13px;
}
}
.commentInfoBox {
flex: 1;
margin-left: 10px;
font-size: 12px;
.nameBox {
.name {
height: 24px;
color: #333333;
font-weight: 500;
line-height: 24px;
flex: unset;
}
> svg {
margin: 0 5px;
}
}
.content {
margin-top: 3px;
line-height: 20px;
font-size: 12px;
}
.commentInfo {
margin-top: 8px;
.time {
font-size: 12px;
color: #999999;
}
}
}
}
}
.pdunset {
padding-top: unset;
}
.expandBox {
color: #989898;
font-size: 12px;
flex: 1;
padding: 11px 12px 12px;
svg {
height: 12px;
width: 12px;
margin-left: 6px;
}
.closeBox {
display: flex;
align-items: center;
width: 100%;
padding-right: 5px;
}
}
}
}
}
.mask {
height: 100vh;
width: 100vw;
position: fixed;
bottom: 0;
right: 0;
z-index: 88;
background-color: rgba(0, 0, 0, 0.4);
}
.bottomBox {
width: 100%;
position: absolute;
bottom: 0;
right: 0;
z-index: 99;
height: 92px;
background-color: #000000;
padding: 14px 16px 0px 16px;
.commentBtnBox {
//height: 92px;
width: 100%;
// @include SafeArea('padding', 'bottom', 20px);
box-shadow: 0px -1px 4px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
.commentInput {
border-radius: 16px;
//border: 1px solid #e6e6e6;
width: 255px;
height: 32px;
position: relative;
flex: 1;
padding: 0 12px;
font-size: 14px;
font-weight: 400;
color: #999;
background: #191919;
:deep(.van-cell) {
padding: 10px 0;
height: 44px;
background-color: unset;
border-radius: 12px;
::placeholder {
font-size: 14px;
color: rgba(255, 255, 255, 0.4);
}
.van-field__control {
font-size: 14px;
color: #ffffff;
}
}
}
// .linkBtn {
// height: 40px;
// width: 40px;
// background: url('@/assets/images/png/home/linkBtn.png') no-repeat;
// background-size: 100% 100%;
// margin-left: 12px;
// }
.commentBtn {
width: 40px;
height: 40px;
background: url('@/assets/images/png/commit_send.png') no-repeat;
background-size: 100% 100%;
margin-left: 11px;
}
}
}
&.normal {
height: unset;
.headerBox {
border-radius: 0;
background: transparent;
border-bottom: 0;
.horizontalLineBlock {
display: none;
}
.title {
color: $titleColor;
font-size: 14px;
}
}
.main {
background: transparent;
height: unset;
overflow: hidden;
}
.bottomBox {
position: fixed;
}
}
}
.blackBackground {
.main .commentBox .commentInfoBox .name {
color: #f1f1f1;
}
.headerBox .title {
color: #fff !important;
}
.main .commentBox .commentInfoBox .content {
color: #fff;
}
.main .commentBox .commentInfoBox .commentInfo .optBox,
.main .commentBox .subCommentArea .subShow .subCommentBox .commentInfoBox .nameBox .name {
color: #fff;
}
.main .commentBox .commentInfoBox .commentInfo .time,
.main .commentBox .subCommentArea .subShow .subCommentBox .commentInfoBox .commentInfo .time {
color: #e6e6e6;
}
::deep(.van-list__finished-text) {
color: #e6e6e6;
}
}
</style>
-90
View File
@@ -1,90 +0,0 @@
<template>
<div class="officialPopup">
<van-popup v-model:show="state.isRewardShow" :style="{ backgroundColor: 'rgba(0, 0, 0, 0)' }">
<div class="popupBox">
<div class="listbox">
<p class="flex-center-center">您还没有绑定邮箱</p>
</div>
<div class="btnBox flex-center-center">
<div class="btn" @click="state.isRewardShow = false">取消</div>
<div class="btn BindBtn" @click="$router.push('/bindEmail')">去绑定</div>
</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { useConfigStore } from '@/stores/config';
const configStore = useConfigStore();
const state = reactive({
isRewardShow: false,
});
const openPop = () => {
state.isRewardShow = true;
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.officialPopup {
background-color: rgba(255, 255, 255);
.popupBox {
background: rgba(255, 255, 255);
border-radius: 6px;
height: 136px;
width: 311px;
overflow: hidden;
position: relative;
.listbox {
width: 100%;
height: 60px;
padding: 0 15px;
box-sizing: border-box;
margin-bottom: 22px;
position: relative;
p {
padding-top: 35px;
text-align: center;
font-weight: 500;
color: #000;
font-size: 16px;
}
.btn {
position: absolute;
width: 85px;
height: 35px;
text-align: center;
border-radius: 5px;
line-height: 35px;
color: white;
right: 10px;
font-size: 14px;
}
}
}
.btnBox {
margin: 0 auto;
.btn {
width: 100%;
text-align: center;
line-height: 48px;
border-top: 0.5px solid rgba(231, 231, 231, 1);
}
.BindBtn {
color: $themeColor;
border-left: 0.5px solid rgba(231, 231, 231, 1);
}
}
:deep(.van-popup) {
border-radius: 12px;
}
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
}
</style>
-38
View File
@@ -1,38 +0,0 @@
<template>
<div class="empty_warp">
<div class="empty_box">
<div class="noDataIcon"></div>
<div class="noData">暂无数据</div>
<p>请稍后再试~</p>
</div>
</div>
</template>
<style lang="scss" scoped>
.empty_warp {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-top: 60px;
padding-bottom: 120px;
.noDataIcon {
height: 140px;
width: 247px;
background: url('./../../assets/images/png/nodata2.png') no-repeat;
background-size: 100% 100%;
}
.noData {
font-size: 18px;
text-align: center;
padding-top: 14px;
color: rgb(102, 102, 102);
}
p {
padding-top: 8px;
text-align: center;
color: rgb(153, 153, 153);
font-size: 11px;
}
}
</style>
-105
View File
@@ -1,105 +0,0 @@
<template>
<van-overlay :show="isShow">
<div class="warp">
<div class="content_warp">
<div class="content">
<div class="announcement_text">
<h1>系统提示</h1>
<h2>APP线路检测失败</h2>
<h3>解决方案:</h3>
<p>1.请检测您的网络是否正常,手机是否开了VPN或10分钟后再尝试访问。</p>
<p>2.前往官网地址 jhs99.cc 下载最新版。</p>
<p>3.前往Potato鉴黄师官方交流群。</p>
</div>
</div>
<div class="btn" @click="toPotato">前往群聊</div>
</div>
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const isShow = ref(true);
const toPotato = () => {
window.location.href = 'https://pzcp.org/jianhuangshigfq';
};
</script>
<style lang="scss" scoped>
.warp {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.content_warp {
width: 312px;
height: 301;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
padding-bottom: 20px;
display: flex;
flex-direction: column;
justify-content: center;
border-radius: 10px;
.content {
height: 230px;
font-size: 14px;
.announcement_text {
padding: 16px 16px 0;
height: 200px;
white-space: pre-wrap;
line-height: 22px;
font-size: 13px;
overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
h1 {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
font-weight: bold;
}
h2 {
margin-top: 20px;
padding: 6px 0;
font-size: 16px;
color: rgb(234, 83, 55);
}
h3 {
font-size: 14px;
color: rgb(38, 38, 38);
font-weight: bold;
padding-bottom: 6px;
}
p {
line-height: 20px;
font-size: 14px;
color: rgb(38, 38, 38);
}
}
.tip {
padding: 0 16px;
color: #ffc069;
font-size: 12px;
line-height: 22px;
p {
padding-top: 10px;
}
}
}
.btn {
height: 38px;
width: 231px;
background-color: $themeColor;
color: white;
border-radius: 10px;
font-size: 14px;
text-align: center;
line-height: 38px;
margin: 0 auto 0;
}
}
}
</style>
-149
View File
@@ -1,149 +0,0 @@
<template>
<!-- 浮窗 -->
<div
class="square"
id="moveDiv"
:class="{ closeSquare: isClose }"
@mousedown="down()"
@touchstart="down()"
@mousemove.prevent.stop="move()"
@touchmove.prevent.stop="move()"
@mouseup="end()"
@touchend="end()"
@click="jumpUrl()"
v-if="advObj"
>
<LazyImg class="squareImg" :src="advObj?.cover" />
<div class="close" @click.stop="close" v-if="!isClose"></div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
// import { AdType, getAdItem } from '@/utils/getConfig';
import { AdType } from '@/enums/app';
import { getAdItem } from '@/utils/getConfig';
import { jumpAdv } from '@/utils/index';
// 悬浮
let flags = ref(false);
let position = reactive({
x: 0,
y: 0,
});
let nx = ref();
let ny = ref();
let dx = ref();
let dy = ref();
let xPum = ref();
let yPum = ref();
let advObj = ref(); // 广告
let isClose = ref(false);
// 初始化广告
init();
function init() {
advObj.value = getAdItem(AdType.SHORTVIDEOADV)[0];
// console.log(getAdItem(AdType.SHORTVIDEOADV)[0].cover, '-------------');
// console.log(advObj.value);
}
// 实现移动端拖拽
function down() {
flags.value = true;
let touch;
if ((event as any).touches) {
touch = (event as any).touches[0];
} else {
touch = event;
}
position.x = touch.clientX;
position.y = touch.clientY;
dx.value = (document.getElementById('moveDiv') as HTMLElement).offsetLeft;
dy.value = (document.getElementById('moveDiv') as HTMLElement).offsetTop;
}
function move() {
if (flags.value) {
let touch;
if ((event as any).touches) {
touch = (event as any).touches[0];
} else {
touch = event;
}
nx.value = touch.clientX - position.x;
ny.value = touch.clientY - position.y;
xPum.value = dx.value + nx.value;
yPum.value = dy.value + ny.value;
//添加限制:只允许在屏幕内拖动
const maxWidth = document.body.clientWidth - 90; //屏幕宽度减去悬浮框宽高
const maxHeight = document.body.clientHeight - 90;
if (xPum.value < 0) {
//屏幕x限制
xPum.value = 0;
} else if (xPum.value > maxWidth) {
xPum.value = maxWidth;
}
if (yPum.value < 65) {
//屏幕y限制
yPum.value = 65;
} else if (yPum.value > maxHeight) {
yPum.value = maxHeight;
}
(document.getElementById('moveDiv') as HTMLElement).style.left = xPum.value + 'px';
(document.getElementById('moveDiv') as HTMLElement).style.top = yPum.value + 'px';
//阻止页面的滑动默认事件
// document.addEventListener(
// "touchmove",
// function () {
// // 1.2 如果碰到滑动问题,请注意是否获取到 touchmove
// (event as any).preventDefault(); //jq 阻止冒泡事件
// // event.stopPropagation(); // 如果没有引入jq 就用 stopPropagation()
// },
// { passive: true }
// );
}
}
//鼠标释放时候的函数
function end() {
flags.value = false;
}
function close() {
isClose.value = true;
}
function jumpUrl() {
if (isClose.value) return (isClose.value = false);
jumpAdv(advObj.value);
}
</script>
<style lang="scss" scoped>
.square {
height: 80px;
width: 80px;
z-index: 1000;
position: fixed;
top: 60px;
left: 5px;
border-radius: 10px;
// overflow: hidden;
:deep(img) {
border-radius: 10px;
}
.squareImg {
height: 80px;
width: 80px;
}
.close {
position: absolute;
right: -10px;
top: -10px;
height: 20px;
width: 20px;
border-radius: 50%;
background: url('../../assets/images/png/appsClose.png') no-repeat;
background-size: 100% 100%;
}
}
.closeSquare {
width: 20px;
border-radius: 20px;
.squareImg {
width: 20px;
}
}
</style>
@@ -1,87 +0,0 @@
<template>
<div class="fullScreenGallery">
<van-swipe
ref="swipeRef"
class="my-swipe"
indicator-color="white"
:loop="isLoop"
:autoplay="autoTime"
:lazy-render="true"
@change="change"
>
<van-swipe-item class="my-swier-item" v-for="url in videoInfo.seriesCover" :key="url">
<div class="fullScreen_item" @touchstart="touchstart" @touchmove="touchmove">
<AnimationImg :src="url" />
</div>
</van-swipe-item>
</van-swipe>
</div>
</template>
<script lang="ts" setup>
import type { SwipeInstance } from 'vant';
import { ref } from 'vue';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
}>();
const autoTime = ref(2000);
const isLoop = ref(true);
const swipeRef = ref<SwipeInstance>();
function change(index: number) {
console.log(index);
}
const startX = ref();
const startY = ref();
function touchstart(e: any) {
// 如果你要阻止点击事件,请反注释下一行代码
// e.preventDefault()
startX.value = e.touches[0].clientX;
startY.value = e.touches[0].clientY;
}
function touchmove(e: any) {
// e.preventDefault()
let moveX = e.touches[0].clientX;
if (startX.value - moveX != 0) {
// 右滑触发
// do something
isLoop.value = false;
}
}
</script>
<style lang="scss" scoped>
:deep(.van-swipe__indicator) {
width: 28px;
height: 4px;
border-radius: 2px;
}
:deep(.van-swipe__indicators) {
bottom: 1%;
}
.fullScreenGallery {
height: 100%;
width: 100%;
display: flex;
align-items: center;
.my-swipe,
.van-swipe-item,
.my-swier-item {
height: 100%;
width: 100%;
.fullScreen_item {
width: 100%;
height: 100%;
:deep(.van-image) {
display: flex;
align-items: center;
background: #000000 !important;
}
:deep(img) {
height: auto;
}
}
}
}
</style>
-90
View File
@@ -1,90 +0,0 @@
<template>
<div class="accountCredentialsPop">
<!-- 账号凭证弹窗 -->
<van-overlay v-model:show="isOpenRecharge" z-index="2001">
<div class="wrapper">
<div class="actTipBox">
<div class="infoboxs">
<div class="info">免费送金币啦!</div>
<div class="infomessage">金币月卡赠送金币已到账</div>
</div>
<div class="actTipBtn">
<div class="btn" @click="getCoin">请领取</div>
</div>
</div>
</div>
</van-overlay>
</div>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue';
import { useAppStore } from '@/stores/app';
import { useUserStore } from '@/stores/user';
import MineApi from '@/service/mine';
import { showToast } from 'vant';
const appStore = useAppStore();
const userStore = useUserStore();
const props = defineProps<{
isOpen: boolean;
}>();
const emits = defineEmits(['closeFree']);
const isOpenRecharge = computed(() => {
return props.isOpen;
});
// 领取
const getCoin = async () => {
try {
let res: any = await MineApi.getCoin();
showToast('领取成功');
emits('closeFree');
} catch (e) {
emits('closeFree');
}
};
</script>
<style lang="scss" scoped>
.accountCredentialsPop {
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
.actTipBox {
width: 292px;
height: 152px;
position: relative;
background-color: rgb(255, 255, 255);
border-radius: 6px;
.infoboxs {
height: calc(100% - 50px);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: #333333;
}
.info {
font-size: 16px;
line-height: 22px;
margin-bottom: 10px;
font-weight: bold;
}
.infomessage {
font-size: 14px;
}
.actTipBtn {
width: 100%;
height: 50px;
text-align: center;
line-height: 50px;
color: #ffa641;
border-top: solid 1px #eaeaea;
position: absolute;
bottom: 0;
left: 0;
}
}
}
}
</style>
-212
View File
@@ -1,212 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper_gold">
<div class="content">
<div class="title">购买解锁</div>
<div class="subTitle">—— unlock ——</div>
<div class="amount_box" @click.stop>
<div class="amount_item" :class="{ active: userInfo.isVip }">
<img class="transactionGoldIcon" src="@/assets/images/png/goldIcon.png" />
<span>{{ videoInfo.coins }}</span>
<div class="subscript">会员优惠</div>
</div>
<div class="amount_item" :class="{ active: !userInfo.isVip }">
<img class="transactionGoldIcon" src="@/assets/images/png/goldIcon.png" />
<span>{{ videoInfo.originCoins }}</span>
<div class="subscript">非会员</div>
</div>
</div>
<!-- <div class="couponBox" v-if="userInfo.goldVideoCoupon">
<div class="couponItem flex-center-between" v-for="(item, index) in userInfo.goldVideoCoupon" :key="index">
<div class="couponName">
观影券({{ item.gold }}金币):
<span>{{ item.count }}张</span>
</div>
<div class="useCouponBtn" @click.stop="selectCoupon(item)">立即使用</div>
</div>
</div> -->
<div class="unlock_now" @click.stop="unlockNow(0)">立即解锁</div>
</div>
<div class="close" @click="emit('close')">
<SvgIcon name="close" />
</div>
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import VideoApi from '@/service/video';
import { computed } from 'vue';
import { ProductType } from '@/enums/app';
import { showToast } from 'vant';
import { useUserStore } from '@/stores/user';
import { useRouter } from 'vue-router';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
isOpen: boolean;
}>();
const userInfo = useUserStore().userInfo;
const router = useRouter();
const { openLoading, closeLoading } = useLoading();
const emit = defineEmits(['close', 'buySuccess']);
const isShow = computed(() => {
return props.isOpen;
});
const selectCoupon = (item: any) => {
let money = userInfo.isVip ? props.videoInfo.coins : props.videoInfo.originCoins;
if (item.gold < money) {
showToast('观影券面值过小,请选择其他支付方式');
} else {
unlockNow(item.gold);
}
};
const unlockNow = (money: any) => {
try {
openLoading();
let productID = props.videoInfo.id;
if (!productID) {
showToast('购买出错~');
return;
}
VideoApi.videoBuying({ productType: ProductType.VIDEO, productID: productID, goldVideoCouponNum: money || 0 })
.then(() => {
showToast('购买成功');
if (money) {
for (let i = 0; i < userInfo.goldVideoCoupon.length; i++) {
if (money == userInfo.goldVideoCoupon[i].gold) {
if (userInfo.goldVideoCoupon[i].count > 1) {
userInfo.goldVideoCoupon[i].count--;
} else {
userInfo.goldVideoCoupon.splice(i, 1);
}
}
}
}
useUserStore().updateWallet();
emit('buySuccess');
})
.catch((e) => {
if (e.toString().indexOf('8000') != -1) {
router.push('/welfareCenter');
}
});
} finally {
closeLoading();
}
};
</script>
<style lang="scss" scoped>
.wrapper_gold {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.content {
width: 312px;
height: 254px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
border-radius: 8px;
background-color: rgb(255, 255, 255);
padding-top: 15px;
box-sizing: border-box;
text-align: center;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
font-weight: bold;
}
.subTitle {
font-size: 12px;
color: rgb(153, 153, 153);
margin: 7px 0 36px;
}
.amount_box {
display: flex;
justify-content: center;
.amount_item {
width: 110px;
height: 67px;
border-radius: 10px;
margin: 0px 10px 0;
font-size: 20px;
color: #ff7510;
display: flex;
align-items: center;
justify-content: center;
position: relative;
background-color: rgb(247, 247, 247);
border: solid 1px rgb(191,191,191);
padding-top: 10px;
> img {
width: 32px;
height: 32px;
margin-right: 4px;
}
.subscript {
position: absolute;
top: 0;
left: 0;
width: 49px;
height: 20px;
line-height: 20px;
text-align: center;
background: rgb(255, 135, 16);
font-size: 10px;
color: rgb(255, 255, 255);
letter-spacing: 0.2px;
border-radius: 8px 0 8px 0px;
}
}
.active {
border: solid 1px rgb(255,135,16);
background-color: rgb(255, 227, 180);
}
}
.couponBox {
max-height: 100px;
margin-top: 14px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
.couponItem {
padding: 0 30px;
font-size: 14px;
margin: 5px 0;
.couponName {
color: rgb(102, 102, 102);
span {
color: rgb(255, 13, 44);
}
}
.useCouponBtn {
color: rgb(255, 13, 44);
}
}
}
.unlock_now {
width: 227px;
height: 38px;
background: rgb(255, 160, 1);
border-radius: 10px;
margin: 28px auto 20px;
font-size: 16px;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
}
.close {
padding-top: 16px;
svg {
font-size: 32px;
}
}
}
</style>
-359
View File
@@ -1,359 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper_gold">
<!-- <div class="content">
<div class="title">温馨提示</div>
<div class="subTitle">—— 需要花费{{ videoInfo.originCoins }}金币解锁观看 ——</div>
<div class="amount_box" @click.stop>
<img class="transactionGoldIcon" src="@/assets/images/png/goldIcon.png" />
<span>{{ videoInfo.originCoins }}</span>
金币
</div>
<div class="unlock_now" @click.stop="unlockNow(0)">确认</div>
</div>
<div class="close" @click="emit('close')">
<SvgIcon name="close" />
</div> -->
<!-- <div class="content">-->
<!-- <div class="title">购买当前影片</div>-->
<!-- <div class="title-right" @click="emit('close')">关闭</div>-->
<!-- <div class="goldContent">-->
<!-- <div class="goldLeft">-->
<!-- <div class="originalPrice">原价</div>-->
<!-- <div class="ordinaryUser">普通用户</div>-->
<!-- <div class="golds">-->
<!-- <SvgIcon name="goldNew" class="goldNew" />-->
<!-- <span>{{ videoInfo.originCoins }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="goldLeft">-->
<!-- <div class="originalPrice isVip">会员</div>-->
<!-- <div class="ordinaryUser">会员用户</div>-->
<!-- <div class="golds">-->
<!-- <SvgIcon name="goldNew" class="goldNew" />-->
<!-- <span>{{ videoInfo.coins }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="goldBtn">-->
<!-- <div class="cancle" @click="$router.push('aiUndress')">AI换脸</div>-->
<!-- <div-->
<!-- class="buyGold"-->
<!-- v-if="walletInfo.amount < videoInfo.originCoins"-->
<!-- @click="$router.push({ path: '/recharge', query: { name: 'wallet' } })"-->
<!-- >-->
<!-- 购买金币-->
<!-- </div>-->
<!-- <div class="buyGold" v-else @click.stop="unlockNow(0)">购买视频</div>-->
<!-- </div>-->
<!-- <div v-if="userInfo.goldVideoCoupon">-->
<!-- <div class="movie" v-for="(item, index) in userInfo.goldVideoCoupon" :key="index">-->
<!-- <span>观影次数*{{ item.count }}</span>-->
<!-- <span @click.stop="selectCoupon(item)">使用次数购买</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<div class="content">
<div class="title">购买当前影片</div>
<div class="title-right" @click="emit('close')">
<SvgIcon name="closeNew" class="goldNew" />
</div>
<div class="goldBox flex-vcenter">
<div class="goldLeft">
<div class="originalPri">原价:</div>
<span>金币 {{ videoInfo.originCoins }}</span>
</div>
<div class="goldLeft">
<div class="originalPri isVip">您的购买价格:</div>
<span>金币 {{ videoInfo.coins }}</span>
</div>
</div>
<div class="goldBtn">
<div class="buyGold cancle" v-if="isVip" @click="emit('close')">稍后再说</div>
<!-- <div-->
<!-- class="buyGold"-->
<!-- v-if="walletInfo.amount < videoInfo.originCoins"-->
<!-- @click="$router.push({ path: '/recharge', query: { name: 'wallet' } })"-->
<!-- >-->
<!-- 开通会员-->
<!-- </div>-->
<div class="buyGold cancle" v-else @click.stop="unlockNow(0)">原价购买</div>
<div class="buyGold" v-if="isVip" @click.stop="unlockNow(0)">立即支付</div>
<div class="buyGold" v-else @click="$router.push({ path: '/recharge', query: { name: 'member' } })">
开通会员
</div>
</div>
<div v-if="userInfo.goldVideoCoupon">
<div class="movie" v-for="(item, index) in userInfo.goldVideoCoupon" :key="index">
<span>观影次数*{{ item.count }}</span>
<span @click.stop="selectCoupon(item)">使用次数购买</span>
</div>
</div>
</div>
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import VideoApi from '@/service/video';
import { computed } from 'vue';
import { ProductType } from '@/enums/app';
import { showToast } from 'vant';
import { useUserStore } from '@/stores/user';
import { useRouter } from 'vue-router';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
isOpen: boolean;
}>();
const { isVip, freeVideo30, userInfo, walletInfo } = useUserStore();
const router = useRouter();
const { openLoading, closeLoading } = useLoading();
const emit = defineEmits(['close', 'buySuccess']);
const isShow = computed(() => {
return props.isOpen;
});
const selectCoupon = (item: any) => {
let money = userInfo.isVip ? props.videoInfo.coins : props.videoInfo.originCoins;
if (item.gold < money) {
showToast('观影券面值过小,请选择其他支付方式');
} else {
unlockNow(item.gold);
}
};
const unlockNow = (money: any) => {
try {
openLoading();
let productID = props.videoInfo.id;
if (!productID) {
showToast('购买出错~');
return;
}
VideoApi.videoBuying({ productType: ProductType.VIDEO, productID: productID, goldVideoCouponNum: money || 0 })
.then(() => {
showToast('购买成功');
if (money) {
for (let i = 0; i < userInfo.goldVideoCoupon.length; i++) {
if (money == userInfo.goldVideoCoupon[i].gold) {
if (userInfo.goldVideoCoupon[i].count > 1) {
userInfo.goldVideoCoupon[i].count--;
} else {
userInfo.goldVideoCoupon.splice(i, 1);
}
}
}
}
useUserStore().updateWallet();
emit('buySuccess');
})
.catch((e) => {
if (e.toString().indexOf('8000') != -1) {
// router.push('/welfareCenter');
router.push('/recharge?name=wallet');
}
});
} finally {
closeLoading();
}
};
</script>
<style lang="scss" scoped>
.wrapper_gold {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.content {
width: 311px;
height: auto;
// background: url('./../../assets/images/png/popupBg.png') no-repeat;
// background-size: 100% 123px;
border-radius: 12px;
background-color: rgb(255, 255, 255);
// padding-top: 15px;
box-sizing: border-box;
text-align: center;
padding-bottom: 16px;
.title {
padding: 16px 24px;
font-size: 18px;
color: $titleColor;
font-weight: bolder;
background-color: $themeColor;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
.title-right {
font-size: 15px;
color: rgba(255, 255, 255);
font-weight: 600;
position: absolute;
right: 12px;
top: 16px;
}
.goldBox {
padding-top: 16px;
flex-direction: column;
.goldLeft {
padding-bottom: 16px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: 500;
line-height: 19.6px;
text-align: center;
color: #999999;
span {
line-height: 20px;
}
}
}
.goldContent {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 8px;
.goldLeft {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 141.5px;
height: 150px;
border: 1px solid $themeColor;
border-radius: 12px;
background: rgba(255, 214, 128, 0.35);
position: relative;
.originalPrice {
font-size: 14px;
position: absolute;
top: -10px;
left: 0px;
padding: 10px 16px;
border-radius: 16px;
border-bottom-left-radius: 0;
background: rgba(247, 63, 2, 1);
color: #ffffff;
}
.isVip {
background: $themeColor;
}
.ordinaryUser {
margin-bottom: 12px;
font-size: 16px;
color: $themeColor;
}
.golds {
display: inline-block;
.goldNew {
margin-right: 4px;
vertical-align: bottom;
width: 20px;
height: 20px;
}
span {
font-size: 16px;
font-weight: 700;
}
}
}
}
.goldBtn {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: #ffffff;
font-size: 16px;
font-weight: 600;
.cancle {
background: #F1A10099 !important;
margin-right: 12px;
}
.buyGold {
width: 139.5px;
height: 40px;
line-height: 10px;
background: $themeColor;
padding: 16px 10px;
border-radius: 100px;
}
}
.movie {
padding-top: 16px;
span:first-child {
font-size: 12px;
font-weight: 400;
line-height: 18px;
margin-right: 16px;
}
span:last-child {
color: #f1a100;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
}
.subTitle {
font-size: 12px;
color: rgb(153, 153, 153);
margin: 7px 0 36px;
}
.amount_box {
display: flex;
justify-content: center;
align-items: center;
color: rgb(255, 135, 16);
img {
width: 32px;
height: 32px;
margin-right: 4px;
}
}
.couponBox {
max-height: 100px;
margin-top: 14px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
.couponItem {
padding: 0 30px;
font-size: 14px;
margin: 5px 0;
.couponName {
color: rgb(102, 102, 102);
span {
color: rgb(255, 13, 44);
}
}
.useCouponBtn {
color: rgb(255, 13, 44);
}
}
}
.unlock_now {
width: 227px;
height: 38px;
background: rgb(255, 160, 1);
border-radius: 10px;
margin: 28px auto 20px;
font-size: 16px;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
}
.close {
padding-top: 16px;
svg {
font-size: 32px;
}
}
}
</style>
-91
View File
@@ -1,91 +0,0 @@
<template>
<div class="header flex-center-between">
<div class="back flex-center-center" @click="$router.go(-1)">
<img class="backIcon" src="@/assets/images/png/navBarIcon.png" />
</div>
<div :class="isBlod ? 'title bloder' : 'title'">{{ title }}</div>
<div class="right-btn" @click="rightBtn">
<SvgIcon class="rightIcon" :name="rightIcon" v-if="rightIcon" />
<div v-if="rightTitle">{{ rightTitle }}</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const props: any = defineProps({
title: {
type: String,
required: true,
default() {
return '标题';
},
},
rightTitle: {
type: String,
required: false,
},
rightIcon: {
type: String,
required: false,
},
path: {
type: String,
required: false,
},
isIcon: {
type: Boolean,
required: false,
default() {
return false;
},
},
isBlod: {
type: Boolean,
required: false,
default() {
return false;
},
},
});
const emit = defineEmits(['rightFuc']);
const rightBtn = () => {
if (props.path) {
router.push(props.path);
} else {
emit('rightFuc');
}
};
</script>
<style lang="scss" scoped>
.header {
height: 44px;
padding: 0 16px;
.back {
.backIcon {
width: 20px;
height: 20px;
}
}
.title {
// color: white;
color: $titleColor;
font-size: 16px;
position: absolute;
left: 50%;
transform: translate(-50%, 0);
}
.bloder {
font-weight: bolder;
}
.right-btn {
color: rgb(128, 128, 128);
font-size: 14px;
}
.rightIcon {
width: 30px;
height: 30px;
}
}
</style>
@@ -1,193 +0,0 @@
<template>
<div class="horizontaImgGroupBox" @click="openVideo">
<div class="photoBox">
<lazyImg class="cover" :src="state.videoInfo?.cover">
<div class="subscriptInfo" v-if="!state.videoInfo?.type">
<div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div>
<!-- <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">
<img src="./../../assets/images/png/vip.png" />
VIP
</div> -->
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<img src="@/assets/images/png/icon.png" />
<span>{{ state.videoInfo?.originCoins }}</span>
</div>
</div>
<div class="photoInfo flex-center-between">
<div class="watchCountInfo flex-vcenter">
<SvgIcon name="viewIcon" />
<span>{{ watchCount(state.videoInfo?.playCount) }}</span>
</div>
<div class="photoQuantityInfo flex-vcenter">
<SvgIcon name="album" />
<span>{{ watchCount(state.videoInfo?.seriesCover ? state.videoInfo?.seriesCover.length : 0) }}</span>
</div>
</div>
</lazyImg>
<div class="title ellipsis">{{ state.videoInfo?.title }}</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { useDataStore } from '@/stores/game';
import { jumpAdv } from '@/utils/index';
const router = useRouter();
const videoStore = useVideoStore();
const dataStore = useDataStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
queryPath?: string;
isOneLine?: boolean;
userInfo?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
queryPath: () => '',
isOneLine: () => false,
userInfo: () => true,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
queryPath: props.queryPath,
isOneLine: props.isOneLine,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
if (state.isMovie) {
emit('changeVideo', state.videoInfo);
} else {
dataStore.setAData(state.videoInfo);
router.push({
path: '/imagePage',
// query: {
// id: state.videoInfo.id,
// },
});
}
};
</script>
<style lang="scss" scoped>
.horizontaImgGroupBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.photoBox {
width: 111px;
border-radius: 6px;
overflow: hidden;
transform: translateZ(0px);
.cover {
height: 150px;
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 0;
right: 0;
z-index: 1;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
// background-image:linear-gradient(to right,#ffe247 ,#ffd4a3,#ffd4a3) ;
// color: #c87426;
border-radius: 0 0 6px 0;
font-size: 12px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 18px;
min-width: 27px;
border-radius: 0 6px 0 6px;
//border-radius: 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 10px;
font-weight: 400;
color: #ffffff;
}
}
.freeLabel {
font-size: 10px;
border-radius: 0 6px 0 6px;
color: #ffffff;
background-color: #1dcf00;
}
}
.photoInfo {
position: absolute;
bottom: 0;
left: 0;
height: 30px;
width: 100%;
padding: 0 3px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, black 100%);
transform: translateZ(0px);
.watchCountInfo {
color: white;
> svg {
height: 18px;
width: 18px;
}
span {
font-size: 10px;
}
}
.photoQuantityInfo {
padding: 2px 4px;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 4px;
transform: translateZ(0px);
color: white;
> svg {
height: 14px;
width: 14px;
margin-right: 3px;
}
span {
font-size: 10px;
}
}
}
}
.title {
width: 100%;
color: #ffffff;
font-size: 12px;
font-weight: 400;
line-height: 18px;
word-wrap: break-word;
}
}
}
</style>
@@ -1,224 +0,0 @@
<template>
<div class="horizontalVideoBox" @click="openVideo">
<div class="coverBox">
<lazyImg class="cover" v-if="!state.videoInfo.type" :src="state.videoInfo?.verticalCover" />
<lazyImg class="cover" v-else :src="state.videoInfo?.cover" />
<div class="subscriptInfo" v-if="!state.videoInfo.type && state.videoInfo.tagNames.length > 0">
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<span>{{ state.videoInfo.tagNames[0] }}</span>
</div>
</div>
</div>
<div :class="state.isOneLine ? 'videoTitle1' : 'videoTitle'">
<div class="title">
{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}
</div>
</div>
<div class="bloggerBox" v-if="state.videoInfo.publisher">
<div class="bloggerInfo">
<lazyImg round class="bloggerAvatar" :src="state.videoInfo?.publisher?.portrait" />
<span>{{ state.videoInfo.publisher?.name }}</span>
</div>
<div class="likeBox" v-if="state.videoInfo.publisher?.officialCert">
<SvgIcon name="isVip" class="like" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
queryPath?: string;
isOneLine?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
queryPath: () => '',
isOneLine: () => false,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
queryPath: props.queryPath,
isOneLine: props.isOneLine,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
router.push({
path: '/portrayPage',
query: {
id: state.videoInfo.id,
},
});
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.coverBox {
height: 94px;
border-radius: 6px;
overflow: hidden;
transform: translateZ(0px);
position: relative;
.videoInfo {
height: 24px;
width: 100%;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
position: absolute;
left: 0;
bottom: 0;
padding: 0 10px 0 12px;
transform: translateZ(0px);
.playCount {
.icon {
height: 10px;
width: 10px;
}
.count {
font-size: 10px;
color: rgb(248, 248, 248);
margin-left: 5px;
}
}
.playDuration {
font-size: 11px;
color: rgb(248, 248, 248);
}
}
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 5px;
right: 0px;
z-index: 1;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
// background-image:linear-gradient(to right,#ffe247 ,#ffd4a3,#ffd4a3) ;
// color: #c87426;
// border-radius: 2px 2px 0 0;
font-size: 8px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 16px;
min-width: 27px;
// border-radius: 0 0 6px 0;
border-radius: 2px 0 0 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 12px;
font-weight: 700;
color: rgb(248, 248, 248);
}
}
.freeLabel {
color: #598300;
background-color: #d8ffa6;
}
}
}
.videoTitle {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 26px;
margin-top: 8px;
.title {
font-size: 12px;
// color: rgba(248, 248, 248, 0.8);
color: #ffffff;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.videoTitle1 {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 30px;
margin-top: 8px;
.title {
font-size: 14px;
// color: rgba(248, 248, 248, 0.8);
color: rgb(38, 38, 38);
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.bloggerBox {
display: flex;
align-items: center;
font-size: 12px;
color: rgba(166, 166, 166, 1);
.likeBox {
display: flex;
justify-content: center;
align-items: center;
background: $themeColor;
width: 16px;
height: 10px;
border-radius: 2px;
margin-left: 8px;
.like {
width: 8px;
height: 8px;
}
}
.bloggerInfo {
display: flex;
align-items: center;
}
.bloggerAvatar {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
}
</style>
@@ -1,294 +0,0 @@
<template>
<div class="horizontalVideoBox" @click="openVideo">
<div class="coverBox">
<lazyImg class="cover" v-if="!state.videoInfo.type" :src="state.videoInfo?.verticalCover" />
<lazyImg class="cover" v-else :src="state.videoInfo?.cover" />
<!-- <div class="videoInfo flex-center-between" v-if="!state.videoInfo.type">-->
<!-- <div class="playCount flex-center-center">-->
<!-- <SvgIcon class="icon" name="playIcon" />-->
<!-- <div class="count">{{ watchCount(state.videoInfo?.playCount) }}</div>-->
<!-- </div>-->
<!-- <div class="playDuration">{{ videoTime(state.videoInfo?.playTime) }}</div>-->
<!-- </div>-->
<!-- <div class="subscriptInfo" v-if="!state.videoInfo.type">-->
<!-- &lt;!&ndash; <div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div> &ndash;&gt;-->
<!-- &lt;!&ndash; <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">-->
<!-- <img src="./../../assets/images/png/vip.png" />-->
<!-- VIP-->
<!-- </div> &ndash;&gt;-->
<!-- <div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">-->
<!-- <img src="./../../assets/images/png/icon.png" />-->
<!-- <span>{{ state.videoInfo?.originCoins }}</span>-->
<!-- </div>-->
<!-- </div>-->
<div class="subscriptInfo" v-if="!state.videoInfo.type && state.videoInfo.tagNames.length > 0">
<!-- <div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div> -->
<!-- <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">
<img src="./../../assets/images/png/vip.png" />
VIP
</div> -->
<!-- <div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">-->
<!-- <img src="./../../assets/images/png/icon.png" />-->
<!-- <span>{{ state.videoInfo?.originCoins }}</span>-->
<!-- </div>-->
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<span>{{ state.videoInfo.tagNames[0] }}</span>
</div>
</div>
</div>
<div :class="state.isOneLine ? 'videoTitle1' : 'videoTitle'">
<div class="title">
{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}
</div>
</div>
<div class="bloggerBox" v-if="state.videoInfo.publisher">
<div class="bloggerInfo">
<lazyImg round class="bloggerAvatar" :src="state.videoInfo?.publisher?.portrait" />
<span>{{ state.videoInfo.publisher?.name }}</span>
</div>
<div class="likeBox" v-if="state.videoInfo.publisher?.officialCert">
<SvgIcon name="isVip" class="like" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
queryPath?: string;
isOneLine?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
queryPath: () => '',
isOneLine: () => false,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
queryPath: props.queryPath,
isOneLine: props.isOneLine,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
router.push({
path: '/novelDetailsPage',
query: {
id: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
},
});
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// if (state.videoInfo.newsType == 'COVER') {
// // router.push({
// // path: '/postDetails',
// // query: {
// // postId: state.videoInfo.id,
// // },
// // });
// } else {
// if (state.videoInfo.playTime > 300) {
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// return;
// } else {
// videoStore.deleteVideoList();
// videoStore.saveVideoList(state.videoList, state.videoInfo);
// router.push('/shortVideoDetails');
// }
// }
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.coverBox {
height: 94px;
border-radius: 6px;
overflow: hidden;
transform: translateZ(0px);
position: relative;
.videoInfo {
height: 24px;
width: 100%;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
position: absolute;
left: 0;
bottom: 0;
padding: 0 10px 0 12px;
transform: translateZ(0px);
.playCount {
.icon {
height: 10px;
width: 10px;
}
.count {
font-size: 10px;
color: rgb(248, 248, 248);
margin-left: 5px;
}
}
.playDuration {
font-size: 11px;
color: rgb(248, 248, 248);
}
}
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 5px;
right: 0px;
z-index: 1;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
// background-image:linear-gradient(to right,#ffe247 ,#ffd4a3,#ffd4a3) ;
// color: #c87426;
// border-radius: 2px 2px 0 0;
font-size: 8px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 16px;
min-width: 27px;
// border-radius: 0 0 6px 0;
border-radius: 2px 0 0 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 12px;
font-weight: 700;
color: rgb(248, 248, 248);
}
}
.freeLabel {
color: #598300;
background-color: #d8ffa6;
}
}
}
.videoTitle {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 26px;
margin-top: 8px;
.title {
font-size: 12px;
// color: rgba(248, 248, 248, 0.8);
color: #ffffff;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.videoTitle1 {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 30px;
margin-top: 8px;
.title {
font-size: 14px;
// color: rgba(248, 248, 248, 0.8);
color: rgb(38, 38, 38);
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.bloggerBox {
display: flex;
align-items: center;
font-size: 12px;
color: rgba(166, 166, 166, 1);
.likeBox {
display: flex;
justify-content: center;
align-items: center;
background: $themeColor;
width: 16px;
height: 10px;
border-radius: 2px;
margin-left: 8px;
.like {
width: 8px;
height: 8px;
}
}
.bloggerInfo {
display: flex;
align-items: center;
}
.bloggerAvatar {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
}
</style>
@@ -1,249 +0,0 @@
<template>
<div class="horizontalVideoBox" @click="openVideo">
<div class="coverBox">
<lazyImg class="cover" :src="state.videoInfo?.cover" />
<div class="videoInfo flex-center-between" v-if="!state.videoInfo.type">
<div class="playCount flex-center-center">
<SvgIcon class="icon" name="playIcon" />
<div class="count">{{ watchCount(state.videoInfo?.playCount) }}</div>
</div>
<div class="playDuration">{{ videoTime(state.videoInfo?.playTime) }}</div>
</div>
<div class="subscriptInfo" v-if="!state.videoInfo.type">
<div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div>
<!-- <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">
<img src="./../../assets/images/png/vip.png" />
VIP
</div> -->
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<img src="./../../assets/images/png/icon.png" />
<span>{{ state.videoInfo?.originCoins }}</span>
</div>
</div>
</div>
<div class="videoTitle">
<div class="title">
{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}
</div>
</div>
<div
class="bloggerBox"
v-if="state.videoInfo.publisher && userInfo"
@click.stop="
$router.push({
path: '/personal',
query: {
uid: state.videoInfo.publisher.uid,
hasFollowed: state.videoInfo.publisher.hasFollowed.toString(),
cover: state.videoInfo.publisher.portrait,
},
})
"
>
<div class="bloggerInfo">
<lazyImg round class="bloggerAvatar" :src="state.videoInfo?.publisher?.portrait" />
<span>{{ state.videoInfo.publisher?.name }}</span>
</div>
<div class="likeBox" v-if="state.videoInfo.publisher?.officialCert">
<SvgIcon name="isVip" class="like" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
userInfo?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
userInfo: () => true,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
if (state.videoInfo.newsType == 'COVER') {
// router.push({
// path: '/postDetails',
// query: {
// postId: state.videoInfo.id,
// },
// });
} else {
if (state.videoInfo.playTime > 300) {
if (state.isMovie) {
emit('changeVideo', state.videoInfo);
} else {
router.push({
path: '/movieDetails',
query: {
vid: state.videoInfo.id,
uid: state.videoInfo.publisher?.uid,
},
});
}
return;
} else {
videoStore.deleteVideoList();
videoStore.saveVideoList(state.videoList, state.videoInfo);
router.push('/shortVideoDetails');
}
}
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.coverBox {
height: 94px;
border-radius: 10px 10px 0 0;
overflow: hidden;
transform: translateZ(0px);
position: relative;
background: linear-gradient(to right, rgba(253, 191, 80, 0.7), rgba(255, 224, 167, 0.7));
.videoInfo {
height: 24px;
width: 100%;
background: linear-gradient(to bottom, rgba(184, 184, 209, 0), rgba(159, 159, 178, 0.52));
position: absolute;
left: 0;
bottom: 0;
padding: 0 4px 0 4px;
transform: translateZ(0px);
.playCount {
.icon {
height: 16px;
width: 16px;
color: #fff;
}
.count {
font-size: 11px;
color: white;
margin-left: 2px;
}
}
.playDuration {
font-size: 11px;
color: white;
}
}
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 0;
right: 0;
z-index: 1;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
border-radius: 0 0 6px 0;
font-size: 12px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 18px;
min-width: 27px;
border-radius: 0 8px 0 6px;
//border-radius: 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 10px;
font-weight: 400;
color: #ffffff;
}
}
.freeLabel {
font-size: 10px;
border-radius: 0 8px 0 6px;
color: #ffffff;
background-color: #1dcf00;
}
}
}
.videoTitle {
//background-color: rgb(255, 255, 251);
//border-radius: 0 0 10px 10px;
padding: 4px 0;
box-sizing: border-box;
.title {
font-size: 12px;
color: #ffffff;
font-weight: 400;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.bloggerBox {
display: flex;
align-items: center;
font-size: 12px;
color: rgba(166, 166, 166, 1);
.likeBox {
display: flex;
justify-content: center;
align-items: center;
background: $themeColor;
width: 16px;
height: 10px;
border-radius: 2px;
margin-left: 8px;
.like {
width: 8px;
height: 8px;
}
}
.bloggerInfo {
display: flex;
align-items: center;
}
.bloggerAvatar {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
}
</style>
-307
View File
@@ -1,307 +0,0 @@
<template>
<div class="horizontalVideoBox" @click="openVideo">
<div class="coverBox">
<lazyImg class="cover" :src="state.videoInfo?.cover" />
<div class="videoInfo flex-center-between" v-if="!state.videoInfo.type">
<div class="playCount flex-center-center">
<SvgIcon class="icon" name="playIcon" />
<div class="count">{{ watchCount(state.videoInfo?.playCount) }}</div>
</div>
<div class="playDuration">{{ videoTime(state.videoInfo?.playTime) }}</div>
</div>
<div class="subscriptInfo" v-if="!state.videoInfo.type">
<div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div>
<!-- <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">
<img src="./../../assets/images/png/vip.png" />
VIP
</div> -->
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<img src="./../../assets/images/png/icon.png" />
<span>{{ state.videoInfo?.originCoins }}</span>
</div>
</div>
<!-- <div class="subscriptInfoAdv" v-else>广告</div>-->
</div>
<div :class="state.isOneLine ? 'videoTitle1' : 'videoTitle'">
<div class="title">
{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}
</div>
</div>
<div
class="bloggerBox"
v-if="state.videoInfo.publisher && userInfo"
@click.stop="
$router.push({
path: '/personal',
query: {
uid: state.videoInfo.publisher.uid,
// hasFollowed: state.videoInfo.publisher.hasFollowed.toString(),
// cover: state.videoInfo.publisher.portrait,
},
})
"
>
<div class="bloggerInfo">
<lazyImg round class="bloggerAvatar" :src="state.videoInfo?.publisher?.portrait" />
<span>{{ state.videoInfo.publisher?.name }}</span>
</div>
<div class="likeBox" v-if="state.videoInfo.publisher?.officialCert">
<SvgIcon name="isVip" class="like" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
queryPath?: string;
isOneLine?: boolean;
userInfo?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
queryPath: () => '',
isOneLine: () => false,
userInfo: () => true,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
queryPath: props.queryPath,
isOneLine: props.isOneLine,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
if (state.isMovie) {
emit('changeVideo', state.videoInfo);
} else {
router.push({
path: '/movieDetails',
query: {
vid: state.videoInfo.id,
uid: state.videoInfo.publisher?.uid,
queryPath: state.queryPath,
},
});
}
// if (state.videoInfo.newsType == 'COVER') {
// // router.push({
// // path: '/postDetails',
// // query: {
// // postId: state.videoInfo.id,
// // },
// // });
// } else {
// if (state.videoInfo.playTime > 300) {
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// return;
// } else {
// videoStore.deleteVideoList();
// videoStore.saveVideoList(state.videoList, state.videoInfo);
// router.push('/shortVideoDetails');
// }
// }
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.coverBox {
height: 94px;
border-radius: 6px;
overflow: hidden;
transform: translateZ(0px);
position: relative;
.videoInfo {
height: 24px;
width: 100%;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
position: absolute;
left: 0;
bottom: 0;
padding: 0 4px 0 4px;
transform: translateZ(0px);
.playCount {
.icon {
height: 16px;
width: 16px;
color: #ffffff;
}
.count {
font-size: 10px;
color: rgb(248, 248, 248);
margin-left: 2px;
}
}
.playDuration {
font-size: 11px;
color: rgb(248, 248, 248);
}
}
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 0;
right: 0;
z-index: 1;
font-family: PingFang SC, serif;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
// background-image:linear-gradient(to right,#ffe247 ,#ffd4a3,#ffd4a3) ;
// color: #c87426;
border-radius: 0 0 6px 0;
font-size: 12px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 18px;
min-width: 27px;
border-radius: 0 6px 0 6px;
//border-radius: 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 10px;
font-weight: 400;
color: #ffffff;
}
}
.freeLabel {
font-size: 10px;
border-radius: 0 6px 0 6px;
color: #ffffff;
font-weight: 400;
background-color: #1dcf00;
}
}
.subscriptInfoAdv {
transform: translateZ(0px);
position: absolute;
top: 0;
left: 0;
z-index: 1;
height: 16px;
width: 34px;
border-radius: 0 0 4px 0;
font-size: 11px;
font-weight: 500;
color: rgb(0, 0, 0);
background-color: $themeColor;
text-align: center;
line-height: 16px;
}
}
.videoTitle {
padding: 2px 0 8px;
box-sizing: border-box;
height: 22px;
margin-top: 2px;
.title {
font-size: 12px;
// color: rgba(248, 248, 248, 0.8);
color: #ffffff;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.videoTitle1 {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 22px;
margin-top: 2px;
.title {
font-size: 14px;
// color: rgba(248, 248, 248, 0.8);
color: rgb(38, 38, 38);
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.bloggerBox {
display: flex;
align-items: center;
font-size: 12px;
color: rgba(166, 166, 166, 1);
.likeBox {
display: flex;
justify-content: center;
align-items: center;
background: $themeColor;
width: 16px;
height: 10px;
border-radius: 2px;
margin-left: 8px;
.like {
width: 8px;
height: 8px;
}
}
.bloggerInfo {
display: flex;
align-items: center;
}
.bloggerAvatar {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
}
</style>
@@ -1,265 +0,0 @@
<template>
<div class="horizontalVideoBox">
<HorizontalVideo
:videoInfo="videoInfo"
@goldBuyShow="goldBuyShow"
@showPreviewTime="showPreviewTime"
ref="verticalVideoRef"
class="videoBox"
/>
<!-- <HorizontalVideo
:videoInfo="videoInfo"
@getFreeCount="getFreeCount"
@goldBuyShow="goldBuyShow"
@showPreviewTime="showPreviewTime"
ref="verticalVideoRef"
class="videoBox"
/> -->
<!-- <div class="tipBox" v-if="!userInfo.isVip && userFreeCount.watchCount > 0 && videoInfo.originCoins == 0">
免费观影数: {{ userFreeCount.watchCount }}次
</div>
<div class="tipBox" v-else-if="freeVideo30 && videoInfo.originCoins < 30 && videoInfo.originCoins > 0">
已享免费特权
</div> -->
<div class="tipBox" v-if="videoInfo.vidStatus.hasPaid">已购买完整版</div>
<!-- <div class="tipBox" v-else-if="!isVip && !userFreeCount.isCan && videoInfo.originCoins == 0">
预览
<span>{{ isPreview ? previewTime : videoInfo.freeTime }}S</span>
,充值会员观看完整版
</div> -->
<div class="tipBox" v-else-if="videoInfo.originCoins > 0 && !videoInfo.vidStatus.hasPaid">
预览
<span>{{ isPreview ? previewTime : videoInfo.freeTime }}S</span>
,购买解锁观看完整版
</div>
<!-- 金币不足做任务提示 -->
<div class="goldBox" v-if="isGoldCoinPurchase">
<!-- <p>金币不足快去做任务吧~</p> -->
<p>观看完整版需要</p>
<!-- <h6>观看完整版需开通会员</h6> -->
<h5>
<span>{{ isVip ? videoInfo.coins : videoInfo.originCoins }}</span>
<img class="transactionGoldIcon" src="@/assets/images/png/icon.png" />
</h5>
<div class="unlock_now">
<div class="button_normal" @click="isShowBuyDialog = true">金币解锁</div>
<div class="button" @click="$router.push({ path: '/recharge', query: { name: 'wallet' } })">购买金币</div>
</div>
</div>
<!-- 金币弹窗 -->
<GoldPopupNew
:is-open="isShowBuyDialog"
:video-info="videoInfo"
@close="isShowBuyDialog = false"
@buy-success="buySuccess"
/>
<!-- 会员弹窗 -->
<!-- <MemberPopup :videoInfo="videoInfo" :is-open="isJoinMerbershipShow" @close="isJoinMerbershipShow = false" /> -->
</div>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import { Ref, ref } from 'vue';
import { useUserStore } from '@/stores/user';
import { showToast } from 'vant';
import VideoApi from '@/service/video';
import { ProductType } from '@/enums/app';
const verticalVideoRef = ref();
// let isJoinMerbershipShow = ref(false); //会员购买
let isGoldCoinPurchase = ref(false); //金币购买
let isShowBuyDialog = ref(false);
let isPreview = ref(false); // 是否预览视频
let previewTime = ref(0); // 预览视频时间
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
}>();
const { isVip, freeVideo30, userInfo, walletInfo } = useUserStore();
let userFreeCount: Ref<Types.Video.UserFreeCount> = ref({
isCan: false,
watchCount: 0,
});
const { openLoading, closeLoading } = useLoading();
/**
* @description 判断是否预览视频
*/
const checkPreview = () => {
if (
!props.videoInfo.vidStatus.hasPaid &&
!(freeVideo30 && props.videoInfo.originCoins < 30 && props.videoInfo.originCoins != 0) &&
!(props.videoInfo.originCoins === 0 && isVip) &&
!(userInfo.uid === props.videoInfo.publisher.uid) &&
// !(userFreeCount.value.isCan === true && props.videoInfo.originCoins === 0) &&
// props.videoInfo.previewURL
// )
props.videoInfo.previewURL
) {
// console.log('1-------------------');
isPreview.value = true;
} else {
// console.log('2-------------------');
isPreview.value = false;
}
};
const unlockNow = (money: any) => {
try {
openLoading();
let productID = props.videoInfo.id;
if (!productID) {
showToast('购买出错~');
return;
}
VideoApi.videoBuying({ productType: ProductType.VIDEO, productID: productID, goldVideoCouponNum: money || 0 })
.then(() => {
showToast('购买成功');
useUserStore().updateWallet();
verticalVideoRef.value.success();
})
.catch((e) => {
console.log(e);
});
} finally {
closeLoading();
}
};
// 获取观影券--看是有有可以使用的
const selectCoupon = () => {
if (userInfo?.goldVideoCoupon && props.videoInfo.coins != 0) {
//如果有观影券--判断是否符合面值 && 是金币视频
let money = userInfo.isVip ? props.videoInfo.coins : props.videoInfo.originCoins;
let goldVideoCoupon = userInfo.goldVideoCoupon;
let buy = '';
goldVideoCoupon.forEach((item: any) => {
if (item.gold >= money) {
buy = item.gold;
}
});
if (buy) {
//如果有符合面值的观影券--则购买
unlockNow(buy);
} else {
checkPreview();
}
} else {
checkPreview();
}
};
// selectCoupon();
//免费观看视频次数
const getFreeCount = (userCount: Types.Video.UserFreeCount) => {
userFreeCount.value = userCount;
};
const showPreviewTime = (time: number) => {
previewTime.value = parseInt(time.toString());
};
//会员购买弹窗
// const vipBuyShow = () => {
// isJoinMerbershipShow.value = true;
// };
//金币购买弹窗
const goldBuyShow = () => {
// 如果用户余额小于观看视频的金币数 --- 弹出做任务
// if (walletInfo.amount < props.videoInfo.originCoins) {
// // isGoldCoinPurchase.value = true;
// } else {
// // 弹出购买弹框
// isShowBuyDialog.value = true;
// }
isShowBuyDialog.value = true;
};
/**
* @description 视频购买成功
*/
const buySuccess = () => {
isShowBuyDialog.value = false;
verticalVideoRef.value.success();
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
position: relative;
.videoBox {
height: 212px;
}
.tipBox {
position: absolute;
z-index: 999;
top: 11px;
right: 14px;
background-color: rgba(6, 15, 33, 0.3);
font-size: 11px;
color: rgb(242, 242, 242);
padding: 6px 20px;
border-radius: 20px;
span {
color: rgb(255, 13, 44);
}
}
.goldBox {
width: 100%;
height: 212px;
position: absolute;
top: 0;
z-index: 1000;
background: rgb(0, 0, 0, 0.5);
padding: 40px 0 0 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
color: #fff;
font-size: 16px;
line-height: 25px;
h6 {
font-size: 14px;
}
h5 {
display: flex;
align-items: center;
justify-content: center;
margin: 5px 0 10px 0;
span {
font-size: 16px;
padding: 0 5px;
box-sizing: border-box;
}
img {
width: 20px;
height: 20px;
}
}
.unlock_now {
display: flex;
div {
width: 84px;
height: 32px;
border-radius: 5px;
text-align: center;
font-size: 14px;
line-height: 32px;
}
.button {
background: #ffd9a4;
color: #dd6a00;
}
.button_normal {
border: solid 2px #fff;
margin: 0 20px 0 0;
}
// width: 107px;
// height: 38px;
// background: rgb(255, 160, 1);
// border-radius: 20px;
// font-size: 16px;
// color: #ffffff;
// display: flex;
// justify-content: center;
// align-items: center;
}
}
}
</style>
@@ -1,296 +0,0 @@
<template>
<div class="horizontalVideoBox" @click="openVideo">
<div class="coverBox">
<lazyImg class="cover" v-if="!state.videoInfo.type" :src="state.videoInfo?.verticalCover" />
<lazyImg class="cover" v-else :src="state.videoInfo?.cover" />
<!-- <div class="videoInfo flex-center-between" v-if="!state.videoInfo.type">-->
<!-- <div class="playCount flex-center-center">-->
<!-- <SvgIcon class="icon" name="playIcon" />-->
<!-- <div class="count">{{ watchCount(state.videoInfo?.playCount) }}</div>-->
<!-- </div>-->
<!-- <div class="playDuration">{{ videoTime(state.videoInfo?.playTime) }}</div>-->
<!-- </div>-->
<!-- <div class="subscriptInfo" v-if="!state.videoInfo.type">-->
<!-- &lt;!&ndash; <div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div> &ndash;&gt;-->
<!-- &lt;!&ndash; <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">-->
<!-- <img src="./../../assets/images/png/vip.png" />-->
<!-- VIP-->
<!-- </div> &ndash;&gt;-->
<!-- <div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">-->
<!-- <img src="./../../assets/images/png/icon.png" />-->
<!-- <span>{{ state.videoInfo?.originCoins }}</span>-->
<!-- </div>-->
<!-- </div>-->
<div class="subscriptInfo" v-if="!state.videoInfo.type && state.videoInfo.tagNames.length > 0">
<!-- <div class="label freeLabel" v-if="state.videoInfo?.coins == 0">免费</div> -->
<!-- <div class="label" v-if="state.videoInfo?.originCoins == 0 && !state.videoInfo?.freeArea">
<img src="./../../assets/images/png/vip.png" />
VIP
</div> -->
<!-- <div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">-->
<!-- <img src="./../../assets/images/png/icon.png" />-->
<!-- <span>{{ state.videoInfo?.originCoins }}</span>-->
<!-- </div>-->
<div class="goldCoinLabel flex-center-center" v-if="state.videoInfo?.coins != 0">
<span>{{ state.videoInfo.tagNames[0] }}</span>
</div>
</div>
</div>
<div :class="state.isOneLine ? 'videoTitle1' : 'videoTitle'">
<div class="title">
{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}
</div>
</div>
<div class="bloggerBox" v-if="state.videoInfo.publisher">
<div class="bloggerInfo">
<lazyImg round class="bloggerAvatar" :src="state.videoInfo?.publisher?.portrait" />
<span>{{ state.videoInfo.publisher?.name }}</span>
</div>
<div class="likeBox" v-if="state.videoInfo.publisher?.officialCert">
<SvgIcon name="isVip" class="like" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime, addVideoHistory } from '@/utils/index';
import { useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
import { HistoryVideoType } from '@/enums/app';
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: Types.Video.VInfosItem;
videoList: Types.Video.VInfosItem[];
isMovie?: boolean;
queryPath?: string;
isOneLine?: boolean;
}>(),
{
videoInfo: () => <Types.Video.VInfosItem>{},
videoList: () => [],
isMovie: () => false,
queryPath: () => '',
isOneLine: () => false,
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
videoList: props.videoList,
isMovie: props.isMovie,
queryPath: props.queryPath,
isOneLine: props.isOneLine,
};
const openVideo = () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
router.push({
path: '/novelDetailsPage',
query: {
id: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
},
});
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// if (state.videoInfo.newsType == 'COVER') {
// // router.push({
// // path: '/postDetails',
// // query: {
// // postId: state.videoInfo.id,
// // },
// // });
// } else {
// if (state.videoInfo.playTime > 300) {
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// return;
// } else {
// videoStore.deleteVideoList();
// videoStore.saveVideoList(state.videoList, state.videoInfo);
// router.push('/shortVideoDetails');
// }
// }
};
</script>
<style lang="scss" scoped>
.horizontalVideoBox {
height: 100%;
width: 100%;
margin-bottom: 10px;
.coverBox {
height: 94px;
border-radius: 6px;
overflow: hidden;
transform: translateZ(0px);
position: relative;
.videoInfo {
height: 24px;
width: 100%;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
position: absolute;
left: 0;
bottom: 0;
padding: 0 10px 0 12px;
transform: translateZ(0px);
.playCount {
.icon {
height: 10px;
width: 10px;
}
.count {
font-size: 10px;
color: rgb(248, 248, 248);
margin-left: 5px;
}
}
.playDuration {
font-size: 11px;
color: rgb(248, 248, 248);
}
}
.subscriptInfo {
transform: translateZ(0px);
position: absolute;
top: 5px;
right: 0px;
z-index: 1;
.label {
padding: 0 5px;
box-sizing: border-box;
height: 18px;
min-width: 34px;
// background-image:linear-gradient(to right,#ffe247 ,#ffd4a3,#ffd4a3) ;
// color: #c87426;
// border-radius: 2px 2px 0 0;
font-size: 8px;
text-align: center;
line-height: 18px;
> img {
height: 10px;
width: 10px;
}
}
.goldCoinLabel {
padding: 0 4px;
box-sizing: border-box;
// background: rgba(0, 0, 0, 0.6);
//border: 0.5px solid #ffad00;
background: linear-gradient(0deg, #ff0000, #ff0000);
height: 16px;
min-width: 27px;
// border-radius: 0 0 6px 0;
border-radius: 2px 0 0 2px;
> img {
height: 10px;
width: 10px;
margin-right: 3.3px;
}
span {
font-size: 12px;
font-weight: 700;
color: rgb(248, 248, 248);
}
}
.freeLabel {
color: #598300;
background-color: #d8ffa6;
}
}
}
.videoTitle {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 26px;
margin-top: 8px;
.title {
font-size: 12px;
// color: rgba(248, 248, 248, 0.8);
color: #ffffff;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.videoTitle1 {
padding: 2px 0px 8px;
box-sizing: border-box;
height: 30px;
margin-top: 8px;
.title {
font-size: 14px;
// color: rgba(248, 248, 248, 0.8);
color: rgb(38, 38, 38);
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.bloggerBox {
display: flex;
align-items: center;
font-size: 12px;
color: rgba(166, 166, 166, 1);
.likeBox {
display: flex;
justify-content: center;
align-items: center;
background: $themeColor;
width: 16px;
height: 10px;
border-radius: 2px;
margin-left: 8px;
.like {
width: 8px;
height: 8px;
}
}
.bloggerInfo {
display: flex;
align-items: center;
}
.bloggerAvatar {
width: 18px;
height: 18px;
border-radius: 50%;
margin-right: 8px;
}
}
}
</style>
-209
View File
@@ -1,209 +0,0 @@
<template>
<div class="InformationPopup">
<van-popup v-model:show="state.isRewardShow" :style="{ backgroundColor: 'rgba(0, 0, 0, 0)' }">
<div class="popupBox">
<div class="listbox">
<p class="flex-center-center">我的地址</p>
</div>
<div class="contentBox flex-center-center">
<div class="provinces flex-center-between">
<div class="text">省份:</div>
<input
type="text"
:value="state.selectedProvince"
@click.stop="state.showDropdown = !state.showDropdown"
readonly
/>
<SvgIcon
name="informationTel"
@click="state.showDropdown = !state.showDropdown"
class="informationTel"
></SvgIcon>
<div v-if="state.showDropdown" class="provincesPop overflow-y-auto">
<div v-for="province in provinces" :key="province" @click="selectProvince(province)">
{{ province }}
</div>
</div>
</div>
<div class="provinces flex-center-between">
<div class="text">城市:</div>
<input
type="text"
:value="state.selectedCity"
:disabled="state.isDisabled"
@click="state.showCity = !state.showCity"
readonly
/>
<SvgIcon
v-if="!state.isDisabled"
name="informationTel"
@click="state.showCity = !state.showCity"
class="informationTel"
></SvgIcon>
<div v-if="state.showCity" class="provincesPop overflow-y-auto">
<div v-for="city in citys" :key="city" @click="selectProvince(city)">
{{ city }}
</div>
</div>
</div>
<div class="provinces flex-center-between">
<div class="text">暗号:</div>
<input type="text" :value="state.signal" />
</div>
<div class="provinces flex-center-between">
<div class="text">联系方式:</div>
<input type="tel" :value="state.information" />
</div>
<div class="tips">温馨提示:请填写准确信息,方便联系安排</div>
<!-- <div class="btn" @click="state.isRewardShow = false">取消</div> -->
<div class="BindBtn" @click="confirm">确认</div>
</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { reactive, watchEffect } from 'vue';
import { useConfigStore } from '@/stores/config';
const configStore = useConfigStore();
const state = reactive({
isRewardShow: false,
showDropdown: false,
selectedProvince: '',
selectedCity: '',
signal: '',
information: '',
isDisabled: true,
showCity: false,
});
watchEffect(() => {
state.isDisabled = state.selectedProvince.trim() === '';
});
const provinces = ['广东省', '湖南省', '湖北省', '河南省', '山东省'];
const citys = ['广东视', '广东视'];
const selectProvince = (province: string) => {
// console.log(province);
state.selectedProvince = province;
state.showDropdown = false;
};
const openPop = () => {
state.isRewardShow = true;
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.InformationPopup {
.popupBox {
width: 313px;
height: 405px;
background-image: url('@/assets/images/png/informationPopBg.png');
background-size: 100% 100%;
overflow: hidden;
position: relative;
.listbox {
width: 100%;
height: 60px;
padding: 0 15px;
box-sizing: border-box;
margin-bottom: 10px;
position: relative;
p {
padding-top: 20px;
text-align: center;
font-weight: 600;
color: #000;
font-size: 16px;
line-height: 22px;
}
}
}
.contentBox {
margin: 0 auto;
width: 280px;
height: 318px;
border: 1px solid rgba(255, 173, 0, 1);
border-radius: 12px;
flex-direction: column;
background-color: #ffffff;
padding: 12px;
.provinces {
width: 100%;
height: 32px;
margin: 12px;
.text {
white-space: nowrap;
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: rgba(119, 119, 119, 1);
}
input {
width: 214px;
height: 32px;
background: rgba(255, 214, 128, 0.35);
border: none;
color: #000;
font-size: 14px;
font-weight: 600;
padding-left: 12px;
}
.informationTel {
position: absolute;
right: 12px;
top: 10px;
width: 16px;
height: 16px;
}
.provincesPop {
position: absolute;
right: 0;
top: 35px;
width: 214px;
height: 200px;
background-color: #ffffff;
color: #000;
z-index: 9999;
line-height: 24px;
text-align: center;
> div {
font-size: 14px;
font-weight: 600;
padding: 4px;
border-bottom: 1px solid rgb(119, 119, 119);
}
}
}
.tips {
color: rgba(255, 173, 0, 1);
font-family: PingFang SC;
font-size: 10px;
font-weight: 400;
line-height: 22px;
text-align: left;
}
.BindBtn {
text-align: center;
line-height: 40px;
color: #ffffff;
width: 248px;
height: 40px;
border-radius: 100px;
background-color: $themeColor;
}
}
:deep(.van-popup) {
// border-radius: 12px;
}
:deep(.van-overlay) {
background-color: none;
backdrop-filter: blur(0px);
}
}
</style>
-20
View File
@@ -1,20 +0,0 @@
<template>
<div class="loading_box">
<img class="loading" :src="loadingSrc" rel="external nofollow" />
</div>
</template>
<script lang="ts" setup>
import loadingSrc from '@/assets/gif/loading.gif';
</script>
<style lang="scss" scoped>
.loading_box {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
img {
width: 30px;
}
}
</style>
-116
View File
@@ -1,116 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper">
<div class="content">
<div class="title">解锁会员</div>
<h2>您还不是会员哟~</h2>
<p>
开通
<span>VIP</span>
{{ state.tip }}
</p>
<div class="btn-box flex-center-between">
<div class="btn share" @click.stop="$router.push('/promotionCode')">分享邀请</div>
<div class="btn" @click.stop="joinMembership">立即开通</div>
</div>
</div>
<SvgIcon name="close" class="close" @click="emit('close')" />
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue';
import { useRouter } from 'vue-router';
const props = defineProps<{
videoInfo?: Types.Video.VInfosItem;
isOpen: boolean;
tip?: string;
}>();
const emit = defineEmits(['close']);
const isShow = computed(() => {
return props.isOpen;
});
const router = useRouter();
const state = reactive({
tip: props.tip || '观看完成版视频',
});
/**
* @description 开通会员
*/
const joinMembership = () => {
router.push({
path: '/recharge',
query: {
name: 'member',
id: props.videoInfo?.id,
title: props.videoInfo?.title || '无标题',
},
});
};
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.content {
height: 187px;
width: 312px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
margin: 0 auto;
text-align: center;
border-radius: 6px;
padding-top: 19px;
box-sizing: border-box;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
margin-bottom: 14px;
font-weight: bold;
}
h2 {
font-size: 14px;
color: rgb(153, 153, 153);
}
p {
font-size: 14px;
color: rgb(38, 38, 38);
line-height: 26px;
margin-top: 10px;
span {
color: rgb(255, 160, 1);
}
}
.btn-box {
padding: 0 22px;
.btn {
height: 38px;
width: 129px;
text-align: center;
line-height: 38px;
border-radius: 10px;
font-size: 14px;
background-color: $themeColor;
color: white;
margin-top: 26px;
}
.share {
border: 1.5px solid rgb(153, 153, 153);
background-color: unset;
color: rgb(153, 153, 153);
}
}
}
.close {
height: 32px;
width: 32px;
margin-top: 20px;
}
}
</style>
-115
View File
@@ -1,115 +0,0 @@
<template>
<div class="nineGridPopup">
<van-overlay :show="isShow && appList.length > 0" z-index="2001" @click="emit('close')">
<div class="wrapper">
<!-- <AdvSwiper @click.stop class="topAdv" :isShowIndicators="true" :list="topList" v-if="topList.length > 0" />-->
<div class="appList">
<div v-for="(item, index) in appList.slice(0, 16)" :key="item.id">
<div class="appItem" @click.stop="jumpAdv(item)">
<lazyImg class="appIcon" :src="item.cover" />
<div class="download">{{ item.title }}</div>
</div>
</div>
</div>
<div class="close"></div>
</div>
</van-overlay>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { getAdItem } from '@/utils/getConfig';
import { AdType } from '@/enums/app';
import { jumpAdv } from '@/utils/index';
import { useGlobalStore } from '@/stores/global';
const globalStore = useGlobalStore();
const props = defineProps<{
isOpen: boolean;
}>();
const emit = defineEmits(['close']);
const isShow = computed(() => {
return props.isOpen;
});
const appList = getAdItem(AdType.QualityApplication);
// const topList = getAdItem(AdType.TopAdvsList);
const init = () => {
if (appList.length > 0) {
globalStore.setNineGridFirst();
}
};
init();
</script>
<style lang="scss" scoped>
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.9);
}
:deep(.adv-container) {
height: 88px !important;
.van-swipe-item {
height: 88px !important;
}
}
.nineGridPopup {
height: 100%;
width: 100%;
position: relative;
.wrapper {
width: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.topAdv {
width: 329px;
height: 88px;
border-radius: 4px;
overflow: hidden;
margin: 20px auto;
box-sizing: border-box;
}
.appList {
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-row-gap: 18px;
grid-column-gap: 12px;
padding: 0 33px;
.appItem {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
//height: 140px;
.appIcon {
width: 68px;
height: 68px;
border-radius: 6px;
overflow: hidden;
margin-bottom: 8px;
}
.download {
//height: 26px;
width: 68px;
//background-color: $themeColor;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-radius: 6px;
text-align: center;
line-height: 26px;
color: white;
font-size: 12px;
}
}
}
.close {
height: 32px;
width: 32px;
background: url('./../../assets/images/png/closeDialog2.png') no-repeat;
background-size: 100% 100%;
margin: 40px auto;
}
}
}
</style>
-90
View File
@@ -1,90 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper">
<div class="content">
<div class="title">温馨提示</div>
<div>—— 需要花费{{ audiobook.price }}解锁 ——</div>
<div class="goldCoinBox flex-center-center">
<div class="goldCoin"></div>
<span>{{ audiobook.price }}</span>
</div>
<div class="btn-box" @click.stop="emit('buying')">立即解锁</div>
</div>
<SvgIcon name="close" class="close" @click.stop="emit('close')" />
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
const props = defineProps<{
isOpen: boolean;
audiobook: Types.Novel.ContentSetItem;
}>();
const emit = defineEmits(['close', 'buying']);
const isShow = computed(() => {
return props.isOpen;
});
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.content {
height: 185px;
width: 312px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
margin: 0 auto;
text-align: center;
border-radius: 6px;
padding-top: 19px;
box-sizing: border-box;
.title {
font-size: 18px;
color: rgb(38, 38, 38);
margin-bottom: 14px;
font-weight: bold;
}
h2 {
font-size: 255px;
color: rgb(153, 153, 153);
}
.goldCoinBox {
margin-top: 13.5px;
.goldCoin {
height: 27px;
width: 27px;
background: url('./../../assets/images/png/goldCoin.png') no-repeat;
background-size: 100% 100%;
margin-right: 8.6px;
}
span {
font-size: 14px;
color: rgb(255, 160, 1);
}
}
.btn-box {
height: 38px;
width: 227px;
background-color: $themeColor;
font-size: 15px;
color: white;
line-height: 38px;
text-align: center;
border-radius: 10px;
margin: 16px auto 0;
}
}
.close {
height: 32px;
width: 32px;
margin-top: 20px;
}
}
</style>
-584
View File
@@ -1,584 +0,0 @@
<template>
<div class="post-box" @click="$router.push({ path: '/postDetail', query: { postid: props.postInfo.id } })">
<!-- header-->
<div class="post-top" v-if="!props.isHiddenAvatar">
<div class="post-top-left flex-center-center" @click.stop="toUserpage">
<div class="post-avatar">
<lazyImg
:src="props.postInfo.publisher?.portrait"
round
:key="props.postInfo.publisher?.portrait"
/>
</div>
<div class="post-info">
<div class="username_box ellipsis">
<span>{{ props.postInfo.publisher?.name }}</span>
<span v-if="props.postInfo.publisher.vipLevel > 0" class="vipLevel">
LV.{{ props.postInfo.publisher.vipLevel }}
</span>
<!-- <img v-if="props.postInfo.publisher?.superUser" src="@/assets/images/png/isCentral.png" alt="" />-->
<!-- <SvgIcon class="vipIcon icon" name="vipIcon" v-if="state.publisher?.vipLevel > 0" />-->
</div>
<!--显示时间-->
<div class="location-box flex-vcenter">
<!-- <span v-if="props.postInfo.publisher?.vipLevel != 0">
{{
props.postInfo.publisher?.vipLevel == 1
? '月卡用户'
: props.postInfo.publisher?.vipLevel == 2
? '季卡用户'
: props.postInfo.publisher?.vipLevel == 3
? '月卡用户'
: ''
}}
</span> -->
<span v-if="props.postInfo.publisher?.vipName">{{ props.postInfo.publisher?.vipName }}</span>
{{ fromNowTime(props.postInfo.reviewAt) }}
</div>
</div>
</div>
<!-- 关注 -->
<div
@click.stop="followUp"
v-if="userStore.userInfo.uid != props.postInfo.publisher?.uid && route.path != '/personal'"
>
<div class="post-top-right-follow flex-center-center" v-if="props.postInfo.publisher?.hasFollowed">已关注</div>
<div class="post-top-right flex-center-center" v-else>
<svgIcon name="follow_add"></svgIcon>
关注
</div>
</div>
<!-- 审核状态 -->
<div
class="statusClass"
v-if="userStore.userInfo.uid == props.postInfo.publisher?.uid && route.path == '/myPublish'"
>
<div @click.stop="props.postInfo?.status == 2 ? showReason() : ''">
{{ setText(props.postInfo?.status) }}
</div>
<!-- <div v-else>-->
<!-- 被拒原因:-->
<!-- <p>{{ props.postInfo?.reason }}</p>-->
<!-- </div>-->
</div>
</div>
<div class="post-time" v-else>发布时间:1小时前</div>
<!--content -->
<div class="post-text">
<TextOverflow :text="props.postInfo.title" :maxLines="3" :isTopping="props.postInfo.isTopping">
<template #default="{ clickToggle, expanded }">
<div @click.stop="clickToggle" class="text-btn">
{{ expanded ? '收起' : '全文' }}
</div>
</template>
</TextOverflow>
</div>
<!-- 图片+视频 -->
<div class="post-media">
<!-- 如果只有图片 没有视频 -->
<template v-if="props.postInfo.seriesCover && props.postInfo.seriesCover.length && !props.postInfo.sourceURL">
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover.slice(0, 3)"
:key="src + index"
class="img_item"
>
<div v-if="index == 2 && props.postInfo.seriesCover.length > 3" class="filter">
+{{ props.postInfo.seriesCover.length - 3 }}
</div>
</lazyImg>
</template>
<!-- 如果有图片 也有有视频 -->
<template v-if="props.postInfo.seriesCover && props.postInfo.seriesCover.length && props.postInfo.sourceURL">
<!-- 图片第一张为封面 -->
<!-- 如果图片第一张地址和封面图一样-->
<template v-if="props.postInfo.seriesCover[0] == props.postInfo.cover">
<!-- 如果图片大于4张 只展示图片 不用展示视频 -->
<template v-if="props.postInfo.seriesCover.length >= 4">
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover.slice(1, 4)"
:key="src + index"
class="img_item"
>
<!-- <div v-if="index == 2 && props.postInfo.seriesCover.length >= 4 && !props.postInfo.sourceURL" class="filter">
+{{ props.postInfo.seriesCover.length -1 - 3 }}
</div> -->
<div
v-if="index == 2 && props.postInfo.seriesCover.length >= 4 && props.postInfo.sourceURL"
class="filter"
>
+{{ props.postInfo.seriesCover.length - 4 + 1 }}
</div>
</lazyImg>
</template>
<!-- 否则图片+视频 -->
<template v-else>
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover.slice(1)"
:key="src + index"
class="img_item"
></lazyImg>
<lazyImg :src="props.postInfo.cover" :key="props.postInfo.cover" class="img_item">
<div class="play_box">
<img class="icon" src="@/assets/images/png/playIcon.png" alt="" />
">
</div>
</lazyImg>
</template>
</template>
<!-- 图片不一样正常展示 -->
<template v-else>
<template v-if="props.postInfo.seriesCover.length >= 3">
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover.slice(0, 3)"
:key="src + index"
class="img_item"
>
<div
v-if="index == 2 && props.postInfo.seriesCover.length > 3 && !props.postInfo.sourceURL"
class="filter"
>
+{{ props.postInfo.seriesCover.length - 3 }}
</div>
<div
v-if="index == 2 && props.postInfo.seriesCover.length > 3 && props.postInfo.sourceURL"
class="filter"
>
+{{ props.postInfo.seriesCover.length + 1 - 3 }}
</div>
</lazyImg>
</template>
<!-- 否则图片+视频 -->
<template v-else>
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover"
:key="src + index"
class="img_item"
></lazyImg>
<lazyImg :src="props.postInfo.cover" :key="props.postInfo.cover" class="img_item">
<div class="play_box">
<img class="icon" src="@/assets/images/png/playIcon.png" alt="" />
">
</div>
</lazyImg>
</template>
</template>
</template>
<!-- 只有视频 没有图片 -->
<template v-if="props.postInfo.seriesCover && !props.postInfo.seriesCover.length && props.postInfo.sourceURL">
<lazyImg :src="props.postInfo.cover" :key="props.postInfo.cover" class="video_item">
<div class="play_box">
<img class="icon" src="@/assets/images/png/playIcon.png" alt="" />
">
</div>
</lazyImg>
</template>
<!-- 如果有图片且图片大于三张 有无视频都无所谓-->
<!-- <template v-if="props.postInfo.seriesCover && props.postInfo.seriesCover.length >= 3">
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover.slice(0, 3)"
:key="src + index"
class="img_item"
>
<div v-if="index == 2 && props.postInfo.seriesCover.length > 3 && !props.postInfo.sourceURL" class="filter">
+{{ props.postInfo.seriesCover.length - 3 }}
</div>
<div v-if="index == 2 && props.postInfo.seriesCover.length > 3 && props.postInfo.sourceURL" class="filter">
+{{ props.postInfo.seriesCover.length + 1 - 3 }}
</div>
</lazyImg>
</template> -->
<!-- 有图片 并且图片小于三张 或者没有图片 且 有视频 -->
<!-- <template v-if="props.postInfo.seriesCover && props.postInfo.seriesCover.length < 3 && props.postInfo.sourceURL">
<lazyImg
:src="src"
v-for="(src, index) in props.postInfo.seriesCover"
:key="src + index"
class="img_item"
></lazyImg>
<lazyImg :src="props.postInfo.cover" :key="props.postInfo.cover" class="img_item">
<div class="play_box">
<SvgIcon name="playIcon" />
</div>
</lazyImg>
</template> -->
</div>
<!-- 操作栏 -->
<div class="post_menu">
<div class="post_menu_left">
<div class="item">
<img class="icon" src="@/assets/images/png/post_eye.png" />
<span class="ellipsis">{{ watchCountText(props.postInfo.playCount) }}</span>
</div>
<div class="item">
<img class="icon" src="@/assets/images/png/post_commit.png" />
<span class="ellipsis">{{ watchCountText(props.postInfo.commentCount) }}</span>
</div>
<div class="item" @click.stop="clickLike">
<img class="icon" v-if="props.postInfo.vidStatus?.hasLiked" src="@/assets/images/png/post_inheart.png" />
<img class="icon" v-else src="@/assets/images/png/post_heart.png" />
<span class="ellipsis">{{ watchCountText(props.postInfo.likeCount) }}</span>
</div>
</div>
<div class="post_menu_right" @click.stop="goTagPage">
{{ props.postInfo.tags && props.postInfo.tags.length ? '#' + props.postInfo.tags[0].name : '' }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, inject, onActivated } from 'vue';
import { watchCountText, watchCount, fromNowTime } from '@/utils/index';
import { useRouter, useRoute } from 'vue-router';
import { showToast } from 'vant';
import { useUserStore } from '@/stores/user';
import CommunityApi from '@/service/community';
import VideoApi from '@/service/video';
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const userInfo = userStore.userInfo;
const props = defineProps<{
postInfo: any;
currentIndex: any;
list: Array<Types.Video.VInfosItem>;
isHiddenAvatar?: boolean;
}>();
const state = reactive({
info: props.postInfo,
});
onActivated(() => {
const userStore = useUserStore();
const userInfo = userStore.userInfo;
});
// 关注
const followUp = async () => {
try {
let req = {
isFollow: !props.postInfo.publisher.hasFollowed,
followUID: props.postInfo.publisher?.uid,
};
await CommunityApi.careOrcancle(req);
props.postInfo.publisher.hasFollowed = !props.postInfo.publisher.hasFollowed;
showToast(`${props.postInfo.publisher.hasFollowed ? '关注' : '取消'}成功`);
} catch (e) {
console.log(e);
}
};
// 点赞
const clickLike = async () => {
localStorage.removeItem('operateData');
try {
let req = {
type: 'image',
objID: props.postInfo.id,
};
if (!props.postInfo.vidStatus.hasLiked) {
await CommunityApi.zan(req);
props.postInfo.vidStatus.hasLiked = true;
// getDetails(props.postInfo.id)
props.postInfo.likeCount++;
showToast('点赞成功');
} else {
await CommunityApi.cancleZan(req);
props.postInfo.vidStatus.hasLiked = false;
props.postInfo.likeCount--;
showToast('取消成功');
// getDetails(props.postInfo.id)
}
} catch (e) {
console.log(e);
}
};
const emit = defineEmits(['showReason', 'setNewData']);
// 获取帖子详情
const getDetails = async (id: any) => {
try {
let req = {
videoID: id,
};
let res: any = await VideoApi.queryVideoInfo(req);
emit('setNewData', res);
} catch (e) {
console.log(e);
}
};
const goTagPage = () => {
if (route.path == '/communityTagDeatil') {
router.replace({ path: '/communityTagDeatil', query: { id: props.postInfo.tags[0].id } });
} else {
router.push({ path: '/communityTagDeatil', query: { id: props.postInfo.tags[0].id } });
}
};
const toUserpage = () => {
router.push({ path: '/userInfo', query: { uid: props.postInfo.publisher.uid } });
localStorage.removeItem('isChange');
};
const setText = (data: any) => {
if (data == 0) {
return '待审核';
} else if (data == 1) {
return '已通过';
} else if (data == 2) {
return '查看原因';
}
};
const showReason = () => {
emit('showReason', props.postInfo?.reason);
};
</script>
<style lang="scss" scoped>
.post-box {
width: 100%;
//background: rgba(65, 55, 72, 0.3);
padding: 10px 16px;
box-sizing: border-box;
border-radius: 8px;
.post-top {
display: flex;
justify-content: space-between;
align-items: flex-end;
.post-top-left {
.post-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 8px;
:deep(.loading) {
background-size: 28px 21px;
}
}
.post-info {
width: 200px;
display: flex;
flex-direction: column;
.username_box {
display: flex;
align-items: center;
margin-bottom: 5px;
span {
display: inline-block;
font-size: 14px;
font-weight: 600;
letter-spacing: 0.36px;
margin-right: 5px;
color: #ffffff;
}
img {
width: 20px;
}
.vipLevel {
padding: 1px 4px;
//width: 30px;
//height: 14px;
background: #f1a100;
border-radius: 8px;
font-size: 10px;
color: #000;
//display: flex;
//align-items: center;
//justify-content: center;
}
}
.location-box {
padding-top: 5px;
font-size: 12px;
color: rgb(172, 186, 191);
span {
font-size: 10px;
color: rgb(102, 56, 0);
display: inline-block;
border-radius: 3px;
background: linear-gradient(to right, rgb(238, 217, 180), rgb(174, 138, 95));
padding: 2px 5px;
box-sizing: border-box;
margin: 0 5px 0 0;
}
}
}
}
.post-top-right {
svg {
width: 12px;
height: 12px;
margin: 0 3px 0 0;
}
border: solid 1px #f1a100;
border-radius: 20px;
font-size: 12px;
color: #f1a100;
width: 60px;
height: 24px;
}
.post-top-right-follow {
width: 60px;
height: 24px;
color: #fff;
background: #333;
border-radius: 20px;
font-size: 12px;
}
}
.post-time {
font-size: 12px;
color: #ffffff;
font-weight: 400;
}
.post-text {
padding-top: 6px;
padding-bottom: 6px;
display: flex;
align-items: center;
width: 100%;
}
.post-media {
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-row-gap: 8px;
grid-column-gap: 5px;
.img_item {
width: 111px;
height: 111px;
border-radius: 8px;
overflow: hidden;
position: relative;
z-index: 1;
.filter {
position: absolute;
bottom: 5px;
right: 5px;
background: rgba(0, 0, 0, 0.6);
color: #f1f1f1;
padding: 3px 12px;
box-sizing: border-box;
font-size: 12px;
border-radius: 8px;
}
.play_box {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 1;
> svg {
width: 20px;
height: 20px;
}
.icon {
width: 30px;
height: 30px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
.video_item {
width: 230px;
height: 130px;
border-radius: 8px;
overflow: hidden;
position: relative;
z-index: 1;
.play_box {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
> svg {
width: 30px;
height: 30px;
}
.icon {
width: 30px;
height: 30px;
}
}
}
}
.post_menu {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
.post_menu_left {
display: flex;
align-items: center;
justify-content: space-between;
.item {
display: flex;
align-items: center;
margin-right: 22px;
.icon {
width: 20px;
height: 20px;
margin-right: 5px;
}
.ellipsis {
font-size: 12px;
color: rgb(172, 186, 191);
}
}
}
.post_menu_right {
font-size: 14px;
color: #246bfd;
}
}
.statusClass {
box-sizing: border-box;
width: 72px;
height: 24px;
color: #fff;
background: #333;
border-radius: 12px;
font-size: 12px;
text-align: center;
line-height: 24px;
p {
margin: 6px 0 0 0;
}
}
}
</style>
-123
View File
@@ -1,123 +0,0 @@
<template>
<div class="refreshing_warp">
<van-pull-refresh
v-model="params.refreshing"
@refresh="onRefresh"
:disabled="params.disabled"
success-text="刷新成功"
:class="[{ 'my-refresh': params.isHigehtMax, none: params.refreshing }, params.className]"
>
<van-list
v-if="!params.isNoData"
v-model:loading="loadingc"
:finished="params.finished"
finished-text="没有更多数据了"
@load="onLoad"
:offset="10"
v-model:error="params.error"
:immediate-check="false"
error-text="请求失败,点击重新加载"
>
<slot></slot>
</van-list>
<Empty v-if="params.isNoData" />
<!-- <div v-if="params.isNoData" class="dataNull">暂无数据</div> -->
<div style="width: 100%; height: 240px"></div>
</van-pull-refresh>
</div>
</template>
<script lang="ts" setup>
import { computed, toRefs } from 'vue';
const props = withDefaults(
defineProps<{
params: {
className?: string; //自定义class
disabled?: boolean; //是否禁止下拉刷新 默认为false
refreshing: boolean; //下拉刷新loading
loading: boolean; //上滑刷新loading
finished: boolean; //列表数据是否记载完毕
isNoData: boolean; //数据是否为空
error?: boolean; //请求错误
/**
* 占满父级100%
* 组件内部 overflow-y:auto
* 此参数设置为true的话,父级不要设置overflow-y:auto
*/
isHigehtMax?: boolean;
};
}>(),
{},
);
const { params } = toRefs(props);
const loadingc = computed({
get() {
return params.value.loading;
},
set() {
return params.value.loading;
},
});
const emit = defineEmits(['onLoad', 'onRefresh']);
function onLoad() {
emit('onLoad');
}
function onRefresh() {
emit('onRefresh');
}
</script>
<style scoped lang="scss">
:deep(.van-loading__circular) {
display: none;
}
.none {
:deep(.van-list) {
.van-loading {
display: none;
}
}
}
:deep(.van-loading__spinner) {
background: url('../../assets/gif/loading.gif');
width: 25px !important;
height: 25px !important;
background-size: cover;
animation: none;
}
.refreshing_warp {
height: 100%;
.my-refresh {
height: 100%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 11px 16px;
}
.loading_warp {
display: flex;
align-items: center;
justify-content: center;
.loading_box {
width: 200px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
.loading {
width: 25px;
}
.loading_text {
padding-left: 5px;
}
}
}
}
</style>
-685
View File
@@ -1,685 +0,0 @@
<template>
<div class="recharge-box">
<div class="recharge-bg">
<div class="payMethod">
<span>付款方式</span>
<!-- <span>¥</span>-->
<!-- {{ state.discountedPrice }}-->
</div>
<van-radio-group v-model="state.activeItem" direction="horizontal" class="rechargeList">
<div v-for="item in state.rechargeList" :key="item.channel" @click.stop="selectPay(item)">
<div class="rechargeItem" v-if="item.type !== 'daichong'">
<SvgIcon class="payIcon" :name="item.type"></SvgIcon>
<div class="payText">{{ item.typeName }}</div>
<van-radio checked-color="rgb(255,173,0)" icon-size="20px" :name="item.type"></van-radio>
</div>
<!-- 代充 -->
<div v-if="state.dcInfo.traders && state.dcInfo.traders.length > 0 && item.type === 'daichong'">
<div
v-for="dcItem in state.dcInfo.traders[0].payInfos"
:key="dcItem.payMethod"
@click="selectDcPay(item, dcItem)"
>
<div class="rechargeItem" v-if="item.type == 'daichong'">
<SvgIcon class="payIcon" :name="getSvgPayIcon(dcItem.payMethod)"></SvgIcon>
<div class="payText">
{{ getPayName(dcItem.payMethod) }}
<span>(人工充值)</span>
</div>
<van-radio checked-color="rgb(253,191,80)" icon-size="16px" :name="'dc' + dcItem.payMethod"></van-radio>
</div>
</div>
</div>
</div>
</van-radio-group>
<div class="payBtn" @click="toPay">{{ queryBtn == 'wallet' ? '去充值' : '去开通' }}</div>
</div>
<!-- 温馨提示 -->
<van-popup class="kindTips" v-model:show="state.showKindTips">
<div class="kindTipsBox">
<div class="kindTitle">温馨提示</div>
<div class="tipContent">
本APP包含成人内容,会被误判为恶意程序,如在支付过程中遇恶意程序报毒提醒,
<span>请「点击忽略」即可。我们有稳定的广告及赞助商收入,保证安全无毒请您放心使用!</span>
</div>
<div class="kindTip"></div>
<div class="cBtn" @click="comfinPay">确认支付</div>
</div>
<SvgIcon class="close" name="close" @click="state.showKindTips = false"></SvgIcon>
</van-popup>
<!-- 订单确认弹窗 -->
<van-popup class="payTipPop" v-model:show="state.showPayTip">
<div class="payTipBox">
<div class="payTitle">订单支付确认</div>
<div class="paySubTitle">支付确认</div>
<div class="tipItem">
1.支付成功后,一般会在
<span>1~10分钟内到账</span>
,如超时未到账,请联系在线客服为您处理。
</div>
<div class="tipItem">2.受特殊行业限制,如支付失败可尝试重新发起订单,系统将会自动切换备用的支付通道。</div>
<div class="tipItem">3.本APP有隐定的广告收益,产品稳定安全,可放心充值使用。</div>
<div class="confirmBtn">
<!-- <router-link to="/customerService" class="btn">联系客服</router-link> -->
<div class="btn" @click="toPotato">官方Potato</div>
<div class="btn knowed" @click="state.showPayTip = false">我知道了</div>
</div>
</div>
<SvgIcon class="close" name="close" @click="state.showPayTip = false"></SvgIcon>
</van-popup>
<van-popup class="payLoading" overlay-class="loadingOverlay" v-model:show="state.payLoading">
<div class="loadingBox">
<div class="loadingGif"></div>
<div class="loadingText">
{{ state.payFailed ? '订单跳转失败,请重新支付' : '正在跳转订单中,请勿重复提交' }}
</div>
<div class="loadingBtn" v-if="state.payFailed" @click="comfinPay">立即支付</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue';
import { useUserStore } from '@/stores/user';
import MineApi from '@/service/mine';
import { Buffer } from 'buffer';
import { showToast } from 'vant';
import { analyticsEventRechage } from '@/utils/analyticsEvent';
import { useRoute } from 'vue-router';
import { useConfigStore } from '@/stores/config';
const route = useRoute();
const userStore = useUserStore();
const userInfo = userStore.userInfo;
const configStore = useConfigStore();
const queryBtn = route.query.name;
const emits = defineEmits(['closePay']);
const state = reactive({
rechargeList: <Array<Types.Recharge.RchgTypeItem>>[], // 支付方式列表
activeItem: '', // 支付方式选中
dcInfo: <Types.Recharge.Daichong>{}, // 代充信息
dcType: 1, // 代充商品类型 会员-1 金币-0
popType: 'vip', // 弹窗类型 会员卡-vip 金币-gold
payMethodType: 'dc', // 支付方式类型 支付渠道-channel 代充-dc 金币兑换-gold
productType: '', // 商品类型
productID: '', // 商品ID
buyType: '', // 商品类型
discountedPrice: 0, // 充值金额
payInfo: <any>{}, // 支付渠道信息
rchgType: <any>{}, // 充值类型
dcPayInfo: <any>{}, // 代充支付信息
showPayTip: false, // 支付提示弹窗状态
showKindTips: false, // 温馨提示
isAmountPay: false, // 是否支持金币兑换
graphicsId: '', // 埋点ID
graphicsTitle: '', // 埋点标题
pageNumber: 1,
loading: false,
finished: false,
radioValue: '',
curRadioValue: '', // 金币加购券
show: false,
payLoading: false,
payFailed: false,
});
const toPotato = () => {
if (configStore.sourceInfo.GROUP) {
window.open(configStore.sourceInfo.GROUP);
}
};
/**
* @description 显示支付方式弹窗
* @param rechargeList - 支付方式列表
* @param dcInfo - 代充信息
* @param popType - 弹窗类型 会员卡-vip 金币-gold
*/
const changeItem = (payData: any) => {
state.rechargeList = payData.rechargeList;
if (payData.dcInfo) {
state.dcInfo = payData.dcInfo;
}
state.popType = payData.popType;
state.dcType = payData.dcType;
state.productType = payData.productType;
state.productID = payData.productID;
state.discountedPrice = payData.discountedPrice;
state.buyType = payData.buyType;
state.isAmountPay = payData.isAmountPay;
let dcArr = state.rechargeList.filter((i: any) => {
return i.type == 'daichong';
});
let channel = state.rechargeList.filter((i: any) => {
return i.type != 'daichong';
});
if (channel.length > 0) {
selectPay(channel[0]);
} else if (dcArr.length > 0 && state.dcInfo.traders.length > 0 && state.dcInfo.traders[0].payInfos.length > 0) {
let arr = state.rechargeList.filter((i: any) => {
return i.type == 'daichong';
});
selectDcPay(arr[0], state.dcInfo.traders[0].payInfos[0]);
} else {
selectGoldPay();
}
};
// 选中支付方式
const selectPay = (item: any) => {
if (item.type != 'daichong') {
state.payInfo = item;
state.rchgType = {};
state.dcPayInfo = {};
state.payMethodType = 'channel';
state.activeItem = item.type;
}
};
// 选中代充支付方式
const selectDcPay = (rchgType: any, dcPayInfo: Types.Recharge.Daichong) => {
state.payInfo = {};
state.rchgType = rchgType;
state.dcPayInfo = dcPayInfo;
state.payMethodType = 'dc';
state.activeItem = 'dc' + state.dcPayInfo.payMethod;
};
// 选中金币兑换
const selectGoldPay = () => {
state.payInfo = { typeName: '金币' };
state.rchgType = {};
state.dcPayInfo = {};
state.payMethodType = 'gold';
state.activeItem = 'gold';
};
// 立即支付
const toPay = () => {
// if (state.payMethodType == 'gold') {
// goldExchange();
// return;
// }
state.showKindTips = true;
};
const comfinPay = () => {
state.payLoading = true;
state.payFailed = false;
if (state.payMethodType == 'channel') {
channelPay();
} else if (state.payMethodType == 'dc') {
dcToPay();
}
// 埋点
const graphicsId: any = route.query.id ? route.query.id : 'user'; // 视频传:视频id 横幅传 bannerId 用户自己拉取传 user
const graphicsTitle: any = route.query.title ? route.query.title : ''; // 标题
const graphicsType = state.popType == 'gold' ? 1 : 2; // 充值类型
try {
analyticsEventRechage(graphicsId, graphicsTitle, graphicsType);
} catch (err) {
console.log(err);
}
};
// 金币兑换
const goldExchange = async () => {
let req = {
productType: state.productType,
productID: state.productID,
isH5: true,
};
try {
userStore.updateWallet();
let walletInfo = userStore.walletInfo;
let walletBalance = ((walletInfo.amount || 0) + (walletInfo.income || 0)).toFixed(2);
if (state.discountedPrice * 10 > Number(walletBalance)) {
showToast('金币不足请充值');
return;
}
await MineApi.goldExchange(req);
showToast('购买成功');
emits('closePay');
} catch (e) {
console.log(e);
}
};
// 支付渠道支付
const channelPay = async () => {
state.show = true;
let req = {
uid: userInfo.uid,
rechargeType: state.payInfo.type,
productID: state.productID,
buyType: state.buyType,
goldExtraID: state.curRadioValue ? state.curRadioValue : undefined,
};
try {
let ret: any = await MineApi.getPay(req);
state.show = false;
state.payInfo = {};
state.showPayTip = true;
state.showKindTips = false;
state.payLoading = false;
// window.open( ret.payUrl, '_blank');
window.location = ret.payUrl;
state.curRadioValue = '';
} catch (e) {
state.show = false;
state.payFailed = true;
console.log(e);
}
};
// 代充
const dcToPay = () => {
try {
state.show = true;
let payMoney = state.discountedPrice;
let dcData = JSON.parse(JSON.stringify(state.dcInfo)); // 代充信息
let payInfoModel = JSON.parse(JSON.stringify(state.dcPayInfo)); // 代充支付类型
let channel = state.rchgType.channel;
let productInfo = {
id: state.productID,
type: state.dcType,
version: '3.0.0',
};
//设置默认值
if (dcData.limit == 0) {
dcData.limit = 500;
}
if (payInfoModel.payType.includes(2) && payInfoModel.payType.includes(3)) {
if (payMoney > dcData.limit) {
payInfoModel.payType = [3];
} else {
payInfoModel.payType = [2];
}
}
if (payInfoModel.payType.length >= 3) {
if (payMoney > dcData.limit) {
payInfoModel.payType = [1, 3];
} else {
payInfoModel.payType = [1, 2];
}
}
dcData.traders[0].payInfos = [payInfoModel];
dcData.productInfo = productInfo;
dcData.chargeMoney = payMoney;
dcData.channel = channel;
let models = JSON.stringify(dcData);
let payUrl = `${channel}/?data=${encodeBase64(models)}&token=${userStore.token}`;
state.showPayTip = true;
state.showKindTips = false;
state.show = false;
state.payLoading = false;
window.location.href = payUrl;
} catch (error) {
state.payFailed = true;
}
};
// 转base64
const encodeBase64 = (str: any) => {
let base64Str = Buffer.from(str, 'utf-8').toString('base64');
return base64Str;
};
// 获取支付方式名称
const getPayName = (type: any) => {
switch (type) {
case 'alipay':
return '支付宝';
case 'wechat':
return '微信';
case 'union':
return '银联';
case 101:
return '支付宝';
case 102:
return '微信';
case 103:
return '银行卡';
case 104:
return '信用卡';
case 105:
return '花呗';
case 106:
return '云闪付';
case 107:
return 'QQ钱包';
case 108:
return '京东支付';
default:
return '金币兑换';
}
};
// 获取支付方式图标
const getSvgPayIcon = (type: any) => {
switch (type) {
case 'alipay':
return 'alipay';
case 'wechat':
return 'wechat';
case 'union':
return 'bankcard';
case 101:
return 'alipay';
case 102:
return 'wechat';
case 103:
return 'bankcard';
case 104:
return 'bankcard';
case 105:
return 'alipay';
case 106:
return 'bankcard';
case 107:
return 'bankcard';
case 108:
return 'bankcard';
default:
return 'gold';
}
};
defineExpose({
changeItem,
});
</script>
<style lang="scss" scoped>
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.4);
}
:deep(.van-popup) {
background-color: transparent;
}
.recharge-box {
width: 100%;
height: 100%;
.recharge-bg {
color: rgb(255, 255, 255);
// background-color: rgb(0, 0, 0);
.recharge-title {
height: 60px;
line-height: 60px;
font-size: 16px;
text-align: center;
position: relative;
color: $titleColor;
> svg {
height: 24px;
width: 24px;
position: absolute;
top: 50%;
right: 14px;
transform: translate(0, -50%);
}
}
.payMethod {
font-size: 31px;
text-align: left;
color: $titleColor;
padding-bottom: 15px;
span {
font-size: 18px;
font-weight: 600;
}
}
.rechargeList {
height: 100%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 8px 0px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: nowrap;
}
.rechargeItem {
display: flex;
align-items: center;
justify-content: space-between;
margin: 8px 0;
.payIcon {
width: 44px;
height: 44px;
background: $titleColor;
border-radius: 100px;
}
.payGoldIcon {
background: url('./../../assets/images/png/goldIcon.png') no-repeat;
background-size: 100% 100%;
}
.payText {
flex: 2;
color: $titleColor;
font-size: 18px;
font-weight: 600;
margin-left: 12px;
}
:deep(.van-radio) {
.van-radio__icon--checked .van-icon {
color: #000 !important;
}
}
}
.couponBox {
height: 90px;
.couponTitle {
font-size: 16px;
font-weight: 500;
color: $titleColor;
margin-bottom: 6px;
}
// .couponList {
// height: 60px;
// overflow-y: auto;
// }
.coupon-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
.coupon-title {
font-size: 14px;
color: $greyDeepColor;
}
.coupon-amount {
flex: 2;
text-align: right;
margin: 0 10px;
font-size: 14px;
color: $titleColor;
}
}
}
.subTitle {
font-size: 14px;
color: rgb(255, 160, 1);
margin: 10px 0;
}
.payTip {
font-size: 14px;
line-height: 20px;
letter-spacing: -0.34px;
color: rgb(128, 128, 128);
}
.customerService {
font-size: 12px;
text-align: center;
margin: 10px 0 6px;
color: rgb(128, 128, 128);
.contactCustomer {
color: $themeColor;
}
}
.payBtn {
margin-top: 20px;
text-align: center;
line-height: 48px;
width: 343px;
height: 48px;
font-weight: 600;
border-radius: 100px;
color: #2c2c2c;
background: #f1a100;
}
.sizeBox {
height: 30px;
}
}
}
.kindTips {
border-radius: 6px;
.kindTipsBox {
width: 308px;
height: 337px;
border-radius: 6px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
padding-top: 16px;
.kindTitle {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
margin-bottom: 18px;
font-weight: bold;
}
.tipContent {
font-size: 12px;
letter-spacing: 0.24px;
line-height: 22px;
margin: 0 20px;
color: #000;
span {
color: rgb(255, 160, 1);
}
}
.kindTip {
height: 86px;
width: 251px;
border-radius: 10px;
background: url('./../../assets/images/png/kindTip.png') no-repeat;
background-size: 100% 100%;
margin: 10px auto 29px;
}
.cBtn {
width: 227px;
height: 38px;
font-size: 14px;
border-radius: 10px;
margin: 0px auto;
color: white;
background: $themeColor;
text-align: center;
line-height: 38px;
}
}
.close {
height: 32px;
width: 32px;
margin: 26px 0 0;
position: relative;
left: 50%;
bottom: 0;
transform: translate(-50%, 0%);
}
}
.payTipPop {
border-radius: 6px;
.payTipBox {
height: 330px;
width: 308px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
padding: 16px 33.5px 0;
border-radius: 6px;
.payTitle {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
font-weight: bold;
}
.paySubTitle {
font-size: 12px;
color: rgb(38, 38, 38);
margin: 23px 0 7px;
}
.tipItem {
font-size: 12px;
margin-bottom: 16px;
color: #000;
line-height: 20px;
span {
color: rgb(255, 125, 77);
}
}
.confirmBtn {
display: flex;
justify-content: center;
margin-top: 27px;
.btn {
height: 38px;
width: 113px;
border-radius: 10px;
line-height: 38px;
text-align: center;
font-size: 14px;
color: rgb(153, 153, 153);
border: 1.5px solid rgb(153, 153, 153);
}
.knowed {
border: none;
background-color: $themeColor;
color: white;
margin-left: 14px;
}
}
}
.close {
height: 32px;
width: 32px;
margin: 26px 0 0;
position: relative;
left: 50%;
bottom: 0;
transform: translate(-50%, 0%);
}
}
.payLoading {
.loadingBox {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.loadingGif {
width: 100px;
height: 100px;
background: url('../../assets/gif/payLoading.gif');
background-size: 100% 100%;
}
.loadingText {
color: #f8c700;
font-family: 'PingFang HK';
font-size: 14px;
margin: 21px 0 29px;
text-align: center;
}
.loadingBtn {
width: 227px;
height: 38px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
background: #ffa001;
color: #fff;
font-family: 'PingFang HK';
font-size: 16px;
}
}
}
:deep(.loadingOverlay) {
backdrop-filter: blur(3.75px);
}
</style>
-609
View File
@@ -1,609 +0,0 @@
<template>
<div class="recharge-box">
<div class="recharge-bg">
<div class="payMethod">
<span>¥</span>
{{ state.discountedPrice }}
</div>
<van-radio-group v-model="state.activeItem" direction="horizontal" class="rechargeList">
<div v-for="item in state.rechargeList" :key="item.channel" @click.stop="selectPay(item)">
<div class="rechargeItem" v-if="item.type !== 'daichong'">
<SvgIcon class="payIcon" :name="item.type"></SvgIcon>
<div class="payText">{{ item.typeName }}</div>
<van-radio checked-color="rgb(253,191,80)" icon-size="16px" :name="item.type"></van-radio>
</div>
<!-- 代充 -->
<div v-if="state.dcInfo.traders && state.dcInfo.traders.length > 0 && item.type === 'daichong'">
<div
v-for="dcItem in state.dcInfo.traders[0].payInfos"
:key="dcItem.payMethod"
@click="selectDcPay(item, dcItem)"
>
<div class="rechargeItem" v-if="item.type == 'daichong'">
<SvgIcon class="payIcon" :name="getSvgPayIcon(dcItem.payMethod)"></SvgIcon>
<div class="payText">
{{ getPayName(dcItem.payMethod) }}
<span>(人工充值)</span>
</div>
<van-radio checked-color="rgb(253,191,80)" icon-size="16px" :name="'dc' + dcItem.payMethod"></van-radio>
</div>
</div>
</div>
</div>
</van-radio-group>
<div class="payBtn" @click="toPay">立即支付</div>
<div class="sizeBox"></div>
</div>
<!-- 温馨提示 -->
<van-popup class="kindTips" v-model:show="state.showKindTips">
<div class="kindTipsBox">
<div class="kindTitle">温馨提示</div>
<div class="tipContent">
本APP包含成人内容,会被误判为恶意程序,如在支付过程中遇恶意程序报毒提醒,
<span>请「点击忽略」即可。我们有稳定的广告及赞助商收入,保证安全无毒请您放心使用!</span>
</div>
<div class="kindTip"></div>
<div class="cBtn" @click="comfinPay">确认支付</div>
</div>
<SvgIcon class="close" name="close" @click="state.showKindTips = false"></SvgIcon>
</van-popup>
<!-- 订单确认弹窗 -->
<van-popup class="payTipPop" v-model:show="state.showPayTip">
<div class="payTipBox">
<div class="payTitle">订单支付确认</div>
<div class="paySubTitle">支付确认</div>
<div class="tipItem">
1.支付成功后,一般会在
<span>1~10分钟内到账</span>
,如超时未到账,请联系在线客服为您处理。
</div>
<div class="tipItem">2.受特殊行业限制,如支付失败可尝试重新发起订单,系统将会自动切换备用的支付通道。</div>
<div class="tipItem">3.本APP有隐定的广告收益,产品稳定安全,可放心充值使用。</div>
<div class="confirmBtn">
<!-- <router-link to="/customerService" class="btn">联系客服</router-link> -->
<div class="btn" @click="toPotato">官方Potato</div>
<div class="btn knowed" @click="state.showPayTip = false">我知道了</div>
</div>
</div>
<SvgIcon class="close" name="close" @click="state.showPayTip = false"></SvgIcon>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue';
import { useUserStore } from '@/stores/user';
import MineApi from '@/service/mine';
import { Buffer } from 'buffer';
import { showToast } from 'vant';
import { analyticsEventRechage } from '@/utils/analyticsEvent';
import { useRoute } from 'vue-router';
import { useConfigStore } from '@/stores/config';
const route = useRoute();
const userStore = useUserStore();
const userInfo = userStore.userInfo;
const configStore = useConfigStore();
const emits = defineEmits(['closePay']);
const state = reactive({
rechargeList: <Array<Types.Recharge.RchgTypeItem>>[], // 支付方式列表
activeItem: '', // 支付方式选中
dcInfo: <Types.Recharge.Daichong>{}, // 代充信息
dcType: 1, // 代充商品类型 会员-1 金币-0
popType: 'vip', // 弹窗类型 会员卡-vip 金币-gold
payMethodType: 'dc', // 支付方式类型 支付渠道-channel 代充-dc 金币兑换-gold
productType: '', // 商品类型
productID: '', // 商品ID
buyType: '', // 商品类型
discountedPrice: 0, // 充值金额
payInfo: <any>{}, // 支付渠道信息
rchgType: <any>{}, // 充值类型
dcPayInfo: <any>{}, // 代充支付信息
showPayTip: false, // 支付提示弹窗状态
showKindTips: false, // 温馨提示
isAmountPay: false, // 是否支持金币兑换
graphicsId: '', // 埋点ID
graphicsTitle: '', // 埋点标题
pageNumber: 1,
loading: false,
finished: false,
radioValue: '',
curRadioValue: '', // 金币加购券
show: false,
});
const toPotato = () => {
if (configStore.sourceInfo.GROUP) {
window.open(configStore.sourceInfo.GROUP);
}
};
/**
* @description 显示支付方式弹窗
* @param rechargeList - 支付方式列表
* @param dcInfo - 代充信息
* @param popType - 弹窗类型 会员卡-vip 金币-gold
*/
const changeItem = (payData: any) => {
state.rechargeList = payData.rechargeList;
if (payData.dcInfo) {
state.dcInfo = payData.dcInfo;
}
state.popType = payData.popType;
state.dcType = payData.dcType;
state.productType = payData.productType;
state.productID = payData.productID;
state.discountedPrice = payData.discountedPrice;
state.buyType = payData.buyType;
state.isAmountPay = payData.isAmountPay;
let dcArr = state.rechargeList.filter((i: any) => {
return i.type == 'daichong';
});
let channel = state.rechargeList.filter((i: any) => {
return i.type != 'daichong';
});
if (channel.length > 0) {
selectPay(channel[0]);
} else if (dcArr.length > 0 && state.dcInfo.traders.length > 0 && state.dcInfo.traders[0].payInfos.length > 0) {
let arr = state.rechargeList.filter((i: any) => {
return i.type == 'daichong';
});
selectDcPay(arr[0], state.dcInfo.traders[0].payInfos[0]);
} else {
selectGoldPay();
}
};
// 选中支付方式
const selectPay = (item: any) => {
if (item.type != 'daichong') {
state.payInfo = item;
state.rchgType = {};
state.dcPayInfo = {};
state.payMethodType = 'channel';
state.activeItem = item.type;
}
};
// 选中代充支付方式
const selectDcPay = (rchgType: any, dcPayInfo: Types.Recharge.Daichong) => {
state.payInfo = {};
state.rchgType = rchgType;
state.dcPayInfo = dcPayInfo;
state.payMethodType = 'dc';
state.activeItem = 'dc' + state.dcPayInfo.payMethod;
};
// 选中金币兑换
const selectGoldPay = () => {
state.payInfo = { typeName: '金币' };
state.rchgType = {};
state.dcPayInfo = {};
state.payMethodType = 'gold';
state.activeItem = 'gold';
};
// 立即支付
const toPay = () => {
if (state.payMethodType == 'gold') {
goldExchange();
return;
}
state.showKindTips = true;
};
const comfinPay = () => {
if (state.payMethodType == 'channel') {
channelPay();
} else if (state.payMethodType == 'dc') {
dcToPay();
}
// 埋点
const graphicsId: any = route.query.id ? route.query.id : 'user'; // 视频传:视频id 横幅传 bannerId 用户自己拉取传 user
const graphicsTitle: any = route.query.title ? route.query.title : ''; // 标题
const graphicsType = state.popType == 'gold' ? 1 : 2; // 充值类型
try {
analyticsEventRechage(graphicsId, graphicsTitle, graphicsType);
} catch (err) {
console.log(err);
}
};
// 金币兑换
const goldExchange = async () => {
let req = {
productType: state.productType,
productID: state.productID,
isH5: true,
};
try {
userStore.updateWallet();
let walletInfo = userStore.walletInfo;
let walletBalance = ((walletInfo.amount || 0) + (walletInfo.income || 0)).toFixed(2);
if (state.discountedPrice * 10 > Number(walletBalance)) {
showToast('金币不足请充值');
return;
}
await MineApi.goldExchange(req);
showToast('购买成功');
emits('closePay');
} catch (e) {
console.log(e);
}
};
// 支付渠道支付
const channelPay = async () => {
state.show = true;
let req = {
uid: userInfo.uid,
rechargeType: state.payInfo.type,
productID: state.productID,
buyType: state.buyType,
goldExtraID: state.curRadioValue ? state.curRadioValue : undefined,
};
try {
let ret: any = await MineApi.getPay(req);
state.show = false;
state.payInfo = {};
state.showPayTip = true;
state.showKindTips = false;
// window.open( ret.payUrl, '_blank');
window.location = ret.payUrl;
state.curRadioValue = '';
} catch (e) {
state.show = false;
console.log(e);
}
};
// 代充
const dcToPay = () => {
state.show = true;
let payMoney = state.discountedPrice;
let dcData = JSON.parse(JSON.stringify(state.dcInfo)); // 代充信息
let payInfoModel = JSON.parse(JSON.stringify(state.dcPayInfo)); // 代充支付类型
let channel = state.rchgType.channel;
let productInfo = {
id: state.productID,
type: state.dcType,
version: '3.0.0',
};
//设置默认值
if (dcData.limit == 0) {
dcData.limit = 500;
}
if (payInfoModel.payType.includes(2) && payInfoModel.payType.includes(3)) {
if (payMoney > dcData.limit) {
payInfoModel.payType = [3];
} else {
payInfoModel.payType = [2];
}
}
if (payInfoModel.payType.length >= 3) {
if (payMoney > dcData.limit) {
payInfoModel.payType = [1, 3];
} else {
payInfoModel.payType = [1, 2];
}
}
dcData.traders[0].payInfos = [payInfoModel];
dcData.productInfo = productInfo;
dcData.chargeMoney = payMoney;
dcData.channel = channel;
let models = JSON.stringify(dcData);
let payUrl = `${channel}/?data=${encodeBase64(models)}&token=${userStore.token}`;
state.showPayTip = true;
state.showKindTips = false;
state.show = false;
window.location.href = payUrl;
};
// 转base64
const encodeBase64 = (str: any) => {
let base64Str = Buffer.from(str, 'utf-8').toString('base64');
return base64Str;
};
// 获取支付方式名称
const getPayName = (type: any) => {
switch (type) {
case 'alipay':
return '支付宝';
case 'wechat':
return '微信';
case 'union':
return '银联';
case 101:
return '支付宝';
case 102:
return '微信';
case 103:
return '银行卡';
case 104:
return '信用卡';
case 105:
return '花呗';
case 106:
return '云闪付';
case 107:
return 'QQ钱包';
case 108:
return '京东支付';
default:
return '金币兑换';
}
};
// 获取支付方式图标
const getSvgPayIcon = (type: any) => {
switch (type) {
case 'alipay':
return 'alipay';
case 'wechat':
return 'wechat';
case 'union':
return 'bankcard';
case 101:
return 'alipay';
case 102:
return 'wechat';
case 103:
return 'bankcard';
case 104:
return 'bankcard';
case 105:
return 'alipay';
case 106:
return 'bankcard';
case 107:
return 'bankcard';
case 108:
return 'bankcard';
default:
return 'gold';
}
};
defineExpose({
changeItem,
});
</script>
<style lang="scss" scoped>
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.4);
}
:deep(.van-popup) {
background-color: transparent;
}
.recharge-box {
.recharge-popup {
.recharge-bg {
background-color: rgb(255, 255, 255);
// background-color: rgb(0, 0, 0);
border-radius: 12px 12px 0 0;
padding: 0 14px;
.recharge-title {
height: 60px;
line-height: 60px;
font-size: 16px;
text-align: center;
position: relative;
color: $titleColor;
> svg {
height: 24px;
width: 24px;
position: absolute;
top: 50%;
right: 14px;
transform: translate(0, -50%);
}
}
.payMethod {
font-size: 31px;
text-align: center;
color: rgb(38, 38, 38);
span {
font-size: 16px;
}
}
.rechargeList {
max-height: 310px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 8px 0px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: nowrap;
}
.rechargeItem {
display: flex;
align-items: center;
justify-content: space-between;
margin: 8px 0;
.payIcon {
width: 30px;
height: 30px;
}
.payGoldIcon {
background: url('./../../assets/images/png/goldIcon.png') no-repeat;
background-size: 100% 100%;
}
.payText {
flex: 2;
color: rgb(38, 38, 38);
font-size: 14px;
margin-left: 5px;
}
}
.couponBox {
height: 90px;
.couponTitle {
font-size: 16px;
font-weight: 500;
color: $titleColor;
margin-bottom: 6px;
}
// .couponList {
// height: 60px;
// overflow-y: auto;
// }
.coupon-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
.coupon-title {
font-size: 14px;
color: $greyDeepColor;
}
.coupon-amount {
flex: 2;
text-align: right;
margin: 0 10px;
font-size: 14px;
color: $titleColor;
}
}
}
.subTitle {
font-size: 14px;
color: rgb(255, 160, 1);
margin: 10px 0;
}
.payTip {
font-size: 14px;
line-height: 20px;
letter-spacing: -0.34px;
color: rgb(128, 128, 128);
}
.customerService {
font-size: 12px;
text-align: center;
margin: 10px 0 6px;
color: rgb(128, 128, 128);
.contactCustomer {
color: $themeColor;
}
}
.payBtn {
height: 42px;
line-height: 42px;
border-radius: 42px;
font-size: 16px;
text-align: center;
color: #fff;
background: rgb(255, 160, 1);
margin: 15px 17px 0px 17px;
}
.sizeBox {
height: 30px;
}
}
}
.kindTips {
border-radius: 6px;
.kindTipsBox {
width: 308px;
height: 337px;
border-radius: 6px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
padding-top: 16px;
.kindTitle {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
margin-bottom: 18px;
font-weight: bold;
}
.tipContent {
font-size: 12px;
letter-spacing: 0.24px;
line-height: 22px;
margin: 0 20px;
color: rgb(38, 38, 38);
span {
color: rgb(255, 160, 1);
}
}
.kindTip {
height: 86px;
width: 251px;
border-radius: 10px;
background: url('./../../assets/images/png/kindTip.png') no-repeat;
background-size: 100% 100%;
margin: 10px auto 29px;
}
.cBtn {
width: 227px;
height: 38px;
font-size: 14px;
border-radius: 10px;
margin: 0px auto;
color: white;
background: $themeColor;
text-align: center;
line-height: 38px;
}
}
}
.payTipPop {
border-radius: 6px;
.payTipBox {
height: 330px;
width: 308px;
background: url('./../../assets/images/png/popupBg.png') no-repeat;
background-size: 100% 123px;
background-color: rgb(255, 255, 255);
padding: 16px 33.5px 0;
border-radius: 6px;
.payTitle {
font-size: 18px;
color: rgb(38, 38, 38);
text-align: center;
font-weight: bold;
}
.paySubTitle {
font-size: 12px;
color: rgb(38, 38, 38);
margin: 23px 0 7px;
}
.tipItem {
font-size: 12px;
margin-bottom: 16px;
color: rgb(38, 38, 38);
line-height: 20px;
span {
color: rgb(255, 125, 77);
}
}
.confirmBtn {
display: flex;
justify-content: center;
margin-top: 27px;
.btn {
height: 38px;
width: 113px;
border-radius: 10px;
line-height: 38px;
text-align: center;
font-size: 14px;
color: rgb(153, 153, 153);
border: 1.5px solid rgb(153, 153, 153);
}
.knowed {
border: none;
background-color: $themeColor;
color: white;
margin-left: 14px;
}
}
}
}
.close {
height: 32px;
width: 32px;
margin: 26px 0 0;
position: relative;
left: 50%;
transform: translate(-50%, 0);
}
}
</style>
-150
View File
@@ -1,150 +0,0 @@
<template>
<div class="reward-popup">
<van-popup v-model:show="state.isRewardShow">
<div class="popup-box">
<div class="popup-title">
打赏
<div class="close flex-center-center" @click="close">
<SvgIcon name="payClose" height="24px" width="24px" />
</div>
</div>
<div class="content">
<div class="subtitle">打赏金额</div>
<van-field v-model="state.coins" type="number" placeholder="请输入价格">
<template #left-icon>
<span>¥</span>
</template>
</van-field>
<div class="gold-coin">
钱包余额 :
<!-- <span>{{ userInfo.activeValue }}</span>-->
<span>{{ (state.userWallet.amount * 100 + state.userWallet.income * 100) / 100 }}</span>
</div>
</div>
<div class="confirm" @click="confirm">打赏</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue';
import { useUserStore } from '@/stores/user';
import CommunityApi from '@/service/community';
import { showToast } from 'vant';
import { stat } from 'fs';
const userStore = useUserStore();
const walletInfo = userStore.walletInfo;
const userInfo = userStore.userInfo;
nextTick(() => {
/**
* 获取金币钱包信息
*/
state.userWallet = userStore.walletInfo;
});
const state = reactive({
userWallet: <Types.User.WalletInfo>{},
isRewardShow: false,
vid: '',
coins: 0,
});
const close = () => {
state.vid = '';
state.coins = 0;
state.isRewardShow = false;
};
const confirm = async () => {
if (!Number(state.coins)) return showToast('请输入金额');
if (Number(state.coins) * 10 > walletInfo.amount && walletInfo.amount == 0) return showToast('余额不足');
try {
let req = {
vid: state.vid,
coins: state.coins,
};
await CommunityApi.reward(req);
showToast('打赏成功');
close();
} catch (e) {
console.log(e);
}
};
const openPop = (vid: string) => {
state.vid = vid;
state.isRewardShow = true;
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.reward-popup {
.popup-box {
height: 237px;
width: 261px;
background-color: white;
border-radius: 8px;
padding: 0 5.5px;
box-sizing: border-box;
.popup-title {
height: 48px;
font-size: 18px;
font-weight: 500;
color: $titleColor;
letter-spacing: 0.36px;
line-height: 48px;
text-align: center;
border-bottom: 1px solid rgb(242, 242, 242);
position: relative;
.close {
position: absolute;
top: 50%;
right: 10px;
transform: translate(0, -50%);
}
}
.content {
padding: 18px 38px 0;
.subtitle {
font-size: 14px;
font-weight: 500;
color: $titleColor;
letter-spacing: 0.28px;
}
:deep(.van-cell) {
border: 1px solid rgb(226, 226, 226);
border-radius: 4px;
margin: 9px 0 17px;
.van-field__left-icon {
span {
font-size: 14px;
font-weight: 600;
}
}
}
.gold-coin {
font-size: 12px;
letter-spacing: 0.24px;
color: $greyDeepColor;
span {
color: $themeColor;
font-weight: 500;
}
}
}
.confirm {
height: 35px;
width: 217px;
background-color: $themeColor;
border-radius: 4px;
color: $greyLightColor;
font-size: 14px;
text-align: center;
line-height: 35px;
margin: 15px auto;
}
}
}
</style>
@/service/entertainment
-76
View File
@@ -1,76 +0,0 @@
<template>
<div class="vertical-scoll">
<vertical-scroll :list="list">
<template v-slot="{ data }">
<span style="width: 100%; display: block; line-height: 30px">
<div>{{ data.name }}</div>
</span>
</template>
</vertical-scroll>
</div>
<div class="horizonta-scoll">
<horizontal-scroll :list="list" direction="left">
<template v-slot="{ data }">
<div class="vertical-text">
{{ data.name }}
</div>
</template>
</horizontal-scroll>
</div>
<div class="vertical-scoll">
<vue3-seamless-scroll :list="list">
<template v-slot="{ data }">
<span style="width: 100%; display: block; line-height: 30px">
<div>{{ data.name }}</div>
</span>
</template>
</vue3-seamless-scroll>
</div>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { Vue3SeamlessScroll, VerticalScroll, HorizontalScroll } from 'vue3-seamless-scroll';
const listData = Array.from({ length: 10000 }, (_, i) => ({
id: Date.now() + i + 1,
name: `Vue3.0无缝滚动展示数据第${i + 1}条`,
}));
export default defineComponent({
name: 'App',
components: {
Vue3SeamlessScroll,
VerticalScroll,
HorizontalScroll,
},
setup() {
const list = ref(listData);
return {
list,
};
},
});
</script>
<style>
.vertical-scoll {
overflow: hidden;
height: 300px;
color: #000;
}
.horizonta-scoll {
overflow: hidden;
height: 300px;
color: #000;
}
.vertical-text {
height: 300px;
writing-mode: vertical-lr;
text-orientation: upright;
line-height: 30px;
display: inline-block;
color: #000;
}
</style>
-107
View File
@@ -1,107 +0,0 @@
<template>
<div class="selectionLinePopup">
<!-- 选择路线弹窗 -->
<van-popup class="selectionPopup" v-model:show="isOpenSelectionLine" position="bottom" @close="emits('close')">
<div class="selectionLineBox">
<div class="titleBox">
选择播放线路
<SvgIcon name="closeBlack" class="close" @click="emits('close')"></SvgIcon>
</div>
<div class="lineBox">
<div
class="line"
:class="{ selectedLine: item.desc == globalStore.videoRoadLine.desc }"
@click="selectLine(item)"
v-for="item in state.roadLines"
:key="item.desc"
>
{{ item.desc }}
</div>
</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue';
import { useConfigStore } from '@/stores/config';
import { useGlobalStore } from '@/stores/global';
import { showToast } from 'vant';
const configStore = useConfigStore();
const globalStore = useGlobalStore();
const props = defineProps<{
isOpen: boolean;
}>();
const emits = defineEmits(['close', 'closeSelectLine']);
const isOpenSelectionLine = computed(() => {
return props.isOpen;
});
const state = reactive({
roadLines: <any>configStore.sourceInfo.VID,
});
const selectLine = (item: any) => {
globalStore.setVideoRoadLine(item);
showToast('切换成功');
emits('closeSelectLine');
};
</script>
<style lang="scss" scoped>
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
:deep(.van-popup) {
background-color: transparent;
}
.selectionLinePopup {
.selectionPopup {
.selectionLineBox {
background: #fff url('./../../assets/images/png/selectLineBg.png') no-repeat;
background-size: 150%;
background-position-y: -20px;
padding: 10px 14px;
height: 300px;
border-radius: 18px 18px 0 0;
.titleBox {
color: black;
font-size: 18px;
font-weight: 500;
height: 50px;
text-align: center;
line-height: 50px;
border-bottom: 1px solid #f1f3f5;
position: relative;
.close {
height: 15px;
width: 15px;
position: absolute;
right: 10px;
top: 10px;
}
}
.lineBox {
padding: 15px 0px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 20px;
grid-row-gap: 14px;
.line {
height: 38px;
width: 100px;
text-align: center;
line-height: 38px;
border-radius: 5px;
font-size: 14px;
color: #424242;
background: #f1f3f5;
}
.selectedLine {
color: white;
background: rgb(255, 166, 65);
}
}
}
}
}
</style>
-132
View File
@@ -1,132 +0,0 @@
<template>
<div class="sharePopup">
<van-popup v-model:show="state.isRewardShow" :style="{ backgroundColor: 'rgba(0, 0, 0, 0)' }">
<div class="popupBox">
<LazyImg class="cover" :src="state.videoInfo?.cover" />
<div class="bottom"></div>
<SvgIcon name="title2" class="title" />
<div class="qrcode">
<qrcode-vue :value="state.qrCodeUrl" :size="90" level="L" />
</div>
<div class="desc">{{ state.videoInfo?.title }}</div>
</div>
<div class="btnBox flex-center-between">
<div class="btn" @click="showToast('请手动截图保存')">保存图片</div>
<div class="btn copyBtn" @click="copyLink">复制链接</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { showToast } from 'vant';
import QrcodeVue from 'qrcode.vue';
import { useUserStore } from '@/stores/user';
import useClipboard from 'vue-clipboard3';
const { toClipboard } = useClipboard();
const userStore = useUserStore();
const userInfo: any = userStore.userInfo;
const props = defineProps<{
videoInfo?: Types.Video.VInfosItem;
}>();
const state = reactive({
isRewardShow: false,
qrCodeUrl: userInfo.promoteURL,
promotionCode: userInfo.promotionCode,
videoInfo: props.videoInfo,
});
const openPop = () => {
state.isRewardShow = true;
};
const copyLink = async () => {
try {
if (!state.qrCodeUrl) {
showToast('复制失败');
return;
}
await toClipboard(state.qrCodeUrl); //实现复制
showToast('复制成功');
} catch (e: any) {
console.log(e);
}
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
.sharePopup {
background-color: rgba(255,255,255);
.popupBox {
background: rgba(255,255,255);
border-radius: 6px;
height: 271px;
width: 301px;
overflow: hidden;
position: relative;
.cover {
height: 191px;
width: 100%;
}
.bottom {
height: 109px;
width: 100%;
}
.title {
height: 35px;
width: 124.6px;
position: absolute;
left: 15px;
top: 15px;
}
.qrcode {
height: 90px;
width: 90px;
position: absolute;
left: 19px;
top: 147px;
border-radius: 6px;
// background: rgba(238,118,47);
// padding:0 5px 3px 0;
}
.desc {
position: absolute;
right: 17.3px;
bottom: 29px;
width: 157px;
height: 37px;
color: rgba(38, 38, 38);
font-size: 13px;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
.btnBox {
margin: 23px auto;
.btn {
height: 48px;
width: 144px;
border-radius: 24px;
text-align: center;
line-height: 48px;
background-color: white;
}
.copyBtn {
color: white;
background-color: $themeColor;
}
}
:deep(.van-popup) {
border-radius: 8px;
}
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
}
</style>
-146
View File
@@ -1,146 +0,0 @@
<template>
<div class="vertical_video_warp">
<div id="video">
<div class="avoid_sliding" @click="pause"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted } from 'vue';
import { useConfigStore } from '@/stores/config';
import { useUserStore } from '@/stores/user';
import HlsJSPlayer from 'xgplayer-hls.js';
const props = defineProps<{
sourceURL: string;
}>();
let player: any = null; //播放器实例
onMounted(() => {
//初始化播放器
initVideo();
});
/**
* @description 暂停
*/
const pause = () => {
player.pause();
};
/**
* @description 初始化播放器
*/
const initVideo = () => {
player = new HlsJSPlayer({
id: 'video',
url: getPath(),
fluid: true,
playsinline: true,
videoInit: true,
loop: true,
autoplay: true,
controls: true,
lang: 'zh-cn',
ignores: ['i18n', 'play', 'volume', 'definition'],
allowPlayAfterEnded: true,
allowSeekAfterEnded: true,
closeVideoPreventDefault: true,
closeVideoStopPropagation: true,
closeInactive: true,
enableVideoDbltouch: true,
});
};
/**
* @description 获取播放链接
*/
const getPath = () => {
const { baseUrl } = useConfigStore();
return `${baseUrl}/api/app/vid/h5/m3u8/${props.sourceURL}?token=${getToken()}`;
};
/**
* @description 获取token
*/
const getToken = () => {
const { token } = useUserStore();
return token;
};
onBeforeUnmount(() => {
if (player) {
player.src = '';
player.destroy(true);
}
player = null;
});
</script>
<style lang="scss" scoped>
.vertical_video_warp {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
background: #000000;
align-items: center;
:deep(.xgplayer-progress-played) {
background: rgb(39, 213, 236);
}
:deep(.xgplayer-progress) {
top: 10px;
}
:deep(.xgplayer-start),
:deep(.xgplayer-controls) {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
:deep(.xgplayer-progress-btn) {
display: none !important;
}
:deep(.xgplayer-time-current:after) {
content: ' ';
}
:deep(.xgplayer-progress) {
left: 43%;
right: 0;
width: 60%;
transform: translate(-50%, 0);
}
:deep(.xgplayer-time) {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
// margin-top: 10px;
font-size: 11px;
}
:deep(.xgplayer-time-current)::after {
display: none;
}
// :deep(.xgplayer-icon-play) {
// > svg {
// height: 40px;
// width: 40px;
// }
// }
// :deep(.xgplayer-skin-default .xgplayer-start) {
// height: 40px;
// width: 40px;
// margin: -20px;
// }
#video {
width: 100%;
height: 100% !important;
position: absolute;
top: 0;
left: 0;
position: relative;
:deep(.xgplayer-enter-spinner) {
width: 50px;
height: 50px;
}
.avoid_sliding {
position: absolute;
width: 100%;
height: 100%;
background: transparent;
top: 0;
}
}
}
</style>
-146
View File
@@ -1,146 +0,0 @@
<template>
<div class="tab_wrapper">
<van-tabbar
v-model="activeName"
:safe-area-inset-bottom="true"
:border="false"
active-color="rgb(255, 173, 0)"
inactive-color="rgb(220, 220, 220)"
>
<van-tabbar-item to="/frontPage" name="frontPage" replace>
<span>导航</span>
<template #icon="props">
<!-- <div :class="props.active ? 'isActive' : 'unActive'"></div> -->
<img :src="props.active ? homeIconActive : homeIconUnActive" replace />
</template>
</van-tabbar-item>
<van-tabbar-item to="/home" name="home" replace>
<span>视频</span>
<template #icon="props">
<!-- <div :class="props.active ? 'isActive' : 'unActive'"></div> -->
<img :src="props.active ? shortVideoIconActive : shortVideoIconUnActive" />
</template>
</van-tabbar-item>
<!-- <van-tabbar-item to="/blackMaterial" name="blackMaterial" v-if="configStore?.appConfig?.hlH5">-->
<!-- <span>会员</span>-->
<!-- <template #icon="props">-->
<!-- <img :src="props.active ? blackMaterialActive : blackMaterialUnActive" replace />-->
<!-- </template>-->
<!-- </van-tabbar-item>-->
<van-tabbar-item to="/community" name="community" v-if="state.isCommunityShow">
<span>社区</span>
<template #icon="props">
<img :src="props.active ? communityActive : communityUnActive" replace />
</template>
</van-tabbar-item>
<van-tabbar-item to="/blackMaterial" name="blackMaterial">
<span>ACG</span>
<template #icon="props">
<!-- <div :class="props.active ? 'isActive' : 'unActive'"></div> -->
<img :src="props.active ? blackMaterialActive : blackMaterialUnActive" replace />
</template>
</van-tabbar-item>
<van-tabbar-item to="/mine" name="mine">
<span>我的</span>
<template #icon="props">
<!-- <div :class="props.active ? 'isActive' : 'unActive'"></div> -->
<img :src="props.active ? mineIconActive : mineIconUnActive" replace />
</template>
</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script lang="ts" setup>
import {nextTick, onMounted, reactive, ref, watch} from 'vue';
import homeIconActive from '@/assets/images/png/homeIconActive.png';
import homeIconUnActive from '@/assets/images/png/homeIconUnActive.png';
import shortVideoIconActive from '@/assets/images/png/shortVideoIconActive.png';
import shortVideoIconUnActive from '@/assets/images/png/shortVideoIconUnActive.png';
import blackMaterialActive from '@/assets/images/png/blackMaterialActive.png';
import blackMaterialUnActive from '@/assets/images/png/blackMaterialUnActive.png';
import loufengIconActive from '@/assets/images/png/loufengIconActive.png';
import loufengIconUnActive from '@/assets/images/png/loufengIconUnActive.png';
import mineIconActive from '@/assets/images/png/mineIconActive.png';
import mineIconUnActive from '@/assets/images/png/mineIconUnActive.png';
import communityUnActive from '@/assets/images/png/communityUnActive.png';
import communityActive from '@/assets/images/png/communityActive.png';
import { useRoute } from 'vue-router';
import { useConfigStore } from '@/stores/config';
import {useGlobalStore} from "@/stores/global";
const configStore: any = useConfigStore();
const route = useRoute();
const activeName = ref('home');
const globalStore = useGlobalStore();
const state = reactive({
isCommunityShow: false,
});
onMounted(() => {
refreshRoute(route.name);
let tabObj: any = globalStore.tabObj;
if (tabObj.community) {
if(tabObj.community){
state.isCommunityShow = true;
}
}
});
const refreshRoute = (name: any) => {
activeName.value = name;
};
watch(route, (newVal) => {
refreshRoute(newVal.name);
});
</script>
<style lang="scss" scoped>
:deep(.van-tabbar) {
padding-bottom: $TabPaddingBottom;
background: rgb(0, 0, 0);
z-index: 3;
//border-top: 1px solid rgb(75, 75, 75);
}
// :deep(.van-tabbar-item__icon) {
// width: 32px;
// height: 75px;
// img {
// height: 22px;
// width: 23px;
// position: absolute;
// left: 50%;
// transform: translate(-50%, 50%);
// }
// .unActive {
// width: 50px;
// left: -20%;
// top: 1px;
// height: 2px;
// background-image: linear-gradient(to right, rgb(255, 255, 255), rgb(255, 255, 255), rgb(255, 255, 255));
// }
// }
:deep(.van-tabbar-item__text) {
span {
display: block;
// margin-top: 35px;
}
}
:deep(.van-tabbar-item--active) {
background-color: unset;
.isActive {
width: 50px;
left: -20%;
top: 1px;
height: 2px;
background-image: linear-gradient(to right, rgb(255, 255, 255), rgb(255, 131, 0), rgb(255, 255, 255));
}
}
.blackMaterial {
height: 62px !important;
width: 62px !important;
top: -65%;
}
</style>
-211
View File
@@ -1,211 +0,0 @@
<template>
<div ref="textOverflow" class="text-overflow" :style="boxStyle">
<!-- <span ref="overEllipsis" class="overEllipsis">{{ realText }}</span> -->
<span v-if="isTopping" class="topping">置顶</span>
<span ref="overEllipsis" class="overEllipsis" v-html="rearrangeText(realText)"></span>
<span class="slot-box" ref="slotRef" v-if="state.showSlotNode">
<slot :click-toggle="toggle" :expanded="state.expanded"></slot>
</span>
</div>
</template>
<script lang="ts" setup>
import { jumpAdv } from '@/utils/index';
import { ref, reactive, computed, nextTick, onMounted, onUpdated, onActivated } from 'vue';
const props = defineProps({
text: {
type: String,
default: '',
},
maxLines: {
type: Number,
default: 3,
},
width: {
type: Number,
default: 0,
},
isTopping: {
type: Boolean,
default: false,
},
});
const overEllipsis = ref<HTMLElement>();
const slotRef = ref<HTMLElement>();
const textOverflow = ref<HTMLElement>();
const state = reactive({
offset: props.text.length,
expanded: false,
slotBoxWidth: 0,
textBoxWidth: props.width,
showSlotNode: false,
isTopping: props.isTopping,
});
const boxStyle = computed(() => {
if (props.width) {
return {
width: props.width + 'px',
};
}
return '0px';
});
const realText = computed(() => {
// 是否被截取
const isCutOut = state.offset !== props.text.length;
let realText = props.text;
if (isCutOut && !state.expanded) {
realText = props.text.slice(0, state.offset) + '...';
}
return realText;
});
const calculateOffset = (from: number, to: number) => {
nextTick(() => {
if (Math.abs(from - to) <= 1) return;
if (isOverflow()) {
to = state.offset;
} else {
from = state.offset;
}
state.offset = Math.floor((from + to) / 2);
calculateOffset(from, to);
});
};
const isOverflow = () => {
const { len, lastWidth } = getLines();
if (len < props.maxLines) {
return false;
}
if (props.maxLines) {
// 超出部分 行数 > 最大行数 或则 已经是最大行数但最后一行宽度 + 后面内容超出正常宽度
const lastLineOver = !!(len === props.maxLines && lastWidth + state.slotBoxWidth > state.textBoxWidth);
if (len > props.maxLines || lastLineOver) {
return true;
}
}
return false;
};
const getLines = () => {
const clientRects: any = overEllipsis.value?.getClientRects();
return {
len: clientRects.length,
lastWidth: clientRects[clientRects.length - 1]?.width,
};
};
const toggle = () => {
state.expanded = !state.expanded;
};
const rearrangeText = (text: any) => {
let originalText = '';
if (props.text && props.text.indexOf('##') != -1) {
let startIndex = props.text.indexOf('{');
let endIndex = props.text.indexOf('}');
let jsonText = props.text.substring(startIndex, endIndex + 1);
let jsonObj = JSON.parse(jsonText);
let texts = '##' + jsonText + '##';
originalText = text.replace(
texts,
`<div onclick="jumpKeywords('${jsonObj.type}', '${jsonObj.id}')" style='color:rgb(162,203,240);display: inline-block;'>${jsonObj.txt}<div style="height: 9px;width: 9px;display: inline-block;margin-bottom: 3px;background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAYCAMAAADJYP15AAAAOVBMVEVHcEyh0fuh0fyi0f2h0fyg0Pyf1f+f0/yfz/+h0fyfz/+h0Pyg0Pyh0fyf0vyi0vyh0fyh0fyg0fw8+yy6AAAAEXRSTlMAkLBwgPAwPxDGIODfoFBgXzlWcyEAAAC6SURBVCjPbZDbEsMgCEQNarxU027+/2MLFQ3pZF9kduQA65wof7aCRmF3VqlB1fMy84ZLLU1b3JdPrh6dq6igxDVpb3oBZSCYSwtYIxCkeDPhmuM8Y5Ts7VaMqfwUIFmbgIMfHni7IQy4Njmzrr+alhTKTd24WaE7bjNp3sMXt2oGYlyfeWgcq2c6zzPMWKMkR4Ejx/os+GKSXREKMg6rSGcxEdUjeFloj3OVuzhRbA++/09J1R9///QF6MAK320wOcMAAAAASUVORK5CYII=');background-size: 100% 100%;"></div></div>`,
);
return originalText;
}
return text;
};
function jumpKeywords(type: any, id: any) {
window.event.stopPropagation();
let item = {
href: '',
};
switch (type) {
case '1':
item.href = 'yinseinner://memberCentre';
break;
case '2':
item.href = 'yinseinner://sharePromote';
break;
case '3':
item.href = 'yinseinner://incomeCenter';
break;
case '4':
item.href = 'yinseinner://taskHall';
break;
case '5':
item.href = `yinseinner://taskDetails?id=${id}`;
break;
case '6':
item.href = 'yinseinner://wishList';
break;
case '7':
item.href = 'yinseinner://creationCenter';
break;
case '8':
item.href = 'yinseinner://myCertification';
break;
case '9':
item.href = 'yinseinner://announcement';
break;
case '10':
item.href = 'yinseinner://kefu';
break;
case '11':
item.href = `yinseinner://horizontalVideo?id=${id}`;
break;
case '12':
item.href = `yinseinner://userHomePage?uid=${id}`;
break;
case '13':
item.href = 'yinseinner://gameWallet';
break;
// case '14':
// case '15':
// item.href = `yinseinner://postDetails?id=${id}`;
// break;
}
jumpAdv(item);
}
onMounted(() => {
window.jumpKeywords = jumpKeywords;
const { len } = getLines();
if (len > props.maxLines) {
state.showSlotNode = true;
nextTick(() => {
state.slotBoxWidth = <number>slotRef.value?.clientWidth;
state.textBoxWidth = <number>textOverflow.value?.clientWidth;
calculateOffset(0, props.text.length);
});
}
});
</script>
<style scoped lang="scss">
.slot-box {
display: inline-block;
color: #246bfd;
}
.overEllipsis {
font-size: 15px;
line-height: 24px;
// color: $greyDeepColor;
color: rgba(255, 255, 255, 0.8);
white-space: pre-wrap;
}
.choosen {
font-size: 12px;
padding: 2px 5px;
border-radius: 3px;
color: #ffffff;
background: linear-gradient(to right, rgb(132, 164, 249), rgb(43, 93, 222));
display: inline-block;
margin: 0 5px 0 0;
}
.topping {
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
color: #1A1A1A;
background: #F1A100;
display: inline-block;
margin: 0 5px 0 0;
}
</style>
-202
View File
@@ -1,202 +0,0 @@
<template>
<div ref="textOverflow" class="text-overflow" :style="boxStyle">
<span ref="overEllipsis" class="overEllipsis" v-html="rearrangeText(realText)"></span>
<span class="slot-box" ref="slotRef" v-if="state.showSlotNode">
<slot :click-toggle="toggle" :expanded="state.expanded"></slot>
</span>
</div>
</template>
<script lang="ts" setup>
import { jumpAdv } from '@/utils/index';
import { ref, reactive, computed, nextTick, onMounted, onUpdated, onActivated } from 'vue';
const props = defineProps({
text: {
type: String,
default: '',
},
maxLines: {
type: Number,
default: 3,
},
width: {
type: Number,
default: 0,
},
isChoosen: {
type: Boolean,
default: false,
},
});
const overEllipsis = ref<HTMLElement>();
const slotRef = ref<HTMLElement>();
const textOverflow = ref<HTMLElement>();
const state = reactive({
offset: props.text.length,
expanded: true,
slotBoxWidth: 0,
textBoxWidth: props.width,
showSlotNode: false,
isChoosen: props.isChoosen,
});
const boxStyle = computed(() => {
if (props.width) {
return {
width: props.width + 'px',
};
}
return '0px';
});
const realText = computed(() => {
// 是否被截取
const isCutOut = state.offset !== props.text.length;
let realText = props.text;
if (isCutOut && !state.expanded) {
realText = props.text.slice(0, state.offset) + '...';
}
return realText;
});
const calculateOffset = (from: number, to: number) => {
nextTick(() => {
if (Math.abs(from - to) <= 1) return;
if (isOverflow()) {
to = state.offset;
} else {
from = state.offset;
}
state.offset = Math.floor((from + to) / 2);
calculateOffset(from, to);
});
};
const isOverflow = () => {
const { len, lastWidth } = getLines();
if (len < props.maxLines) {
return false;
}
if (props.maxLines) {
// 超出部分 行数 > 最大行数 或则 已经是最大行数但最后一行宽度 + 后面内容超出正常宽度
const lastLineOver = !!(len === props.maxLines && lastWidth + state.slotBoxWidth > state.textBoxWidth);
if (len > props.maxLines || lastLineOver) {
return true;
}
}
return false;
};
const getLines = () => {
const clientRects: any = overEllipsis.value?.getClientRects();
return {
len: clientRects.length,
lastWidth: clientRects[clientRects.length - 1]?.width,
};
};
const toggle = () => {
state.expanded = !state.expanded;
if (!state.expanded) {
calculateOffset(0, props.text.length);
}
};
const rearrangeText = (text: any) => {
let originalText = '';
if (props.text && props.text.indexOf('##') != -1) {
let startIndex = props.text.indexOf('{');
let endIndex = props.text.indexOf('}');
let jsonText = props.text.substring(startIndex, endIndex + 1);
let jsonObj = JSON.parse(jsonText);
let texts = '##' + jsonText + '##';
originalText = text.replace(
texts,
`<div onclick="jumpKeywords('${jsonObj.type}', '${jsonObj.id}')" style='color:rgb(162,203,240);display: inline-block;'>${jsonObj.txt}<div style="height: 9px;width: 9px;display: inline-block;margin-bottom: 3px;background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAYCAMAAADJYP15AAAAOVBMVEVHcEyh0fuh0fyi0f2h0fyg0Pyf1f+f0/yfz/+h0fyfz/+h0Pyg0Pyh0fyf0vyi0vyh0fyh0fyg0fw8+yy6AAAAEXRSTlMAkLBwgPAwPxDGIODfoFBgXzlWcyEAAAC6SURBVCjPbZDbEsMgCEQNarxU027+/2MLFQ3pZF9kduQA65wof7aCRmF3VqlB1fMy84ZLLU1b3JdPrh6dq6igxDVpb3oBZSCYSwtYIxCkeDPhmuM8Y5Ts7VaMqfwUIFmbgIMfHni7IQy4Njmzrr+alhTKTd24WaE7bjNp3sMXt2oGYlyfeWgcq2c6zzPMWKMkR4Ejx/os+GKSXREKMg6rSGcxEdUjeFloj3OVuzhRbA++/09J1R9///QF6MAK320wOcMAAAAASUVORK5CYII=');background-size: 100% 100%;"></div></div>`,
);
return originalText;
}
return text;
};
function jumpKeywords(type: any, id: any) {
window.event.stopPropagation();
let item = {
href: '',
};
switch (type) {
case '1':
item.href = 'yinseinner://memberCentre';
break;
case '2':
item.href = 'yinseinner://sharePromote';
break;
case '3':
item.href = 'yinseinner://incomeCenter';
break;
case '4':
item.href = 'yinseinner://taskHall';
break;
case '5':
item.href = `yinseinner://taskDetails?id=${id}`;
break;
case '6':
item.href = 'yinseinner://wishList';
break;
case '7':
item.href = 'yinseinner://creationCenter';
break;
case '8':
item.href = 'yinseinner://myCertification';
break;
case '9':
item.href = 'yinseinner://announcement';
break;
case '10':
item.href = 'yinseinner://kefu';
break;
case '11':
item.href = `yinseinner://horizontalVideo?id=${id}`;
break;
case '12':
item.href = `yinseinner://userHomePage?uid=${id}`;
break;
case '13':
item.href = 'yinseinner://gameWallet';
break;
// case '14':
// case '15':
// item.href = `yinseinner://postDetails?id=${id}`;
// break;
}
jumpAdvs(item);
}
onMounted(() => {
window.jumpKeywords = jumpKeywords;
const { len } = getLines();
console.log(len, props.maxLines);
if (len > props.maxLines) {
console.log(state.expanded);
state.showSlotNode = true;
nextTick(() => {
state.slotBoxWidth = <number>slotRef.value?.clientWidth;
state.textBoxWidth = <number>textOverflow.value?.clientWidth;
});
}
});
</script>
<style scoped lang="scss">
.slot-box {
display: inline-block;
color: rgba(62, 100, 234);
}
.overEllipsis {
font-size: 15px;
line-height: 24px;
// color: $greyDeepColor;
color: rgba(255, 255, 255, 0.8);
white-space: pre-wrap;
}
.choosen {
font-size: 12px;
padding: 2px 5px;
border-radius: 3px;
color: #ffffff;
background: linear-gradient(to right, rgb(132, 164, 249), rgb(43, 93, 222));
display: inline-block;
margin: 0 5px 0 0;
}
</style>
-238
View File
@@ -1,238 +0,0 @@
<template>
<div class="upload-img" @click.stop>
<van-uploader
v-model="state.fileList"
:after-read="afterRead"
@delete="delImg"
:multiple="multiple"
:before-read="beforeRead"
:max-count="maxCount"
>
<div class="addBox">
<slot>
<div class="upload-text" v-if="props.isCover">
<!-- <SvgIcon name="upload_add" height="15px" width="15px"></SvgIcon>-->
<img class="icon" src="@/assets/images/png/upload_add.png" alt="" />
<span>添加图片</span>
</div>
<div class="upload-text" v-else>
<img class="icon" src="@/assets/images/png/upload_add.png" alt="" />
<!-- <SvgIcon name="upload_add" height="18px" width="18px"></SvgIcon>-->
</div>
</slot>
</div>
</van-uploader>
</div>
</template>
<script lang="ts" setup>
import AppApi from '@/service/app';
import { showToast } from 'vant';
import { reactive } from 'vue';
const props = defineProps({
maxCount: {
type: String,
default: '99',
},
multiple: {
type: Boolean,
default: false,
},
isCover: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['uploadSuccess', 'delImg']);
const state = reactive({
fileList: [],
maxSize: 1024 * 1024 * 10,
});
const beforeRead = (file: any) => {
if (file.length > props.maxCount) {
showToast(`一次最多上传${props.maxCount}张图`);
return false;
}
if (file.length) {
file.map(async (item: any) => {
if (item.size > state.maxSize) {
showToast('单个图片最大为10M');
return false;
}
});
}
if (file.size > state.maxSize) {
showToast('单个图片最大为10M');
return false;
}
return true;
};
// 图片上传
const afterRead = async (file: any) => {
if (file.length) {
file.map(async (item: any) => {
let newFile = await compress(item.file);
item.file = newFile;
await uploadImg(item);
});
} else {
let newFile = await compress(file.file);
file.file = newFile;
uploadImg(file);
}
};
//图片上传
const uploadImg = async (fileInfo: any) => {
let req = new FormData();
req.append('upload', fileInfo.file);
fileInfo.status = 'uploading';
fileInfo.message = '上传中...';
try {
let ret: any = await AppApi.uploadFile(req);
fileInfo.status = 'done';
fileInfo.message = '上传成功';
emit('uploadSuccess', ret.coverImg);
} catch (e) {
fileInfo.status = 'failed';
fileInfo.message = '上传失败';
}
};
//将base64转换为文件对象
const convertBase64ToBlob = (base64: any) => {
var base64Arr = base64.split(',');
var imgtype = '';
var base64String = '';
if (base64Arr.length > 1) {
//如果是图片base64,去掉头信息
base64String = base64Arr[1];
imgtype = base64Arr[0].substring(base64Arr[0].indexOf(':') + 1, base64Arr[0].indexOf(';'));
}
// 将base64解码
var bytes = atob(base64String);
//var bytes = base64;
var bytesCode = new ArrayBuffer(bytes.length);
// 转换为类型化数组
var byteArray = new Uint8Array(bytesCode);
// 将base64转换为ascii码
for (var i = 0; i < bytes.length; i++) {
byteArray[i] = bytes.charCodeAt(i);
}
// 生成Blob对象(文件对象)
return new Blob([bytesCode], { type: imgtype });
};
// 压缩图片
const compress = (file: any) => {
if (!file || !window.FileReader) return;
if (/^image/.test(file.type)) {
let reader = new FileReader();
reader.readAsDataURL(file);
return new Promise((resolve) => {
reader.onloadend = function () {
let result: any = reader.result;
let img: any = new Image();
img.src = result;
if (result.length <= 200 * 1024) {
let blob = convertBase64ToBlob(result);
let files = new File([blob], file.name, { type: blob.type });
resolve(files);
return;
}
img.onload = function () {
let canvas = document.createElement('canvas');
let ctx: any = canvas.getContext('2d');
let width = img.width;
let height = img.height;
canvas.width = width;
canvas.height = height;
// 铺底色
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, width, height);
//进行最小压缩
let ndata = canvas.toDataURL('image/jpeg', 0.3);
let blob = convertBase64ToBlob(ndata);
let files = new File([blob], file.name, { type: blob.type });
resolve(files);
};
};
});
}
};
// 删除回调
const delImg = (_file: any, detail: any) => {
emit('delImg', detail.index);
};
// 清理图片列表
const clearImg = () => {
state.fileList = [];
};
defineExpose({
clearImg,
});
</script>
<style lang="scss" scoped>
.upload-img {
.addBox {
width: 111px;
height: 111px;
border-radius: 8px;
border: dashed 1px #4D4D4D;
background: #262626;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
.upload-text {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
span {
font-size: 12px;
color: #9a9a9a;
margin-top: 9px;
}
.icon {
width: 30px;
height: 30px;
}
}
margin: 0 2px 0 0;
}
:deep(.van-uploader__preview-delete) {
border-radius: 50%;
width: 16px;
height: 16px;
top: 4px;
right: 4px;
display: flex;
align-items: center;
border: 1px solid #ffffff;
.van-uploader__preview-delete-icon {
top: 0;
right: 0;
transform: scale(0.7);
}
}
:deep(.van-uploader__mask) {
border-radius: 6px;
}
:deep(.van-uploader__preview) {
width: 111px;
height: 111px;
border-radius: 6px;
margin: 0 3px 3px 0;
}
:deep(.van-image) {
width: 111px;
height: 111px;
border-radius: 6px;
box-sizing: border-box;
}
}
</style>
-347
View File
@@ -1,347 +0,0 @@
<template>
<div class="upload-video" @click.stop>
<van-uploader accept="video/*" :max-count="1" :after-read="afterVideo" v-if="!state.startUploadVideo">
<div class="upload-box">
<!-- <SvgIcon name="upload_add" height="15px" width="15px"></SvgIcon>-->
<img class="icon" src="@/assets/images/png/upload_add.png" alt="" />
<span>添加视频</span>
</div>
</van-uploader>
<!-- 上传中样式 -->
<div v-if="state.startUploadVideo" class="uploading">
<video class="previewVideo" id="video" style="object-fit: cover"></video>
<div class="video-mask"></div>
<van-circle
v-model:current-rate="state.currentRate"
:rate="state.uploadStatue.percent"
:speed="100"
color="#00b95b"
:text="state.currentRate + '%'"
size="50px"
class="circle"
/>
<p :class="state.uploadStatue.statusText == '上传成功' ? 'green' : 'F5164E'">
{{ state.uploadStatue.statusText }}
</p>
<div
class="close"
v-if="state.uploadStatue.statusText == '上传成功' || state.uploadStatue.statusText == '上传失败'"
@click="uploadDelete"
></div>
</div>
</div>
</template>
<script lang="ts" setup>
import SparkMD5 from 'spark-md5';
import { Buffer } from 'buffer';
import { reactive, nextTick } from 'vue';
import CommunityApi from '@/service/community';
const state = reactive({
requestAll: <any>[], //总请求
requestNum: 0, //正在请求的数量
requestMaxNum: 6, //同时最大请求数量
chunk_size: 1024 * 1024 * 2, //分片大小
startUploadVideo: false, //视频是否上传中
currentRate: 0,
uploadStatue: {
statusText: '上传中,请勿退出',
percent: 0, //当前上传文件执行的百分比
},
totalProgress: <any>{}, //总进度
sendVideoSucNum: 0, //分片上传视频,成功收到的返回
videoHeight: 0,
uploadData: {
newsType: 'SP', //上传类型 COVER-图集 SP-视频
tags: [], //视频标签数组
playTime: 0, //视频时长
cover: '', //封面
coverThumb: '', //封面小图
seriesCover: [], // 图集图片地址
via: '', //视频来源
coins: 0, //观看金币
size: 0, //文件大小
mimeType: '', //文件类型
actor: '', //演员名字
sourceURL: '', //资源url 为SP时 必传
sourceID: '', //上��视频成功后 返回���ID 为SP时必传
filename: '', //文件名
resolution: '', //分辨率
ratio: 1, //视频比例
md5: '', //文件摘要
freeTime: 0, //免费观影时长
location: {
city: '',
}, //地理位置信息
isActivity: false, //是否参加大赛
},
newUpload: {
sourceID: '', //上��视频成功后 返回���ID
md5: '', //文件摘要
type: 'vid',
src: '',
height: '',
width: '',
},
});
const emit = defineEmits(['uploadVideoSuccess']);
const fileReaderPromisify = (file: any) => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
let Spark = new SparkMD5.ArrayBuffer();
reader.readAsArrayBuffer(file);
reader.onload = function (e: any) {
Spark.append(e.target.result);
let md5 = Spark.end();
resolve(md5);
};
reader.onerror = (err) => {
reject(err);
};
});
};
// 上传视频
const afterVideo = async (e: any) => {
state.startUploadVideo = true;
let file = e.file;
let fileMd5: any = await fileReaderPromisify(file);
let file_chunks = getFileChunks(file);
let length = file_chunks.length;
for (let index in file_chunks) {
let chunk = file_chunks[Number(index)];
let opts = {
pos: Number(index) + 1,
totalPos: length,
data: Buffer.from(await chunk.arrayBuffer(), 'utf8').toString('base64'),
id: fileMd5,
};
state.uploadData.md5 = fileMd5;
state.newUpload.md5 = fileMd5;
state.requestAll.push(opts);
}
getVideoPlayerInfo(file); //获取视频宽高
requestConcurrency();
};
//限制请求数量
const requestConcurrency = () => {
while (state.requestAll.length > 0 && state.requestNum <= state.requestMaxNum && state.requestNum >= 0) {
let opts = state.requestAll.shift();
upload(opts);
}
};
// 将视频分片
const getFileChunks = (file: any) => {
let { size } = file;
let total_chunks = Math.ceil(size / state.chunk_size);
let file_chunks = [];
if (size > state.chunk_size) {
for (let i = 0; i < total_chunks; i++) {
let start = i * state.chunk_size;
let end = (i + 1) * state.chunk_size;
let chunk = file.slice(start, end);
state.totalProgress[i] = 0;
file_chunks.push(chunk);
}
} else {
file_chunks.push(file);
state.totalProgress[0] = 0;
}
return file_chunks;
};
// 开始上传
const upload = async (options: any) => {
try {
state.requestNum++;
let callback = (processCallback: any) => {
let percent = parseInt(((processCallback.loaded / processCallback.total) * 100).toString());
state.totalProgress[options.pos - 1] = percent.toFixed(2);
let strArr = Object.values(state.totalProgress);
let numberArr = strArr.map(Number);
state.uploadStatue.percent = Number(average(numberArr).toFixed(1));
};
let ret: any = await CommunityApi.uploadVideo(options, callback);
state.sendVideoSucNum++;
if (state.uploadStatue.percent == 100) {
// 所有分片请求发送完毕,等待返回
state.uploadStatue.statusText = '处理中,请勿退出';
}
if (state.uploadStatue.percent == 100 && state.sendVideoSucNum == options.totalPos) {
state.uploadStatue.statusText = '上传成功';
state.uploadData.sourceURL = ret.videoUri;
state.uploadData.sourceID = ret.id;
state.newUpload.sourceID = ret.id;
state.newUpload.src = ret.videoUri;
emit('uploadVideoSuccess', state.uploadData);
return;
}
} catch (e) {
state.currentRate = 0;
state.uploadStatue.percent = 0;
state.uploadStatue.statusText = '上传失败';
state.sendVideoSucNum = 0;
} finally {
state.requestNum--;
requestConcurrency();
}
};
// 获取视频信息
const getVideoPlayerInfo = (file: any) => {
state.uploadData.size = file.size;
state.uploadData.filename = file.name;
state.uploadData.mimeType = file.type;
// state.newUpload.type = file.type;
nextTick(() => {
const videoElement: any = document.getElementById('video');
videoElement.src = URL.createObjectURL(file);
videoElement.addEventListener('loadedmetadata', () => {
state.uploadData.playTime = parseInt(videoElement.duration);
state.videoHeight = videoElement.videoHeight;
state.uploadData.ratio = Number((videoElement.videoWidth / videoElement.videoHeight).toFixed(2));
state.uploadData.resolution = videoElement.videoWidth + 'x' + videoElement.videoHeight;
state.newUpload.height = videoElement.videoHeight;
state.newUpload.width = videoElement.videoWidth;
});
});
};
// 还原默认数据
const reductionData = () => {
state.uploadData = {
newsType: 'SP', //上传类型 COVER-图集 SP-视频
tags: [], //视频标签数组
playTime: 0, //视频时长
cover: '', //封面
coverThumb: '', //封面小图
seriesCover: [], // 图集图片地址
via: '', //视频来源
coins: 0, //观看金币
size: 0, //文件大小
mimeType: '', //文件类型
actor: '', //演员名字
sourceURL: '', //资源url 为SP时 必传
sourceID: '', //上��视频成功后 返回���ID 为SP时必传
filename: '', //文件名
resolution: '', //分辨率
ratio: 1, //视频比例
md5: '', //文件摘要
freeTime: 0, //免费观影时长
location: {
city: '',
}, //地理位置信息
isActivity: false, //是否参加大赛
};
state.newUpload = {
sourceID: '',
md5: '', //文件摘要
type: 'vid',
src: '',
height: '',
width: '',
};
};
// 删除视频
const uploadDelete = () => {
state.newUpload.src = 'vid';
state.uploadData.sourceURL = '';
state.uploadStatue.statusText = '上传中,请勿退出';
state.uploadStatue.percent = 0;
state.currentRate = 0;
state.sendVideoSucNum = 0;
state.startUploadVideo = false;
};
// 平均数
const average = (nums: any) => {
return nums.reduce((a: any, b: any) => a + b) / nums.length;
};
defineExpose({
reductionData,
uploadDelete,
});
</script>
<style lang="scss" scoped>
.upload-video {
margin: 0 3px 0 0;
.upload-box {
width: 111px;
height: 111px;
border-radius: 6px;
border: dashed 1px rgb(154, 154, 154);
background: #262626;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
position: relative;
box-sizing: border-box;
span {
font-size: 12px;
color: #9a9a9a;
margin-top: 9px;
}
.icon {
width: 30px;
height: 30px;
}
}
.upload-box span {
font-size: 10px;
color: rgb(129, 129, 129);
margin-top: 5.5px;
}
.uploadCover {
padding-left: 0.16rem;
}
/* 上传中 */
.uploading {
width: 111px;
height: 111px;
border-radius: 6px;
color: #fff;
background: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
font-size: 10px;
font-weight: 400;
}
.previewVideo {
width: 111px;
height: 111px;
border-radius: 6px;
position: absolute;
}
.video-mask {
width: 111px;
height: 111px;
border-radius: 6px;
position: absolute;
background: rgba(0, 0, 0, 0.6);
}
.circle {
margin-bottom: 5px;
}
.uploading .close {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
background: url('../../assets/images/png/community_close.png') center center no-repeat;
background-size: 100%;
}
:deep(.van-circle__text) {
color: #fff;
font-size: 12px;
}
}
</style>
-248
View File
@@ -1,248 +0,0 @@
<template>
<div class="videoBox" @click.stop="openVideo">
<div class="coverBox">
<lazyImg
class="cover"
:src="state.videoInfo?.type ? state.videoInfo?.cover : state.videoInfo?.verticalCover"
:key="state.videoInfo?.type ? state.videoInfo?.cover : state.videoInfo?.verticalCover"
/>
<div class="goldLabel" v-if="state.videoInfo?.price">
<SvgIcon name="goldIcon" class="goldIcon" />
<div class="price">{{ state.videoInfo?.price }}</div>
</div>
<div class="readNum" v-if="state.videoInfo?.kind == 4">
<SvgIcon name="book" class="bookIcon" />
{{ watchCount(state.videoInfo?.countBrowse) }}
</div>
<div class="readNum" v-if="state.videoInfo?.kind == 6">
<SvgIcon name="audio" class="bookIcon" />
{{ watchCount(state.videoInfo?.countView) }}
</div>
<div class="mask" :class="{ imgMask: state.videoInfo?.kind == 5 }">
<div
class="updatedDesc"
v-if="state.videoInfo?.kind == 1 || state.videoInfo?.kind == 4 || state.videoInfo?.kind == 6"
>
{{ `${state.videoInfo?.updateStatus == 0 ? '已完结' : '连载中'}${state?.videoInfo?.totalEpisode}话` }}
</div>
<div class="videoData" v-if="state.videoInfo?.kind == 5">
<div class="watchCount">
<SvgIcon name="viewCount" class="viewCount" />
{{ watchCount(state.videoInfo?.countBrowse) }}
</div>
<div class="workNum">
<SvgIcon name="workNumIcon" class="workNumIcon" />
</div>
</div>
</div>
</div>
<div class="videoTitle ellipsis">{{ state.videoInfo?.title ? state.videoInfo?.title : '无标题' }}</div>
</div>
</template>
<script lang="ts" setup>
import { watchCount, videoTime, addVideoHistory } from '@/utils/index';
import { useRoute, useRouter } from 'vue-router';
import { useVideoStore } from '@/stores/video';
import { jumpAdv } from '@/utils/index';
import path from 'path';
const route = useRoute();
const router = useRouter();
const videoStore = useVideoStore();
const props = withDefaults(
defineProps<{
videoInfo: any;
videoList: any;
type?: string;
}>(),
{
videoInfo: () => <any>{},
videoList: () => [],
type: () => '',
},
);
const emit = defineEmits(['changeVideo']);
const state = {
videoInfo: props.videoInfo,
};
// console.log(state.videoInfo);
const openVideo = async () => {
if (state.videoInfo.type) {
jumpAdv(state.videoInfo);
return;
}
if (state.videoInfo?.kind == 5) {
router.push({
path: '/portrayPage',
query: {
id: state.videoInfo.id,
},
});
} else {
if (route.path == '/novelDetailsPage') {
await router.replace({
path: '/novelDetailsPage',
query: {
id: state.videoInfo.id,
},
});
router.go(0);
} else {
router.push({
path: '/novelDetailsPage',
query: {
id: state.videoInfo.id,
},
});
}
}
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// if (state.videoInfo.newsType == 'COVER') {
// // router.push({
// // path: '/postDetails',
// // query: {
// // postId: state.videoInfo.id,
// // },
// // });
// } else {
// if (state.videoInfo.playTime > 300) {
// if (state.isMovie) {
// emit('changeVideo', state.videoInfo);
// } else {
// router.push({
// path: '/movieDetails',
// query: {
// vid: state.videoInfo.id,
// uid: state.videoInfo.publisher?.uid,
// queryPath: state.queryPath,
// },
// });
// }
// return;
// } else {
// videoStore.deleteVideoList();
// videoStore.saveVideoList(state.videoList, state.videoInfo);
// router.push('/shortVideoDetails');
// }
// }
};
</script>
<style lang="scss" scoped>
.videoBox {
width: 111px;
.coverBox {
width: 111px;
height: 146px;
position: relative;
border-radius: 8px;
overflow: hidden;
margin-bottom: 8px;
:deep(img) {
border-radius: 8px;
}
.goldLabel {
min-width: 38px;
height: 20px;
padding: 0 6px;
box-sizing: border-box;
border-radius: 0px 8px;
background: #f00;
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
.goldIcon {
width: 12px;
height: 12px;
margin-right: 2px;
}
.price {
color: #fff;
font-size: 10px;
}
}
.readNum {
color: #fff;
position: absolute;
top: 6px;
left: 6px;
height: 14px;
padding: 0 2px;
box-sizing: border-box;
display: flex;
align-items: center;
border-radius: 4px;
background: rgba(0, 0, 0, 0.5);
font-size: 10px;
.bookIcon {
width: 12px;
height: 12px;
margin-right: 2px;
}
}
.mask {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 30px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%);
padding: 0 8px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
color: #fff;
font-size: 10px;
.videoData {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.watchCount {
display: flex;
align-items: center;
font-size: 10px;
.viewCount {
width: 16px;
height: 16px;
margin-right: 2px;
}
}
.workNum {
display: flex;
align-items: center;
height: 18px;
padding: 0 4px;
box-sizing: border-box;
border-radius: 4px;
background: rgba(0, 0, 0, 0.8);
font-size: 10px;
}
}
}
.imgMask {
justify-content: space-between;
}
}
.videoTitle {
color: #fff;
width: 100%;
font-size: 12px;
}
}
</style>
-251
View File
@@ -1,251 +0,0 @@
<template>
<div class="vertical_video_warp">
<div id="video">
<div class="avoid_sliding" @click="pause"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import VideoApi from '@/service/video';
import { useConfigStore } from '@/stores/config';
import { useUserStore } from '@/stores/user';
import { debounce } from '@/utils';
import { onBeforeUnmount, onMounted, Ref, ref, toRefs } from 'vue';
import HlsJSPlayer from 'xgplayer-hls.js';
import { addVideoHistory } from '@/utils';
import { HistoryVideoType } from '@/enums/app';
import { analyticsEventVideo } from '@/utils/analyticsEvent';
import { useGlobalStore } from '@/stores/global';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
}>();
defineExpose({
success,
});
const { videoInfo } = toRefs(props);
const { isVip, freeVideo30, userInfo } = useUserStore();
let userFreeCount: Ref<Types.Video.UserFreeCount> = ref({
isCan: false,
watchCount: 0,
});
const globalStore = useGlobalStore();
const emit = defineEmits(['vipBuyShow', 'goldBuyShow', 'getFreeCount']);
let player: any = null; //播放器实例
let isReport = ref(false); // 视频埋点锁
onMounted(() => {
//获取免费观看次数
getFreeWatchCount();
//初始化播放器
initVideo();
//保存历史记录
addVideoHistory(props.videoInfo, HistoryVideoType.VIDEO);
});
/**
* @description 暂停
*/
const pause = () => {
player.pause();
};
/**
* @description 初始化播放器
*/
const initVideo = () => {
player = new HlsJSPlayer({
id: 'video',
url: getPath(),
fluid: true,
playsinline: true,
videoInit: true,
loop: true,
autoplay: true,
controls: true,
lang: 'zh-cn',
ignores: ['fullscreen', 'i18n', 'play', 'volume', 'definition'],
allowPlayAfterEnded: true,
allowSeekAfterEnded: true,
closeVideoPreventDefault: true,
closeVideoStopPropagation: true,
closeInactive: true,
enableVideoDbltouch: true,
});
player.on('timeupdate', () => {
if (player.hasStart) {
if (!playCheck()) {
pause();
if (player.video.currentTime > props.videoInfo.freeTime) {
analyticsEventVideo(props.videoInfo.id, props.videoInfo.title);
}
debounce(() => {
if (videoInfo.value.originCoins === 0) {
emit('vipBuyShow');
} else {
emit('goldBuyShow');
}
}, 100)();
}
//埋点
analytics();
}
});
};
/**
* @description 视频埋点
*/
const analytics = () => {
if (isReport.value) return;
if (props.videoInfo.playTime > 300) {
if (player.video.currentTime > props.videoInfo.playTime * 0.1) {
isReport.value = true;
analyticsEventVideo(props.videoInfo.id, props.videoInfo.title);
}
} else if (player.video.currentTime > 15) {
isReport.value = true;
analyticsEventVideo(props.videoInfo.id, props.videoInfo.title);
}
};
/**
* @description 获取用户免费观看次数
*/
const getFreeWatchCount = () => {
VideoApi.getPlayCount(videoInfo.value.id).then((userCount) => {
userFreeCount.value = userCount;
emit('getFreeCount', userCount);
});
};
/**
* @description 获取播放链接
*/
const getPath = () => {
const { baseUrl } = useConfigStore();
return `${baseUrl}/api/app/vid/h5/m3u8/${videoInfo.value.sourceURL}?token=${getToken()}&c=${
globalStore.videoRoadLine.url
}`;
};
/**
* @description 检测视频是否可以播放
*/
const playCheck = () => {
let freeTime = videoInfo.value.freeTime || 0;
//视频已购买
if (videoInfo.value.vidStatus.hasPaid === true) {
player.config.disableProgress = false;
return true;
}
//30金币以下视频免费看
if (freeVideo30 && videoInfo.value.originCoins < 30 && videoInfo.value.originCoins != 0) {
return true;
}
//免费时长
if (player.video.currentTime < freeTime) {
player.config.disableProgress = false;
return true;
}
// 会员视频
if (videoInfo.value.originCoins === 0 && isVip) {
player.config.disableProgress = false;
return true;
}
//自己发布的视频
if (userInfo.uid === videoInfo.value.publisher.uid) {
player.config.disableProgress = false;
return true;
}
// VIP免费次数
if (userFreeCount.value.isCan === true && videoInfo.value.originCoins === 0) {
player.config.disableProgress = false;
return true;
}
player.config.disableProgress = true;
return false;
};
//视频购买成功
function success() {
videoInfo.value.vidStatus.hasPaid = true;
player.play();
}
/**
* @description 获取token
*/
const getToken = () => {
const { token } = useUserStore();
return token;
};
onBeforeUnmount(() => {
if (player) {
player.src = '';
player.destroy(true);
}
player = null;
});
</script>
<style lang="scss" scoped>
// @media (min-height: 700px) {
// .vertical_video_warp {
// padding-top: 20px;
// }
// }
.vertical_video_warp {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
background: #000000;
align-items: center;
:deep(.xgplayer-progress-played) {
background: #ff0d2c;
}
:deep(.xgplayer-progress) {
top: 20px;
}
:deep(.xgplayer-start),
:deep(.xgplayer-controls) {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
:deep(.xgplayer-progress-btn) {
display: none !important;
}
:deep(.xgplayer-time-current:after) {
content: ' ';
}
:deep(.xgplayer-progress) {
left: 50%;
right: 0;
width: 70%;
transform: translate(-50%, 0);
}
:deep(.xgplayer-time) {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
margin-top: 10px;
}
:deep(.xgplayer-time-current)::after {
display: none;
}
#video {
width: 100%;
height: 100% !important;
position: absolute;
top: 0;
left: 0;
position: relative;
:deep(.xgplayer-enter-spinner) {
width: 50px;
height: 50px;
}
.avoid_sliding {
position: absolute;
width: 100%;
height: 100%;
background: transparent;
top: 0;
}
}
}
</style>
-430
View File
@@ -1,430 +0,0 @@
<template>
<div class="video_wrap">
<div class="video_content">
<!-- 图文播放器 -->
<FullScreenGallery
:videoInfo="videoInfo"
v-if="videoInfo.newsType == 'AD_COVER' || videoInfo.newsType == 'COVER'"
/>
<!-- 视频播放器 -->
<div class="video_box" v-else-if="videoInfo.newsType == 'SP' || videoInfo.newsType == 'AD_SP'">
<VerticalVideo
:video-info="videoInfo"
v-if="videoShow"
@goldBuyShow="goldBuyShow"
@vipBuyShow="vipBuyShow"
@getFreeCount="getFreeCount"
ref="verticalVideoRef"
/>
</div>
<!-- 右边菜单 -->
<div :class="['right_menu', { pb_8: isNewUser }]">
<div v-show="!pureMode.status">
<div class="right-info-item" @click.stop="clickLike">
<SvgIcon name="videoUnlike" v-if="!videoInfo.vidStatus.hasLiked"></SvgIcon>
<SvgIcon name="videoLike" v-else></SvgIcon>
<span>{{ watchCount(videoInfo.likeCount) }}</span>
</div>
<div class="right-info-item" @click.stop="openComment">
<SvgIcon name="videoComment"></SvgIcon>
<span>{{ watchCount(videoInfo.commentCount) }}</span>
</div>
<div class="right-info-item" @click.stop="share">
<SvgIcon name="videoShare"></SvgIcon>
<span>分享</span>
</div>
</div>
<div class="right-info-item" @click="pureMode.status = !pureMode.status">
<SvgIcon name="eye" v-if="!pureMode.status"></SvgIcon>
<SvgIcon name="uneye" v-else></SvgIcon>
</div>
</div>
<!-- 底部信息 -->
<div :class="['button_info', { pb60: isNewUser }]" v-show="!pureMode.status">
<div class="tags_box ellipsis">
<div
class="tags_item"
@click="router.push({ path: '/tagShortVideo', query: { tag: item.name, tagId: item.id } })"
v-for="item in videoInfo.tags.slice(0, 3)"
:key="item.id"
>
#{{ item.name }}
</div>
</div>
<div class="locationAnduserNameBox">
<span class="userName">@{{ videoInfo.publisher.name || '未知用户' }}</span>
</div>
<div class="title">{{ videoInfo.title }}</div>
<!-- 视频购买按钮 -->
<div v-if="videoInfo.newsType == 'SP' || videoInfo.newsType == 'COVER'">
<!-- 会员视频购买 -->
<div class="buying" @click="isJoinMerbershipShow = true" v-if="showVipBuy()">
<span>
{{
userFreeCount.isCan
? `免费视频,剩余免费次数${userFreeCount.watchCount}`
: `预览${
videoInfo.newsType == 'COVER' ? '3张' : `${videoInfo.freeTime}S`
},开通会员观看完整版(${videoTime(videoInfo.playTime)})`
}}
</span>
</div>
<!-- 金币视频购买 -->
<div class="buyingGold" v-else-if="showGoldBuy()" @click="isGoldCoinPurchase = true">
<span>{{ getGoldCoin() }}金币,购买完整版 ({{ videoTime(videoInfo.playTime) }})</span>
</div>
</div>
<!-- 广告下载按钮 -->
<div class="adv_down" v-if="videoInfo.newsType == 'AD_COVER' || videoInfo.newsType == 'AD_SP'">立即下载</div>
</div>
</div>
<!-- <div
class="new_user_box"
v-if="isNewUser && timeObj && timeObj.countdownSec > 0"
@click="$router.push('/recharge?name=member')"
>
<div class="flex-center-center">
<SvgIcon name="hot" />
<p>限时福利 新人专享年卡</p>
</div>
<div class="time">
<div class="time_item">{{ timeObj.tenHr }}</div>
<div class="time_item">{{ timeObj.hr }}</div>
<span>:</span>
<div class="time_item">{{ timeObj.tenMin }}</div>
<div class="time_item">{{ timeObj.min }}</div>
<span>:</span>
<div class="time_item">{{ timeObj.tenSec }}</div>
<div class="time_item">{{ timeObj.sec }}</div>
</div>
</div> -->
<!-- 会员弹窗 -->
<MemberPopup :videoInfo="videoInfo" :is-open="isJoinMerbershipShow" @close="isJoinMerbershipShow = false" />
<!-- 金币弹窗 -->
<GoldPopup
:is-open="isGoldCoinPurchase"
:video-info="videoInfo"
@close="isGoldCoinPurchase = false"
@buy-success="buySuccess"
/>
</div>
</template>
<script lang="ts" setup>
import { videoTime, watchCount } from '@/utils/index';
import { useUserStore } from '@/stores/user';
import { Ref, ref, toRefs, inject } from 'vue';
import VideoApi from '@/service/video';
import { LikeType } from '@/enums/app';
import { showToast } from 'vant';
import UserApi from '@/service/user';
import { useVideoStore } from '@/stores/video';
import { useRouter } from 'vue-router';
const router = useRouter();
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
videoShow: boolean;
pureMode: {
status: boolean;
};
timeObj: any;
}>();
const openShareBox: any = inject('openShareBox'); // 获取到祖组件的方法
const openRewardBox: any = inject('openRewardBox'); // 获取到祖组件的方法
const openCommentPopup: any = inject('openCommentPopup'); // 获取到祖组件的方法
let userFreeCount: Ref<Types.Video.UserFreeCount> = ref({
isCan: false,
watchCount: 0,
});
// const isNewUser = ref(true); //是否为新人
const { isVip, freeVideo30, isNewUser } = useUserStore();
const { videoInfo, pureMode } = toRefs(props);
const verticalVideoRef = ref();
let isJoinMerbershipShow = ref(false); //会员购买
let isGoldCoinPurchase = ref(false); //金币购买
//点赞-取消
const clickLike = () => {
let hasLiked = videoInfo.value.vidStatus.hasLiked;
let id = videoInfo.value.id;
try {
if (hasLiked) {
VideoApi.cancleLike(id, LikeType.LikeTypeVideo).then(() => {
videoInfo.value.vidStatus.hasLiked = false;
videoInfo.value.likeCount--;
showToast('取消成功');
});
} else {
VideoApi.like(id, LikeType.LikeTypeVideo).then(() => {
videoInfo.value.vidStatus.hasLiked = true;
videoInfo.value.likeCount++;
showToast('点赞成功');
});
}
} catch (e) {
console.log(e);
}
};
//金币购买弹窗
const goldBuyShow = () => {
isGoldCoinPurchase.value = true;
};
//免费观看视频次数
const getFreeCount = (userCount: Types.Video.UserFreeCount) => {
userFreeCount.value = userCount;
};
//会员购买弹窗
const vipBuyShow = () => {
isJoinMerbershipShow.value = true;
};
//获取当前视频价格
const getGoldCoin = () => {
return isVip ? videoInfo.value.coins : videoInfo.value.originCoins;
};
//会员按钮是否展示
const showVipBuy = () => {
const { isVip } = useUserStore();
if (videoInfo.value.originCoins == 0) {
if (isVip) return false;
return true;
}
return false;
};
//金币按钮是否展示
const showGoldBuy = () => {
//已经购买视频
if (videoInfo.value.vidStatus.hasPaid === true) {
return false;
}
//30金币以下视频免费看
if (freeVideo30 && videoInfo.value.originCoins < 30) {
return false;
}
//金币视频
if (videoInfo.value.originCoins > 0 && videoInfo.value.coins > 0) {
return true;
}
return false;
};
/**
* @description 视频购买成功
*/
const buySuccess = () => {
isGoldCoinPurchase.value = false;
verticalVideoRef.value.success();
};
// 打开分享弹窗
const share = () => {
openShareBox();
};
// 打开打赏弹窗
const openReward = () => {
openRewardBox(videoInfo.value.id);
};
// 打开评论弹窗
const openComment = () => {
openCommentPopup(videoInfo.value);
};
</script>
<style lang="scss" scoped>
.pb60 {
bottom: 40px !important;
}
.pb_8 {
bottom: 20px !important;
}
.video_wrap {
width: 100%;
height: 100%;
color: #ffffff;
position: relative;
overflow: hidden;
box-sizing: border-box;
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
.new_user_box {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
height: 30px;
background: rgba($color: #999999, $alpha: 0.44);
z-index: 1001;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
svg {
width: 16px;
height: 19px;
}
p {
padding-left: 11px;
font-size: 14px;
color: #ff0d2c;
line-height: 30px;
}
.time {
display: flex;
span {
padding-left: 4px;
}
.time_item {
margin-left: 4px;
width: 14px;
height: 17px;
background-color: #ffffff;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
color: #666666;
}
}
}
.video_content {
width: 100%;
height: 100%;
.video_box {
width: 100%;
height: 100%;
}
.right_menu {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
position: absolute;
right: 10px;
bottom: 3%;
z-index: 1000;
.right-info-item {
display: flex;
flex-direction: column;
align-items: center;
font-size: 12px;
padding-bottom: 19px;
svg {
width: 30px;
height: 30px;
}
span {
color: #ffffff;
padding-top: 5px;
}
:deep(.van-switch) {
height: 14px;
background: rgba($color: #f2f2f2, $alpha: 0.36);
width: 36.5px;
height: 15px;
}
:deep(.van-switch__node) {
width: 14px;
height: 14px;
top: 0.15vw;
left: 1.33vw;
}
}
}
.button_info {
width: 300px;
position: absolute;
bottom: 20px;
left: 14px;
z-index: 1000;
.tags_box {
width: 300px;
display: flex;
.tags_item {
padding-right: 14px;
font-size: 12px;
letter-spacing: 0.24px;
color: $themeColor;
}
}
.locationAnduserNameBox {
padding-top: 13px;
white-space: nowrap;
overflow: hidden;
display: flex;
align-items: center;
.locationAnduser {
display: flex;
align-items: center;
}
svg {
font-size: 22px;
}
.location {
display: inline-block;
font-size: 12px;
padding: 0 13px 0 0;
}
.userName {
display: inline-block;
font-size: 16px;
}
}
.title {
padding-top: 10px;
font-size: 12px;
letter-spacing: 0.24px;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.buying {
margin-top: 10px;
width: fit-content;
padding: 0 10px;
height: 24px;
position: relative;
background-color: rgba(253, 191, 80, 0.35);
border-radius: 13px;
padding-left: 22px;
display: flex;
align-items: center;
span {
font-size: 10px;
}
}
.buyingGold {
margin-top: 10px;
width: 171px;
height: 24px;
position: relative;
background: url('../../assets/images/png/goldBuy.png') no-repeat;
background-size: 100%;
padding-left: 22px;
display: flex;
align-items: center;
span {
font-size: 10px;
}
}
.adv_down {
margin-top: 10px;
width: 94px;
height: 26px;
background: #ff112f;
border-radius: 15px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
</style>
-117
View File
@@ -1,117 +0,0 @@
<template>
<van-overlay :show="isShow" z-index="2001" @click="emit('close')">
<div class="wrapper-gold flex-center-center">
<div class="content">
<div class="title">开通VIP</div>
<div class="line"></div>
<div class="about-promote">
<div class="sub-title">推广赚钱,最高返利75%</div>
<p>成功推广好友,立即获得会员权益</p>
<div class="tip">你的观看次数不足</div>
</div>
<div class="btn-box flex-center-between">
<router-link class="promote-btn btn" to="/promotionCode">推广赚钱</router-link>
<div class="recharge-btn btn" @click="joinMembership">开通VIP</div>
</div>
</div>
<div class="close" @click="emit('close')">
<SvgIcon name="close" />
</div>
</div>
</van-overlay>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
isOpen: boolean;
}>();
const emit = defineEmits(['close']);
const isShow = computed(() => {
return props.isOpen;
});
/**
* @description 开通会员
*/
const joinMembership = () => {
router.push({
path: '/recharge',
query: {
name: 'member',
id: props.videoInfo.id,
title: props.videoInfo.title || '无标题',
},
});
};
</script>
<style lang="scss" scoped>
.wrapper-gold {
width: 100%;
height: 100%;
flex-direction: column;
.content {
width: 300px;
text-align: center;
background: linear-gradient(to bottom, #fff0f1, #ffffff, #ffffff);
border-radius: 8px;
padding: 0 14px 21px 14px;
.title {
padding: 16px 0;
font-size: 24px;
font-weight: 400;
}
.line {
margin: 0 auto;
width: 100%;
height: 1px;
background: #ebebeb;
}
.about-promote {
padding: 18px 0px;
.sub-title {
font-size: 18px;
color: $titleColor;
font-weight: 500;
}
p {
font-size: 14px;
color: $titleColor;
margin-top: 10px;
font-weight: 500;
}
.tip {
font-size: 14px;
color: $greyDeepColor;
margin-top: 26px;
}
}
.btn-box {
.btn {
height: 38px;
width: 129px;
line-height: 38px;
text-align: center;
border-radius: 4px;
}
.promote-btn {
border: 1.5px solid $themeColor;
color: $themeColor;
}
.recharge-btn {
background-color: $themeColor;
color: white;
}
}
}
.close {
padding-top: 16px;
svg {
font-size: 32px;
}
}
}
</style>
-112
View File
@@ -1,112 +0,0 @@
<template>
<div class="vipPromptDialog" v-if="props.show">
<van-overlay :show="props.show">
<div class="contentBox">
<div class="content">
<div class="title">
<span>温馨提示</span>
</div>
<div class="desc">
<span>{{ props.msg }}</span>
</div>
<div class="btnBox">
<div class="btn" @click="$router.push('/recharge?name=member')">
<span>前往购买VIP</span>
</div>
</div>
</div>
<div class="btnBox" @click="close">
<img src="@/assets/images/png/closeDialog.png" />
</div>
</div>
</van-overlay>
</div>
</template>
<script lang="ts" setup>
import { withDefaults } from 'vue';
const props = withDefaults(
defineProps<{
show: boolean;
msg: string;
}>(),
{
show: () => false,
msg: () => '',
},
);
const emit = defineEmits(['close']);
/**
* 关闭弹窗
*/
const close = () => {
emit('close', false);
};
</script>
<style lang="scss" scoped>
.vipPromptDialog {
width: 100%;
height: 100%;
.contentBox {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
.content {
width: 300px;
//height: 249px;
background: rgb(255, 255, 255);
border-radius: 11px;
padding: 20px 0;
.title {
font-size: 20px;
color: rgb(51, 51, 51);
font-weight: bold;
text-align: center;
}
.desc {
font-size: 16px;
color: rgb(51, 51, 51);
text-align: center;
margin-top: 20px;
}
.btnBox {
display: flex;
justify-content: center;
margin-top: 20px;
.btn {
width: 189px;
height: 37px;
background: rgb(255, 13, 44);
color: rgb(255, 255, 255);
display: flex;
justify-content: center;
align-items: center;
border-radius: 18.5px;
}
}
}
.btnBox {
margin-top: 24px;
img {
width: 32px;
height: 32px;
}
}
}
}
</style>
-132
View File
@@ -1,132 +0,0 @@
<template>
<div class="vipTipPopup">
<van-popup
v-model:show="state.isRewardShow"
teleport="body"
:close-on-click-overlay="false"
:style="{ backgroundColor: 'rgba(0, 0, 0, 0)' }"
>
<div class="popupBox" v-if="state.isShowGold">
<div class="title">温馨提示</div>
<div class="desc">
<p>您的金币余额不足,请充值金币</p>
</div>
<div class="btnBox flex-center-center">
<div class="btn" @click="state.isRewardShow = false">取消</div>
<div class="btn BindBtn" @click="$router.push({ path: '/recharge', query: { name: 'wallet' } })">
前往充值
</div>
</div>
</div>
<div class="popupBox" v-else>
<div class="listbox">
<p>该功能为会员权益哦</p>
</div>
<div class="btnBox flex-center-center">
<div class="btn" @click="cancel" v-if="!props.isCancelReturn">下次再说</div>
<div class="btn" @click="state.isRewardShow = false" v-else>下次再说</div>
<div class="btn BindBtn" @click="openVip">开通会员</div>
</div>
</div>
</van-popup>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
const props = defineProps<{
isCancelReturn?: boolean;
isShowGold?: string;
}>();
const state = reactive({
isRewardShow: false,
isShowGold: props.isShowGold,
});
const router = useRouter();
const cancel = () => {
router.go(-1);
state.isRewardShow = false;
};
const openPop = () => {
state.isRewardShow = true;
};
const openVip = () => {
router.push({
path: '/recharge',
});
};
defineExpose({
openPop,
});
</script>
<style scoped lang="scss">
//.vipTipPopup {
// background-color: rgba(255, 255, 255);
.popupBox {
background: rgba(255, 255, 255);
border-radius: 6px;
height: 136px;
width: 311px;
overflow: hidden;
position: relative;
.title {
font-weight: 600;
color: #000;
font-size: 18px;
text-align: center;
padding-top: 18px;
}
.desc {
font-weight: 400;
color: #00000099;
font-size: 16px;
text-align: center;
height: 55px;
padding: 16px 0;
}
.listbox {
width: 100%;
height: 60px;
line-height: 70px;
padding: 0 15px;
box-sizing: border-box;
margin-bottom: 20px;
p {
font-weight: 500;
color: #000;
font-size: 16px;
text-align: center;
}
}
}
.btnBox {
margin: 0 auto;
.btn {
width: 100%;
text-align: center;
line-height: 48px;
border-top: 0.5px solid rgba(231, 231, 231, 1);
}
.BindBtn {
color: $themeColor;
border-left: 0.5px solid rgba(231, 231, 231, 1);
}
}
:deep(.van-popup) {
border-radius: 12px;
}
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
//}
</style>
-66
View File
@@ -1,66 +0,0 @@
<template>
<div class="adv-list" v-if="state.advsList.length > 0">
<div class="rectAdvBox" @click="jumpAdv(item)" v-for="item in state.advsList" :key="item.id">
<lazyImg class="rectAdvIcon" :src="item.cover" />
<div class="rectName">{{ item.title }}</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { getAdItem } from '@/utils/getConfig';
import { AdType } from '@/enums/app';
import { jumpAdv } from '@/utils/index';
import { reactive } from 'vue';
const state = reactive({
advsList: <Array<Types.App.AdsInfoListItem>>[], // 专栏之间的广告
});
const props = defineProps({
index: {
type: Number,
default: 0,
},
moduleCount: {
type: Number,
default: 0,
},
});
const handleAdvList = (origin: any) => {
const arr = [];
let j = 0;
for (let index = 0; index < props.moduleCount * 5; index++, j++) {
if (!origin[j]) {
j = 0;
}
arr[index] = origin[j];
}
return arr;
};
console.log(handleAdvList(getAdItem(AdType.HOMETOPIC)).length, getAdItem(AdType.HOMETOPIC));
if (getAdItem(AdType.HOMETOPIC) && getAdItem(AdType.HOMETOPIC).length > 0) {
state.advsList = handleAdvList(getAdItem(AdType.HOMETOPIC)).slice(props.index * 5, (props.index + 1) * 5);
}
</script>
<style lang="scss" scoped>
.adv-list {
display: flex;
gap: 12px;
color: #fff;
// margin-top: 10px;
}
.rectAdvBox {
width: 60px;
.rectAdvIcon {
height: 60px;
width: 60px;
border-radius: 12px;
overflow: hidden;
}
.rectName {
font-size: 12px;
margin-top: 4px;
overflow: hidden;
white-space: nowrap;
text-align: center;
}
}
</style>
-263
View File
@@ -1,263 +0,0 @@
<template>
<div class="cardPopup">
<van-popup v-model:show="state.isRewardShow" teleport="body" z-index="2002">
<div class="popupBox">
<h1 class="title flex-center-center">身/份/证/卡</h1>
<img class="logoImg flex-center-center" src="@/assets/images/png/cardLogo.png" />
<!-- <lazyImg class="avatar" :src="state.userInfo.portrait" round :key="state.userInfo.portrait" /> -->
<!-- <div class="promoteCode flex-center-center">
<span>{{ state.userInfo.promotionCode }}</span>
</div> -->
<div class="qrCodeBg">
<div class="qrCodeBox flex-center-center">
<qrcode-vue id="qrCode" class="qrCodeImg" v-if="state.qrUrl" :value="state.qrUrl" level="L" />
<van-loading size="50px" type="spinner" color="#c0c9ea" v-else />
</div>
</div>
<div class="lines"></div>
<div class="contentBox">
<div class="addressBox flex-center-between">
<div class="address">
<SvgIcon name="Account" class="official" />
账号:
<span>{{ state.userInfo.uid }}</span>
</div>
</div>
<div class="addressBox flex-center-between">
<div class="address">
<SvgIcon name="emails" class="official" />
官方邮箱:
<span>{{ state.email }}</span>
</div>
<div class="officialCopy" @click="copyLink('email')">复制</div>
</div>
<div class="addressBox flex-center-between">
<div class="address">
<SvgIcon name="official" class="official" />
官方网站:
<span>{{ state.url }}</span>
</div>
<div class="officialCopy" @click="copyLink('url')">复制</div>
</div>
</div>
</div>
<div class="btnBox flex-center-center">
<div class="btn" @click="showToast('请手动截图保存')">请截图保存</div>
</div>
</van-popup>
</div>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue';
import { useUserStore } from '@/stores/user';
import UserApi from '@/service/user';
import { showToast } from 'vant';
import { useConfigStore } from '@/stores/config';
import useClipboard from 'vue-clipboard3';
const configStore = useConfigStore();
const { toClipboard } = useClipboard();
const userStore = useUserStore();
const userInfo: any = userStore.userInfo;
const state = reactive({
userInfo: <Types.User.UserInfoType>{},
userWallet: <Types.User.WalletInfo>{},
qrUrl: <string>'',
url: configStore.sourceInfo.LAND,
email: configStore.sourceInfo.EMAIL,
isRewardShow: false,
qrCodeId: <string>'',
});
nextTick(async () => {
/**
* 获取用户信息
*/
const userStore = useUserStore();
state.userInfo = userStore.userInfo;
let ret: any = await UserApi.queryCertificate();
state.qrUrl = ret.content;
});
const copyLink = async (val: string) => {
state.qrCodeId = val;
try {
if (!state.qrCodeId) {
showToast('复制失败');
return;
}
if (state.qrCodeId === 'url') {
await toClipboard(state.url); //实现复制
showToast('复制成功');
} else if (state.qrCodeId === 'email') {
await toClipboard(state.email); //实现复制
showToast('复制成功');
}
} catch (e: any) {
console.log(e);
}
};
const openPop = () => {
state.isRewardShow = true;
};
defineExpose({
openPop,
});
</script>
<style lang="scss" scoped>
// width: 100%;
// height: 100%;
// background-color: rgba(255, 255, 255);
.popupBox {
background: linear-gradient(180deg, #f9ca26 0%, #ffffff 100%);
border-radius: 6px;
height: 470px;
width: 343px;
overflow: hidden;
position: relative;
padding: 6px 0;
.title {
font-size: 24px;
font-weight: 600;
letter-spacing: 6px;
}
.logoImg {
width: 199.29px;
height: 56px;
margin: 10px auto;
}
.avatar {
height: 76px;
width: 76px;
border-radius: 50%;
border: 3px solid rgb(234, 247, 255);
position: absolute;
left: 50%;
top: -38px;
transform: translate(-50%, 0);
z-index: 1;
background-color: rgb(234, 247, 255);
}
.promoteCode {
position: absolute;
width: 100%;
top: 46px;
font-size: 16px;
color: rgb(24, 24, 24);
text-align: center;
span {
font-size: 28px;
}
}
.qrCodeBg {
border: 2px solid rgba(255, 173, 0, 1);
border-radius: 12px;
margin: 0 auto;
// position: absolute;
// background: url('./../../../assets/images/png/actCredentialsBg.png') no-repeat;
// background-size: 100% 100%;
// top: 107px;
// left: 50%;
// transform: translate(-50%, 0);
width: 222px;
height: 222px;
display: flex;
justify-content: center;
align-items: center;
.qrCodeBox {
background-color: white;
width: 208px;
height: 208px;
border-radius: 10px;
.qrCodeImg {
width: 190px !important;
height: 190px !important;
}
}
}
.lines {
margin: 19px 0;
height: 3px;
background-image: linear-gradient(to right, rgba(255, 173, 0, 1) 0%, rgba(255, 173, 0, 1) 50%, transparent 0%);
background-size: 14px 3px;
background-repeat: repeat-x;
}
.lines::after {
content: '';
position: absolute;
left: 0;
top: -22.5px;
width: 22.5px;
height: 45px;
// border-radius: 45px 0 0 45px;
border-radius: 0 45px 45px 0;
background: #000;
}
.lines::before {
content: '';
position: absolute;
top: -22.5px;
right: 0;
width: 22.5px;
height: 45px;
border-radius: 45px 0 0 45px;
background: #000;
}
.contentBox {
display: flex;
justify-content: flex-start;
flex-direction: column;
padding: 0 16px;
.addressBox {
margin: 6px 0;
.address {
display: inline-block;
width: 100%;
font-size: 14px;
color: rgb(24, 24, 24);
.official {
vertical-align: top;
width: 14px;
height: 14px;
}
span {
font-weight: 700;
white-space: nowrap; /*强制span不换行*/
}
}
.officialCopy {
font-size: 14px;
white-space: nowrap; /*强制span不换行*/
display: inline-block;
width: 32px;
line-height: 0.9;
}
}
}
}
.btnBox {
margin: 25px auto;
.btn {
height: 40px;
width: 343px;
background-color: $themeColor;
border-radius: 100px;
text-align: center;
line-height: 40px;
color: $titleColor;
font-size: 16px;
font-weight: 600;
}
}
:deep(.van-popup) {
border-radius: 10px;
padding-bottom: 100px;
}
:deep(.van-overlay) {
background-color: rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
}
</style>