初始化黄果短剧 H5 客户端
对接后端 hgmjapi.mfcg.me: - 接口解密 key 更新为 resKey360 - 登录 sysType 使用原生 getDevType(后端已放开 pc/android) - 修正榜单接口路径 /vid/rangking → /vid/ranking,方法改为 GET Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="contact">
|
||||
<div class="title">商务合作</div>
|
||||
<div class="contact-list">
|
||||
<div class="item" v-for="(item, index) in state.contactInfo" :key="index" @click="openPage(item.officialUrl)">
|
||||
<lazyImg class="connectImg" :src="item.officialImg" />
|
||||
<div class="text">
|
||||
<p class="name">{{ item.officialName }}</p>
|
||||
<p class="described">{{ item.officialDesc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue';
|
||||
import AppApi from '@/service/app';
|
||||
const state = reactive({
|
||||
contactInfo: <any>[],
|
||||
});
|
||||
const queryContactInfo = async () => {
|
||||
try {
|
||||
let req = {
|
||||
type: 3,
|
||||
};
|
||||
const res: any = await AppApi.getBusiness(req);
|
||||
state.contactInfo = res || [];
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
queryContactInfo();
|
||||
const openPage = (url: string) => {
|
||||
window.open(url);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.contact {
|
||||
color: #fff;
|
||||
width: 1104px;
|
||||
margin: 112px auto 0;
|
||||
padding-bottom: 70px;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 41px;
|
||||
}
|
||||
.contact-list {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 61px;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.connectImg {
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
border-radius: 50%;
|
||||
margin-right: 17px;
|
||||
}
|
||||
.name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.described {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="classification">
|
||||
<div class="item-wrap" v-for="(item, index) in tagList" :key="index">
|
||||
<div class="title">{{ item.subModuleName }}</div>
|
||||
<div class="tag-list">
|
||||
<span v-for="(tag, i) in item.allSection" :key="i" @click="toListOfTopics(tag)">{{ tag.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import HomeApi from '@/service/home';
|
||||
import { ref} from 'vue'
|
||||
const router = useRouter()
|
||||
const tagList = ref([])
|
||||
const getTagList = async() => {
|
||||
const res: any = await HomeApi.getTabs({ isSection: true })
|
||||
tagList.value = res.homePage;
|
||||
}
|
||||
getTagList()
|
||||
|
||||
const toListOfTopics = (item: any) => {
|
||||
router.push({
|
||||
path: '/listOfTopics',
|
||||
query: {
|
||||
sectionid: item.id,
|
||||
name: item.name,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.classification {
|
||||
width: 1248px;
|
||||
min-height: 500px;
|
||||
padding: 24px;
|
||||
margin: 36px auto;
|
||||
}
|
||||
.item-wrap {
|
||||
padding-bottom: 44px;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 32px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
.tag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 12px;
|
||||
span {
|
||||
width: 190px;
|
||||
height: 32px;
|
||||
color: #FFF;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="contact">
|
||||
<div class="title">官方联系</div>
|
||||
<div class="contact-list">
|
||||
<div class="item" v-for="(item, index) in contactInfo" :key="index" @click="openPage(item.officialUrl)">
|
||||
<lazyImg class="connectImg" :src="item.officialImg" />
|
||||
<div class="text">
|
||||
<p class="name">{{ item.officialName }}</p>
|
||||
<p class="described">{{ item.officialDesc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
const configStore = useConfigStore();
|
||||
const contactInfo = computed(() => {
|
||||
return configStore.contactInfo;
|
||||
});
|
||||
const openPage = (url: string) => {
|
||||
window.open(url);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.contact {
|
||||
color: #fff;
|
||||
width: 1104px;
|
||||
margin: 112px auto 0;
|
||||
padding-bottom: 70px;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 41px;
|
||||
}
|
||||
.contact-list {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 61px;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.connectImg {
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
border-radius: 50%;
|
||||
margin-right: 17px;
|
||||
}
|
||||
.name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.described {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="banner-wrap">
|
||||
<swiper :loop="true" ref="swiperDom"
|
||||
:autoplay="{ delay: 5000, disableOnInteraction: false, pauseOnMouseEnter: true, }" :modules="[Autoplay]"
|
||||
@slideChange="onSlideChange">
|
||||
<swiper-slide v-for="(item, index) in swiperList" :key="index" @click="toMoivePlay(item.id)">
|
||||
<DecryptImg class="banner-img" :src="item.cover" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<div class="btn-list">
|
||||
<img @click="toMoivePlay(swiperList[currentIndex]?.id)" src="@/assets/images/pc/play.png" alt="">
|
||||
<img @click="toMoivePlay(swiperList[currentIndex]?.id)" src="@/assets/images/pc/like1.png" alt="">
|
||||
</div>
|
||||
<img @click="slideNext" class="left-arrow" src="@/assets/images/pc/left_arrow.png" alt="">
|
||||
<img @click="slidePrev" class="right-arrow" src="@/assets/images/pc/left_arrow.png" alt="">
|
||||
<div class="point-list">
|
||||
<i @click="changeSlideTo(i)" v-for="i in swiperList.length" :key="i"
|
||||
:class="{ active: currentIndex === i - 1 }"></i>
|
||||
</div>
|
||||
<div class="mask"></div>
|
||||
<div class="slide-wrap" v-if="bannerList.length">
|
||||
<swiper ref="swiperDom1" :modules="[Pagination, FreeMode]" free-mode :slides-per-view="3.2" :space-between="24">
|
||||
<swiper-slide v-for="(item, index) in bannerList" :key="index">
|
||||
<div class="card-item" @click="jumpPage('/moviePlay?page=1&vid=' + item.id + '&topindex=' + (index + 1))">
|
||||
<div class="item-mask"></div>
|
||||
<div :class="'top-img' + index"></div>
|
||||
<DecryptImg class="cover" :src="item.cover" />
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<img @click="slidePrev1" class="left-arrow1" src="@/assets/images/pc/left_arrow.png" alt="">
|
||||
<div class="rigth-mask">
|
||||
<img @click="slideNext1" class="right-arrow1" src="@/assets/images/pc/left_arrow.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Autoplay, Pagination, FreeMode } from 'swiper'
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
import 'swiper/swiper.min.css';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
|
||||
withDefaults(defineProps<{
|
||||
swiperList: Array<any>;
|
||||
bannerList: Array<any>;
|
||||
}>(), {
|
||||
swiperList: () => <Array<any>>[],
|
||||
bannerList: () => <Array<any>>[]
|
||||
})
|
||||
|
||||
const currentIndex = ref(0)
|
||||
const onSlideChange = (swiper: any) => {
|
||||
console.log(swiper.realIndex);
|
||||
|
||||
currentIndex.value = swiper.realIndex;
|
||||
}
|
||||
|
||||
const swiperDom = ref(null)
|
||||
const slideNext = () => {
|
||||
swiperDom.value && swiperDom.value.$el.swiper.slideNext()
|
||||
}
|
||||
|
||||
const slidePrev = () => {
|
||||
swiperDom.value && swiperDom.value.$el.swiper.slidePrev()
|
||||
}
|
||||
const changeSlideTo = (i: number) => {
|
||||
swiperDom.value && swiperDom.value.$el.swiper.slideTo(i, 500);
|
||||
}
|
||||
|
||||
const swiperDom1 = ref(null)
|
||||
const slideNext1 = () => {
|
||||
swiperDom1.value && swiperDom1.value.$el.swiper.slideNext()
|
||||
}
|
||||
|
||||
const slidePrev1 = () => {
|
||||
swiperDom1.value && swiperDom1.value.$el.swiper.slidePrev()
|
||||
}
|
||||
const toMoivePlay = (id: string) => {
|
||||
router.push({
|
||||
path: '/moviePlay',
|
||||
query: {
|
||||
vid: id,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const jumpPage = (path: string) => {
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.banner-wrap {
|
||||
width: 100%;
|
||||
height: 830px;
|
||||
margin-bottom: 190px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 830px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.left-arrow {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 34px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.right-arrow {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
transform-origin: center;
|
||||
left: 34px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.point-list {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: 102px;
|
||||
right: 56px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 1.5px;
|
||||
background: rgba(255, 255, 255, 0.30);
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #fff;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.mask {
|
||||
width: 100%;
|
||||
height: 217px;
|
||||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.80) 1.8%, rgba(0, 0, 0, 0.00) 78.8%);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.slide-wrap {
|
||||
width: 1516px;
|
||||
height: 260px;
|
||||
position: absolute;
|
||||
bottom: -190px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 3;
|
||||
|
||||
.card-item {
|
||||
width: 462px;
|
||||
height: 260px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.item-mask {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.60) 83%);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.cover {
|
||||
width: 462px;
|
||||
height: 260px;
|
||||
}
|
||||
|
||||
.left-arrow1 {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
transform-origin: center;
|
||||
left: -40px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rigth-mask {
|
||||
width: 100px;
|
||||
height: 260px;
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.01) 0%, #000 80%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.right-arrow1 {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 0px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.btn-list {
|
||||
width: 152px;
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
bottom: 123px;
|
||||
left: 206px;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@for $i from 0 through 19 {
|
||||
.top-img#{$i} {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
z-index: 2;
|
||||
width: 170px;
|
||||
height: 68px;
|
||||
background: url('@/assets/images/pc/top/top#{$i + 1}.png') center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="thematic" v-for="(item, index) in videoList" :key="index">
|
||||
<div class="title">
|
||||
{{ item.sectionName }}
|
||||
<div class="more" @click="toListOfTopics(item)">
|
||||
<img class="left-arrow" src="@/assets/images/pc/more.png" alt="">
|
||||
更多
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-list">
|
||||
<movieCard :itemData="itemData" v-for="(itemData, i) in item.allVideoInfo" :key="i" />
|
||||
</div>
|
||||
<div class="adv-list-wrap" v-if="index % 2 === 1">
|
||||
<DecryptImg v-for="(item, i) in renderAdvList(index)" class="adv" :src="item.cover" @click="jumpAdv(item)" :key="i"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { jumpAdv } from '@/utils/index';
|
||||
import { useRouter } from 'vue-router';
|
||||
withDefaults(defineProps<{
|
||||
videoList: Array<any>[]
|
||||
}>(), {
|
||||
videoList: () => []
|
||||
})
|
||||
const router = useRouter()
|
||||
const toListOfTopics = (item: any) => {
|
||||
router.push({
|
||||
path: '/listOfTopics',
|
||||
query: {
|
||||
sectionid: item.sectionID,
|
||||
name: item.sectionName,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const renderAdvList = (index: number) => {
|
||||
const list = getAdItem(3)
|
||||
for (let j = 0; j < 10; j++) {
|
||||
list.push(...list)
|
||||
}
|
||||
const i = Math.floor(index / 2 )
|
||||
const startIndex = i * 2
|
||||
const endindex = (i + 1) * 2
|
||||
return list.slice(startIndex, endindex)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: 1516px;
|
||||
margin: auto;
|
||||
|
||||
.thematic {
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
color: #FFF;
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 28px;
|
||||
padding-top: 50px;
|
||||
box-sizing: content-box;
|
||||
@include flexBetween;
|
||||
|
||||
.more {
|
||||
@include flexBetween;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
.adv-list-wrap {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
.adv {
|
||||
margin-top: 30px;
|
||||
width: 746px;
|
||||
height: 146px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<!-- 轮播 -->
|
||||
<banner :swiperList="swiperList" :bannerList="bannerList" />
|
||||
<content :videoList="videoList" v-if="videoList.length > 0" />
|
||||
</template>
|
||||
<script setup lang="ts" name="home">
|
||||
import { ref, watchEffect } from 'vue'
|
||||
import { useHomeStore } from '@/stores/home'
|
||||
import banner from './banner.vue';
|
||||
import content from './content.vue';
|
||||
import HomeApi from '@/service/home';
|
||||
const homeStore = useHomeStore()
|
||||
const swiperList = ref([])
|
||||
const videoList = ref([])
|
||||
watchEffect(async() => {
|
||||
const homeData = homeStore.homeData.find(item => item.showType === 1) || {}
|
||||
if (homeData.id) {
|
||||
const res: any = await HomeApi.getModuleList({
|
||||
id: homeData.id,
|
||||
data: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
videoCount: 16,
|
||||
moduleSort: homeData.moduleSort
|
||||
}
|
||||
})
|
||||
getBannerList()
|
||||
const allSection = res.allSection
|
||||
swiperList.value = (allSection.find((item: any) => item.showType === 2) || {}).allVideoInfo || []
|
||||
videoList.value = allSection.filter((item: any) => item.showType !== 2) || []
|
||||
}
|
||||
})
|
||||
const bannerList = ref([])
|
||||
const getBannerList = async () => {
|
||||
const res: any = await HomeApi.getRangking({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
bannerList.value = res.videos
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<div id="home">
|
||||
<div class="header-wrap-box">
|
||||
<div class="adv-wrap" v-if="showHeaderAdv && getAdItem(1).length > 0">
|
||||
<swiper loop :autoplay="{ delay: 5000, disableOnInteraction: false, pauseOnMouseEnter: true, }" :modules="[Autoplay]">
|
||||
<swiper-slide class="adv" v-for="(item, index) in getAdItem(1)" :key="index" @click="jumpAdv(item)">
|
||||
<DecryptImg class="banner-img" :src="item.cover" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<img @click="closeHeaderAdv" class="close-adv" src="@/assets/images/pc/adv_close.png" />
|
||||
</div>
|
||||
<div class="header-wrap">
|
||||
<div class="flex-vcenter left-wrap">
|
||||
<img @click="jumpPage('/home')" class="logo" src="@/assets/images/pc/logo.png" />
|
||||
<div class="tab-list">
|
||||
<span
|
||||
class="item"
|
||||
:class="{ active: activeIndex === index }"
|
||||
@click="changeTab(item, index)"
|
||||
v-for="(item, index) in homeStore.homeData"
|
||||
:key="index">
|
||||
{{ item.subModuleName }}
|
||||
<span v-show="activeIndex === index">✨</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-list">
|
||||
<div class="search-box">
|
||||
<input type="text" v-model="searchText" @keydown.enter="tosearch" placeholder="输入你搜索的关键字">
|
||||
<div class="search-btn" @click="tosearch">
|
||||
<img class="search" src="@/assets/images/pc/search.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="item history-box">
|
||||
<div @click="toHistory" @mouseover="getHistoryList">
|
||||
<img class="search" src="@/assets/images/pc/history.png" />
|
||||
<p>观看历史</p>
|
||||
</div>
|
||||
<div class="history-list-box-wrap" v-if="historyList.length > 0">
|
||||
<div class="history-list-box">
|
||||
<img class="top-tip" src="@/assets/images/pc/history_bg.png" />
|
||||
<div class="movie-list" @click="toMoivePlay(item.id)" v-for="(item, index) in historyList" :key="index">
|
||||
<DecryptImg class="cover" :src="item.cover" :key="item.cover + Math.random()" />
|
||||
<div class="described ellipsis2">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="more-btn" @click="toHistory">更多 <img src="@/assets/images/pc/left_arrow.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" @click="jumpPage('/business')">
|
||||
<img class="search" src="@/assets/images/pc/business.png" />
|
||||
<p>商务合作</p>
|
||||
</div>
|
||||
<DecryptImg @click="userHandle" class="avatar" v-if="userInfo?.portrait" :src="userInfo.portrait" :key="userInfo?.portrait" />
|
||||
<img v-else @click="userHandle" class="avatar" src="@/assets/images/pc/avatar.png" />
|
||||
<div class="official" @click="jumpPage('/contact')">联系官方</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
<pc-footer></pc-footer>
|
||||
<!-- <Mask @closeMaskPop="setAdvPopup"/> -->
|
||||
<!-- 弹窗广告 -->
|
||||
<!-- v-if="isShowAdvPopup" -->
|
||||
<HomePopupAdv v-if="isShowAdvPopup" :advList="getAdItem(9).reverse()" @close="closeHomePopupAdv" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watchEffect, computed, watch, onMounted } from 'vue'
|
||||
import { jumpAdv } from '@/utils/index';
|
||||
import { getAdItem } from '@/utils/getConfig'
|
||||
import { Autoplay } from 'swiper'
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
import 'swiper/swiper.min.css';
|
||||
import { useHomeStore } from '@/stores/home'
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import { queryHistoryVideo } from '@/utils';
|
||||
import Mask from './mask.vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { eventBus } from "@/utils/eventBus";
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const homeStore = useHomeStore()
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo') || '{}'
|
||||
if (data) {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
return data
|
||||
})
|
||||
const searchText = ref('')
|
||||
const activeIndex = ref(0)
|
||||
|
||||
// const isShowAdvPopup = ref(false)
|
||||
|
||||
// const setAdvPopup = () => {
|
||||
// isShowAdvPopup.value = !globalStore.homeAdvFirst && getAdItem(9)?.length > 0 && !!sessionStorage.getItem('maskPop');
|
||||
// }
|
||||
// 首页弹窗广告
|
||||
const isShowAdvPopup = computed(() => {
|
||||
return getAdItem(9)?.length > 0;
|
||||
});
|
||||
const closeHomePopupAdv = () => {
|
||||
globalStore.setHomeAdvFirst();
|
||||
};
|
||||
watchEffect(() => {
|
||||
if (['/home','/welfare', '/classification', '/pageList'].includes(route.path)) {
|
||||
activeIndex.value = Number(route.query.index || 0)
|
||||
} else {
|
||||
activeIndex.value = -1
|
||||
}
|
||||
})
|
||||
watch(() => route.path, (path) => {
|
||||
if (path !== '/search') {
|
||||
searchText.value = ''
|
||||
}
|
||||
})
|
||||
const changeTab = (item: any, index: number) => {
|
||||
activeIndex.value = index
|
||||
if (item.showType === 2) {
|
||||
router.push({
|
||||
path: '/pageList',
|
||||
query: {
|
||||
index,
|
||||
id: item.id,
|
||||
name: item.subModuleName,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
if (item.showType === 1) {
|
||||
router.push({
|
||||
path: '/home',
|
||||
query: {
|
||||
index,
|
||||
id: item.id,
|
||||
}
|
||||
})
|
||||
}
|
||||
if (item.showType === 4) {
|
||||
router.push('/welfare?index=' + index)
|
||||
}
|
||||
if (item.showType === 5) {
|
||||
router.push('/classification?index=' + index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const tosearch = () => {
|
||||
router.push({
|
||||
path: '/search',
|
||||
query: {
|
||||
id: searchText.value,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
const jumpPage = (path: string) => {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
const showHeaderAdv = ref(true)
|
||||
showHeaderAdv.value = !sessionStorage.getItem('HeaderAdv')
|
||||
const closeHeaderAdv = () => {
|
||||
sessionStorage.setItem('HeaderAdv', 'true')
|
||||
showHeaderAdv.value = false
|
||||
}
|
||||
|
||||
const userHandle = () => {
|
||||
if (userInfo.value.account) {
|
||||
router.push('/mine')
|
||||
} else {
|
||||
eventBus.emit('SHOW_LOGIN')
|
||||
}
|
||||
}
|
||||
|
||||
const historyList = ref([])
|
||||
const getHistoryList = async () => {
|
||||
const list: any = await queryHistoryVideo(1, 3, HistoryVideoType.VIDEO)
|
||||
historyList.value = list
|
||||
};
|
||||
const toMoivePlay = (id: string) => {
|
||||
router.push({
|
||||
path: '/moviePlay',
|
||||
query: {
|
||||
vid: id,
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
getHistoryList()
|
||||
|
||||
const toHistory = () => {
|
||||
if (userInfo.value.account) {
|
||||
router.push('/mine/history')
|
||||
} else {
|
||||
eventBus.emit('SHOW_LOGIN')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
#home {
|
||||
min-width: 1280px;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
background-color: #000;
|
||||
}
|
||||
.header-wrap-box {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
.adv-wrap {
|
||||
border: 1px solid #34363B;
|
||||
background: #1A1C22;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
.adv {
|
||||
height: 100%;
|
||||
width: max-content;
|
||||
}
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
.close-adv {
|
||||
width: 50px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 200px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header-wrap {
|
||||
width: 100%;
|
||||
height: 72px;
|
||||
background: #1A1C22;
|
||||
@include flexCenter;
|
||||
.left-wrap {
|
||||
min-width: 900px;
|
||||
margin-right: 3vw;
|
||||
}
|
||||
.logo {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.logo:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
.tab-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #A3A4A7;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
|
||||
.item {
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #FFF;
|
||||
font-size: 18px;
|
||||
}
|
||||
.item:hover {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 240px;
|
||||
height: 36px;
|
||||
padding: 10px 0 10px 12px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.20);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
all: unset;
|
||||
color: #fff;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.item {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
padding-left: 30px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
.item:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin: 30px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.avatar:hover {
|
||||
transform: scale(1.1);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.official {
|
||||
width: 87px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #fff;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
}
|
||||
.official:hover {
|
||||
color: #FFC303;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.history-box {
|
||||
position: relative;
|
||||
.history-list-box-wrap {
|
||||
width: 282px;
|
||||
min-height: 100px;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
transform: translateX(-50%);
|
||||
left: 50%;
|
||||
display: none;
|
||||
}
|
||||
.history-list-box {
|
||||
width: 282px;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 60px;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
background: #1A1C22;
|
||||
.top-tip {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
.movie-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.cover {
|
||||
width: 110px;
|
||||
height: 62px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.described {
|
||||
width: 130px;
|
||||
height: 44px;
|
||||
text-align: left;
|
||||
color: #A9A9AC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
border-bottom: 2px solid rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.more-btn {
|
||||
@include flexCenter;
|
||||
height: 44px;
|
||||
color: #A9A9AC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.history-box:hover {
|
||||
.history-list-box-wrap {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
:deep() {
|
||||
.imgBox {
|
||||
width: 474px !important;
|
||||
height: 664px !important;
|
||||
.closeIcon {
|
||||
top: 23px !important;
|
||||
right: 29px !important;
|
||||
width: 52px !important;
|
||||
height: 52px !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="left-wrap">
|
||||
<div class="thematic">
|
||||
<div class="title">
|
||||
{{ route.query.name }}
|
||||
<div class="more">
|
||||
<div class="all">{{ sortText }} <img class="arrow" src="@/assets/images/pc/down_arrow.png" alt=""></div>
|
||||
<div class="meun-wrap">
|
||||
<div class="meun-list">
|
||||
<div @click="changeSort(item, index)" :class="{ active: activeIndex === index }"
|
||||
v-for="(item, index) in sortList" :key="index">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-list">
|
||||
<movieCard :itemData="item" v-for="(item, index) in videoList" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-wrap" v-if="total > 0">
|
||||
<Pagination :total="total" :pageSize="pageSize" @changePage="changePage" />
|
||||
</div>
|
||||
</div>
|
||||
<tag-list />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import insertAdv from '@/utils/insertAdv'
|
||||
import { onMounted, ref, watch, watchEffect } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import HomeApi from '@/service/home';
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const activeIndex = ref(0)
|
||||
const sortText = ref('全部視頻')
|
||||
const sortList = ref([
|
||||
{ name: '最新上架', value: 'new'},
|
||||
{ name: '最多观看', value: 'watch'},
|
||||
{ name: '最多收藏', value: 'like'},
|
||||
])
|
||||
const videoList = ref([])
|
||||
let pageNumber = 1
|
||||
const pageSize = 21
|
||||
const total = ref(0)
|
||||
const changeSort = (item: any, index: number) => {
|
||||
activeIndex.value = index
|
||||
sortText.value = item.name
|
||||
getDataList(item.value)
|
||||
}
|
||||
watchEffect(() => {
|
||||
pageNumber = Number(route.query.page || 1)
|
||||
})
|
||||
|
||||
const getDataList = async (sortType: string) => {
|
||||
try {
|
||||
const sectionid = route.query.sectionid as string
|
||||
const res: any = await HomeApi.getVidSectionList(sectionid, {
|
||||
pageNumber,
|
||||
pageSize,
|
||||
sortType,
|
||||
playTimeType: 1
|
||||
})
|
||||
const updatedArray = insertAdv({
|
||||
insertNum: 6,
|
||||
pageSize,
|
||||
pageNumber: pageNumber,
|
||||
originArray: res.videos,
|
||||
advNum: 5
|
||||
})
|
||||
videoList.value = updatedArray
|
||||
total.value = res.total
|
||||
} catch (error) {
|
||||
videoList.value = []
|
||||
total.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
const changePage = (page: number) => {
|
||||
pageNumber = page
|
||||
router.push({
|
||||
pāth: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
page
|
||||
}
|
||||
})
|
||||
getDataList('new')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList('new')
|
||||
})
|
||||
|
||||
watch(() => route.query.sectionid, (sectionid) => {
|
||||
if (sectionid) {
|
||||
getDataList('new')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: 1560px;
|
||||
margin: auto;
|
||||
padding-top: 65px;
|
||||
display: flex;
|
||||
.left-wrap {
|
||||
width: 1128px;
|
||||
margin: 0 42px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.thematic {
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
color: #FFF;
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 28px;
|
||||
box-sizing: content-box;
|
||||
@include flexBetween;
|
||||
|
||||
.more {
|
||||
@include flexBetween;
|
||||
display: inline-flex;
|
||||
height: 48px;
|
||||
padding: 0px 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
.all {
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
|
||||
img {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.meun-wrap {
|
||||
width: 100%;
|
||||
height: 143px;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.2s;
|
||||
}
|
||||
|
||||
.meun-list {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 105px;
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.more:hover {
|
||||
.meun-wrap {
|
||||
height: 143px;
|
||||
transition: height 0.1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
padding-top: 63px;
|
||||
@include flexCenter;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<div class="login-page" v-if="modelValue">
|
||||
<!-- 获取验证码 -->
|
||||
<div class="form-content" v-if="state.showSendCode">
|
||||
<img class="close" @click="close" src="@/assets/images/pc/close.png" />
|
||||
<div class="title">找回密码</div>
|
||||
<!-- 手机号 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="请输入绑定的手机号" v-model="state.formData.mobile" @blur="verificationMobile">
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.mobileVerific }">手机号格式有误</div>
|
||||
</div>
|
||||
<!-- 短信验证码 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="请输入短信验证码" v-model="state.formData.code" @blur="verificationCode">
|
||||
<div class="codetimer" v-if="state.timer > 0">{{ state.timer }}S</div>
|
||||
<div class="getcodebtn" @click="countdown" v-else>获得验证码</div>
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.codeVerific }">验证码不正确</div>
|
||||
</div>
|
||||
<div class="submit" @click="sumbit">提交</div>
|
||||
<div class="box1">
|
||||
<div class="registered" @click="showLogin">回到登录</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设置新密码 -->
|
||||
<div class="form-content" v-else>
|
||||
<img class="close" @click="close" src="@/assets/images/pc/close.png" />
|
||||
<div class="title">设置新密码</div>
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="请输入新密码" v-model="state.formData.password" @blur="verificationPassword">
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.passwordVerific }">密码格式有误</div>
|
||||
</div>
|
||||
<div class="submit" @click="setNewPassword">提交</div>
|
||||
<div class="box1">
|
||||
<div class="set-mobile" @click="state.showSendCode = true">重新输入手机号</div>
|
||||
<div class="registered" @click="showLogin">回到登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import UserApi from '@/service/user';
|
||||
import { showToast } from 'vant';
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: boolean
|
||||
}>(),
|
||||
{
|
||||
modelValue: false
|
||||
}
|
||||
)
|
||||
const state = reactive({
|
||||
formData: {
|
||||
mobile: '',
|
||||
code: '',
|
||||
password: ''
|
||||
},
|
||||
mobileVerific: false,
|
||||
codeVerific: false,
|
||||
passwordVerific: false,
|
||||
timer: 0,
|
||||
showSendCode: true
|
||||
})
|
||||
|
||||
|
||||
// 手机号
|
||||
const verificationMobile = () => {
|
||||
state.mobileVerific = !validatePhoneNumber(state.formData.mobile)
|
||||
}
|
||||
// 验证码
|
||||
const verificationCode = () => {
|
||||
state.codeVerific = !state.formData.code
|
||||
}
|
||||
// 密码
|
||||
const verificationPassword = () => {
|
||||
state.passwordVerific = !validatePassword(state.formData.password)
|
||||
}
|
||||
|
||||
// 密码校验正则
|
||||
function validatePassword(password: string) {
|
||||
const regex = /^(?=.*[A-Za-z])(?=.*[\d@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,20}$/;
|
||||
return regex.test(password);
|
||||
}
|
||||
// 手机号验证
|
||||
function validatePhoneNumber(phone: string) {
|
||||
const regex = /^1[3-9]\d{9}$/; // 正则表达式匹配中国大陆手机号
|
||||
return regex.test(phone);
|
||||
}
|
||||
// 提交
|
||||
const sumbit = async () => {
|
||||
if (!validatePhoneNumber(state.formData.mobile)) {
|
||||
state.mobileVerific = true
|
||||
return
|
||||
}
|
||||
if (!state.formData.code) {
|
||||
state.codeVerific = true
|
||||
return
|
||||
}
|
||||
try {
|
||||
await UserApi.mobileverify({
|
||||
mobile: '+86' + state.formData.mobile,
|
||||
code: state.formData.code,
|
||||
});
|
||||
state.showSendCode = false
|
||||
showToast('验证通过')
|
||||
} catch (error) {
|
||||
// state.showSendCode = false
|
||||
// state.codeVerific = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码倒计时
|
||||
*/
|
||||
const countdown = () => {
|
||||
if (!validatePhoneNumber(state.formData.mobile)) {
|
||||
state.mobileVerific = true
|
||||
return;
|
||||
}
|
||||
sendCode();
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*/
|
||||
const sendCode = async () => {
|
||||
try {
|
||||
await UserApi.getCaptcha({
|
||||
type: 2,
|
||||
mobile: '+86' + state.formData.mobile,
|
||||
});
|
||||
state.timer = 60;
|
||||
let countdownTime = setInterval(() => {
|
||||
state.timer--;
|
||||
if (state.timer <= 0) {
|
||||
clearInterval(countdownTime);
|
||||
}
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//设置新密码
|
||||
const setNewPassword = async () => {
|
||||
if(!validatePassword(state.formData.password)) {
|
||||
state.passwordVerific = true
|
||||
return
|
||||
}
|
||||
try {
|
||||
await UserApi.resetpassword({
|
||||
mobile: '+86' + state.formData.mobile,
|
||||
passWord: state.formData.password
|
||||
})
|
||||
showToast('设置成功');
|
||||
showLogin()
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue', 'login'])
|
||||
const close = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
const showLogin = () => {
|
||||
close()
|
||||
emit('login')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.login-page {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000 35%);
|
||||
z-index: 1111;
|
||||
}
|
||||
.form-content{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
.title {
|
||||
color: #222;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.form-item {
|
||||
width: 100%;
|
||||
margin: 24px 0;
|
||||
.input-box {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFCD3F;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
.codetimer {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
.getcodebtn {
|
||||
font-size: 14px;
|
||||
width: 100px;
|
||||
color: #FFCD3F;
|
||||
cursor: pointer;
|
||||
}
|
||||
.getcodebtn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.eyes {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
input {
|
||||
all: unset;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
input::placeholder {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.verification {
|
||||
margin-top: 8px;
|
||||
color: #FF5356;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
opacity: 0;
|
||||
}
|
||||
.show {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #502D1E;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
}
|
||||
.forgot-password {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFC303;
|
||||
@include flexCenter;
|
||||
color: #FFC303;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-top: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.box1 {
|
||||
@include flexCenter;
|
||||
color: #FFC303;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-top: 24px;
|
||||
gap: 20px;
|
||||
}
|
||||
.registered {
|
||||
text-align: center;
|
||||
}
|
||||
.registered:hover, .set-mobile:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div class="login-page" v-if="modelValue">
|
||||
<div class="form-content">
|
||||
<img class="close" @click="close" src="@/assets/images/pc/close.png" />
|
||||
<div class="title">登陆</div>
|
||||
<!-- 账号 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="请输入账号" v-model="state.formData.account" @blur="verificationaccount">
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.accountVerific }">账号格式有误</div>
|
||||
</div>
|
||||
<!-- 密码 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input :type="state.openEyes ? 'text' : 'password'" placeholder="密码" v-model="state.formData.password" @blur="verificationPassword">
|
||||
<!-- 设置password -->
|
||||
<img @click="state.openEyes = !state.openEyes" class="eyes" v-if="state.openEyes" src="@/assets/images/pc/eyes.png" />
|
||||
<img @click="state.openEyes = !state.openEyes" class="eyes" v-else src="@/assets/images/pc/eyes1.png" />
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.passwordVerific }">密码格式有误</div>
|
||||
</div>
|
||||
<div class="submit" @click="sumbit">登录</div>
|
||||
<div class="forgot-password" @click="showForgotPassword">忘记密码</div>
|
||||
<div class="registered">没有账号? <span @click="showRegist"> 注册</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { showToast } from 'vant';
|
||||
import UserApi from '@/service/user';
|
||||
import { reactive } from 'vue';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { clearnIndexDb } from '@/utils/indexDB';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: boolean
|
||||
}>(),
|
||||
{
|
||||
modelValue: false
|
||||
}
|
||||
)
|
||||
const state = reactive({
|
||||
formData: {
|
||||
account: '',
|
||||
password: ''
|
||||
},
|
||||
accountVerific: false,
|
||||
passwordVerific: false,
|
||||
openEyes: false
|
||||
})
|
||||
|
||||
// 账号
|
||||
const verificationaccount = () => {
|
||||
state.accountVerific = !validateString(state.formData.account)
|
||||
}
|
||||
// 密码
|
||||
const verificationPassword = () => {
|
||||
state.passwordVerific = !validatePassword(state.formData.password)
|
||||
}
|
||||
|
||||
// 账号验证
|
||||
function validateString(account: string) {
|
||||
const regex = /^[a-zA-Z0-9]{2,16}$/; // 验证2到16位的字母和数字组合
|
||||
return regex.test(account)
|
||||
}
|
||||
|
||||
// 密码校验正则
|
||||
function validatePassword(password: string) {
|
||||
const regex = /^(?=.*[A-Za-z])(?=.*[\d@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,20}$/;
|
||||
return regex.test(password);
|
||||
}
|
||||
const sumbit = async () => {
|
||||
if (!validateString(state.formData.account)) {
|
||||
state.accountVerific = true
|
||||
return;
|
||||
}
|
||||
if (!validatePassword(state.formData.password)) {
|
||||
state.passwordVerific = true
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let req: any = {
|
||||
account: state.formData.account,
|
||||
passWord: state.formData.password,
|
||||
};
|
||||
let res: any = await UserApi.userLogin(req);
|
||||
userStore.saveUserInfo(res);
|
||||
userStore.saveToken(res.token);
|
||||
clearnIndexDb()
|
||||
showToast('登录成功');
|
||||
close()
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 100)
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'regist', 'forgotPasswordFn'])
|
||||
const close = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
const showRegist = () => {
|
||||
close()
|
||||
emit('regist')
|
||||
}
|
||||
|
||||
const showForgotPassword = () => {
|
||||
close()
|
||||
emit('forgotPasswordFn')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.login-page {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000 35%);
|
||||
z-index: 1111;
|
||||
}
|
||||
.form-content{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
.title {
|
||||
color: #222;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.form-item {
|
||||
width: 100%;
|
||||
margin: 24px 0;
|
||||
.input-box {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFCD3F;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.eyes {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
input {
|
||||
all: unset;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
input::placeholder {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.verification {
|
||||
margin-top: 8px;
|
||||
color: #FF5356;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
opacity: 0;
|
||||
}
|
||||
.show {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #502D1E;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
}
|
||||
.forgot-password {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFC303;
|
||||
@include flexCenter;
|
||||
color: #FFC303;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-top: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.registered {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
margin-top: 24px;
|
||||
span {
|
||||
color: #FFCD3F;
|
||||
}
|
||||
span:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<div class="login-page" v-if="modelValue">
|
||||
<div class="form-content">
|
||||
<img class="close" @click="close" src="@/assets/images/pc/close.png" />
|
||||
<div class="title">注册</div>
|
||||
<!-- 账号 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="账号" v-model="state.formData.account" @blur="verificationAccount">
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.accountVerific }">账号格式有误</div>
|
||||
</div>
|
||||
<!-- 手机号 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="手机号" v-model="state.formData.mobile" @blur="verificationMobile">
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.mobileVerific }">手机号格式有误</div>
|
||||
</div>
|
||||
<!-- 密码 -->
|
||||
<div class="form-item">
|
||||
<div class="input-box">
|
||||
<input :type="state.openEyes ? 'text' : 'password'" placeholder="密码" v-model="state.formData.password" @blur="verificationPassword">
|
||||
<!-- 设置password -->
|
||||
<img @click="state.openEyes = !state.openEyes" class="eyes" v-if="state.openEyes" src="@/assets/images/pc/eyes.png" />
|
||||
<img @click="state.openEyes = !state.openEyes" class="eyes" v-else src="@/assets/images/pc/eyes1.png" />
|
||||
</div>
|
||||
<div class="verification" :class="{ show: state.passwordVerific }">密码格式有误</div>
|
||||
</div>
|
||||
<div class="submit" @click="sumbit">注册</div>
|
||||
<div class="registered">已有账号? <span @click="showLogin">登陆</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { showToast } from 'vant';
|
||||
import UserApi from '@/service/user';
|
||||
import { reactive } from 'vue';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { clearnIndexDb } from '@/utils/indexDB';
|
||||
const userStore = useUserStore();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: boolean
|
||||
}>(),
|
||||
{
|
||||
modelValue: false
|
||||
}
|
||||
|
||||
)
|
||||
const state = reactive({
|
||||
formData: {
|
||||
account: '',
|
||||
mobile: '',
|
||||
password: ''
|
||||
},
|
||||
accountVerific: false,
|
||||
mobileVerific: false,
|
||||
passwordVerific: false,
|
||||
openEyes: false
|
||||
})
|
||||
|
||||
// 账号
|
||||
const verificationAccount = () => {
|
||||
state.accountVerific = !validateString(state.formData.account)
|
||||
}
|
||||
// 手机号
|
||||
const verificationMobile = () => {
|
||||
state.mobileVerific = !validatePhoneNumber(state.formData.mobile)
|
||||
}
|
||||
// 密码
|
||||
const verificationPassword = () => {
|
||||
state.passwordVerific = !validatePassword(state.formData.password)
|
||||
}
|
||||
|
||||
// 账号验证
|
||||
function validateString(account: string) {
|
||||
const regex = /^[a-zA-Z0-9]{2,16}$/; // 验证2到16位的字母和数字组合
|
||||
return regex.test(account)
|
||||
}
|
||||
// 密码校验正则
|
||||
function validatePassword(password: string) {
|
||||
const regex = /^(?=.*[A-Za-z])(?=.*[\d@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,20}$/;
|
||||
return regex.test(password);
|
||||
}
|
||||
// 手机号验证
|
||||
function validatePhoneNumber(phone: string) {
|
||||
const regex = /^1[3-9]\d{9}$/; // 正则表达式匹配中国大陆手机号
|
||||
return regex.test(phone);
|
||||
}
|
||||
|
||||
const sumbit = async () => {
|
||||
if (!validateString(state.formData.account)) {
|
||||
state.accountVerific = true
|
||||
return;
|
||||
}
|
||||
if (!validatePhoneNumber(state.formData.mobile)) {
|
||||
state.mobileVerific = true
|
||||
return;
|
||||
}
|
||||
if (!validatePassword(state.formData.password)) {
|
||||
state.passwordVerific = true
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let req: any = {
|
||||
account: state.formData.account,
|
||||
mobile: '+86' + state.formData.mobile,
|
||||
passWord: state.formData.password,
|
||||
};
|
||||
let res: any = await UserApi.register(req);
|
||||
userStore.saveUserInfo(res);
|
||||
userStore.saveToken(res.token);
|
||||
clearnIndexDb()
|
||||
showToast('注册成功');
|
||||
close()
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 100)
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue', 'login'])
|
||||
const close = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
const showLogin = () => {
|
||||
close()
|
||||
emit('login')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.login-page {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000 35%);
|
||||
z-index: 1111;
|
||||
}
|
||||
.form-content{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px;
|
||||
height: 550px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
.title {
|
||||
color: #222;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.form-item {
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
.input-box {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFCD3F;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
.eyes {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
input {
|
||||
all: unset;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
input::placeholder {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.verification {
|
||||
margin-top: 8px;
|
||||
color: #FF5356;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
opacity: 0;
|
||||
}
|
||||
.show {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #502D1E;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
}
|
||||
.forgot-password {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFC303;
|
||||
@include flexCenter;
|
||||
color: #FFC303;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-top: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.registered {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
margin-top: 24px;
|
||||
span {
|
||||
color: #FFCD3F;
|
||||
}
|
||||
span:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="mask-pop" v-if="showMask">
|
||||
<div class="mask"></div>
|
||||
<div class="content">
|
||||
<img class="logo" src="@/assets/images/pc/logo1.png" />
|
||||
<div class="title">欢迎</div>
|
||||
<div class="line"></div>
|
||||
<div class="text">🔞 您欲觀看的頁面包含成人內容 🔞</div>
|
||||
<div class="text">本站內容均遵守本國法律規定若您是未成年者,請選擇離開。 滿 18 歲。</div>
|
||||
<div class="text">請点击确认进入。</div>
|
||||
<div class="btn" @click="close">确认进入</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const showMask = ref(true)
|
||||
const emit = defineEmits(['closeMaskPop'])
|
||||
showMask.value = !sessionStorage.getItem('maskPop')
|
||||
const close = () => {
|
||||
showMask.value = false
|
||||
sessionStorage.setItem('maskPop', 'true');
|
||||
emit('closeMaskPop')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.mask-pop {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
.mask {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000 47.31%);
|
||||
backdrop-filter: blur(250px);
|
||||
}
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
.logo {
|
||||
width: 237.057px;
|
||||
height: 153.492px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
margin-top: 54px;
|
||||
}
|
||||
.line {
|
||||
width: 213px;
|
||||
height: 1px;
|
||||
opacity: 0.1;
|
||||
background: #FFF;
|
||||
margin: 10px auto;
|
||||
}
|
||||
.text {
|
||||
color: #FFF;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
}
|
||||
.btn {
|
||||
width: 118px;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
border-radius: 10px;
|
||||
background: #FFC400;
|
||||
box-shadow: 0px -3px 8px 0px rgba(249, 255, 139, 0.60) inset, 0px 0px 8px 0px rgba(255, 196, 0, 0.50);
|
||||
margin: 22px auto 0;
|
||||
color: #482F28;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,154 @@
|
||||
|
||||
<template>
|
||||
<div class="history">
|
||||
<h1 class="title">
|
||||
<span>观看历史</span>
|
||||
<span class="edit-btn" v-if="!state.isEdit" @click="state.isEdit = true">编辑</span>
|
||||
<div class="list-btn" v-else>
|
||||
<span class="btn1" @click="state.isEdit = false">取消</span>
|
||||
<span class="edit-btn btn2" @click="deleteHistory">删除</span>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<div class="movie-list">
|
||||
<div class="all-select" v-if="state.isEdit" @click="allSelectFn">
|
||||
<img v-if="!state.allSelect" src="@/assets/images/pc/select1.png" alt="">
|
||||
<img v-else src="@/assets/images/pc/select2.png" alt="">
|
||||
全选
|
||||
</div>
|
||||
<div class="item-wrap" v-for="(item, index) in historyList" :key="index">
|
||||
<movieCard :itemData="item" />
|
||||
<div class="mask" v-if="state.isEdit" @click="item.check = !item.check">
|
||||
<img v-if="!item.check" src="@/assets/images/pc/select1.png" alt="">
|
||||
<img v-else src="@/assets/images/pc/select2.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="mine-history">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import { queryHistoryVideo, deletePost } from '@/utils';
|
||||
|
||||
const historyList = ref([])
|
||||
const getHistoryList = async () => {
|
||||
const list: any = await queryHistoryVideo(1, 999, HistoryVideoType.VIDEO)
|
||||
list.forEach((item: any) => {
|
||||
item.check = false
|
||||
})
|
||||
historyList.value = list
|
||||
};
|
||||
getHistoryList()
|
||||
|
||||
const state = reactive({
|
||||
isEdit: false,
|
||||
select: false,
|
||||
allSelect: false
|
||||
})
|
||||
|
||||
const deleteHistory = () => {
|
||||
// 选中删除
|
||||
const filterDeleteData = historyList.value.filter(item => item.check)
|
||||
filterDeleteData.forEach((item => {
|
||||
deletePost(item.id, HistoryVideoType.VIDEO)
|
||||
}))
|
||||
setTimeout(() => {
|
||||
getHistoryList()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const allSelectFn = () => {
|
||||
state.allSelect = !state.allSelect
|
||||
historyList.value.forEach(item => {
|
||||
item.check = state.allSelect
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.history {
|
||||
margin: 70px 0 0 250px;
|
||||
width: 1120px;
|
||||
.title {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
line-height: 61px;
|
||||
border-bottom: 1px solid #fff2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.edit-btn {
|
||||
@include flexCenter;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #FFF;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
.list-btn {
|
||||
@include flexCenter;
|
||||
gap: 16px;
|
||||
.btn1 {
|
||||
@include flexCenter;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFC303;
|
||||
color: #FFC303;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.movie-list {
|
||||
padding-top: 50px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
.all-select {
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.item-wrap {
|
||||
width: 360px;
|
||||
height: 320px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(26, 28, 34, 0.5);
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 11;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,170 @@
|
||||
|
||||
<template>
|
||||
<div class="likes">
|
||||
<h1 class="title">
|
||||
<span>我的收藏</span>
|
||||
<span class="edit-btn" v-if="!state.isEdit" @click="state.isEdit = true">编辑</span>
|
||||
<div class="list-btn" v-else>
|
||||
<span class="btn1" @click="state.isEdit = false">取消</span>
|
||||
<span class="edit-btn btn2" @click="cancelCollection">取消收藏</span>
|
||||
</div>
|
||||
</h1>
|
||||
<div class="movie-list">
|
||||
<div class="all-select" v-if="state.isEdit" @click="allSelectFn">
|
||||
<img v-if="!state.allSelect" src="@/assets/images/pc/select1.png" alt="">
|
||||
<img v-else src="@/assets/images/pc/select2.png" alt="">
|
||||
全选
|
||||
</div>
|
||||
<div class="item-wrap" v-for="(item, index) in likesList" :key="index">
|
||||
<movieCard :itemData="item" />
|
||||
<div class="mask" v-if="state.isEdit" @click="item.check = !item.check">
|
||||
<img v-if="!item.check" src="@/assets/images/pc/select1.png" alt="">
|
||||
<img v-else src="@/assets/images/pc/select2.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="mine-likes">
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
import MineApi from '@/service/mine'
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo')
|
||||
if (data) {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
return data
|
||||
})
|
||||
const likesList = ref([])
|
||||
const geLikeList = async () => {
|
||||
const res: any = await MineApi.queryCollectionList({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
type: 'video',
|
||||
uid: userInfo.value.uid
|
||||
})
|
||||
const list = res.list
|
||||
list.forEach((item: any) => {
|
||||
item.check = false
|
||||
})
|
||||
likesList.value = list
|
||||
};
|
||||
geLikeList()
|
||||
|
||||
const state = reactive({
|
||||
isEdit: false,
|
||||
select: false,
|
||||
allSelect: false
|
||||
})
|
||||
|
||||
const allSelectFn = () => {
|
||||
state.allSelect = !state.allSelect
|
||||
likesList.value.forEach(item => {
|
||||
item.check = state.allSelect
|
||||
})
|
||||
}
|
||||
|
||||
const cancelCollection = async () => {
|
||||
// 选中删除
|
||||
const filterDeleteData = likesList.value.filter(item => item.check)
|
||||
const objIds = filterDeleteData.map(item => item.id)
|
||||
|
||||
await MineApi.batchUnCollect({
|
||||
isCollect: true,
|
||||
objIds,
|
||||
type: "video"
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
geLikeList()
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.likes {
|
||||
margin: 70px 0 0 250px;
|
||||
width: 1120px;
|
||||
.title {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
line-height: 61px;
|
||||
border-bottom: 1px solid #fff2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.edit-btn {
|
||||
@include flexCenter;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #FFF;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
.list-btn {
|
||||
@include flexCenter;
|
||||
gap: 16px;
|
||||
.btn1 {
|
||||
@include flexCenter;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #FFC303;
|
||||
color: #FFC303;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.movie-list {
|
||||
padding-top: 50px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
.all-select {
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.item-wrap {
|
||||
width: 360px;
|
||||
height: 320px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(26, 28, 34, 0.5);
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 11;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
<template>
|
||||
<div class="mine-setting">
|
||||
<h1 class="mine-setting__title">编辑个人资料</h1>
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="昵称" v-model="state.name">
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<input type="text" placeholder="密码" v-model="state.password">
|
||||
</div>
|
||||
<div class="tip">
|
||||
<p>密码8-20位</p>
|
||||
<p>至少为字母、数字或字符中任意两种的组合</p>
|
||||
</div>
|
||||
<div class="save-btn-list">
|
||||
<div class="btn1" @click="confirmExchange">保存</div>
|
||||
<!-- <div class="btn2">取消</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="mine-setting">
|
||||
import { showToast } from 'vant';
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import UserApi from '@/service/user';
|
||||
const store = useUserStore()
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo')
|
||||
if (data) {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
return data
|
||||
})
|
||||
const state = reactive({
|
||||
name: userInfo.value.name,
|
||||
password: ''
|
||||
})
|
||||
const confirmExchange = async () => {
|
||||
if (!state.name) return showToast('请输入新昵称');
|
||||
if (!validatePassword(state.password)) {
|
||||
showToast('密码格式有误');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await UserApi.editPersonInfo({ name: state.name, passWord: state.password })
|
||||
showToast('设置成功');
|
||||
userInfo.value.name = state.name;
|
||||
store.saveUserInfo(userInfo.value);
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
|
||||
// 密码校验正则
|
||||
function validatePassword(password: string) {
|
||||
const regex = /^(?=.*[A-Za-z])(?=.*[\d@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,20}$/;
|
||||
return regex.test(password);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mine-setting {
|
||||
margin: 70px 0 0 250px;
|
||||
width: 1100px;
|
||||
&__title {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
line-height: 61px;
|
||||
border-bottom: 1px solid #fff2;
|
||||
}
|
||||
.input-box {
|
||||
width: 504px;
|
||||
margin-top: 30px;
|
||||
height: 62px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
input {
|
||||
all: unset;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
flex: 1;
|
||||
line-height: 62px;
|
||||
}
|
||||
input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.40);
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
padding-top: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #999;
|
||||
}
|
||||
.save-btn-list {
|
||||
display: flex;
|
||||
height: 42px;
|
||||
margin-top: 30px;
|
||||
.btn1 {
|
||||
width: 88px;
|
||||
height: 42px;
|
||||
@include flexCenter;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #FFC303 0%, #FFDD01 100%);
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn2 {
|
||||
width: 88px;
|
||||
height: 42px;
|
||||
@include flexCenter;
|
||||
border-radius: 4px;
|
||||
color: #FFF;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="slide-menu">
|
||||
<div class="slide-menu__top flex-center-center">
|
||||
<DecryptImg class="avatar" v-if="userInfo.portrait" :src="userInfo.portrait" :key="userInfo.portrait" />
|
||||
<img v-else class="avatar" src="@/assets/images/pc/avatar.png" />
|
||||
<span>{{ userInfo.name }}</span>
|
||||
</div>
|
||||
<div class="slide-menu__content">
|
||||
<div class="cell-item" :class="{ selected: menu.pathName === $route.name }" v-for="(menu, i) in menus" :key="i"
|
||||
@click="cellAction(menu)">
|
||||
<div class="cell flex-vcenter">
|
||||
<svg-icon :name="menu.icon"></svg-icon>
|
||||
<span>{{ menu.text }}</span>
|
||||
</div>
|
||||
<div v-if="menu.line" class="line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { clearnIndexDb } from '@/utils/indexDB';
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo')
|
||||
if (data) {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
return data
|
||||
})
|
||||
const router = useRouter()
|
||||
|
||||
const menus = [
|
||||
{ text: '观看历史', icon: 'mine_history', pathName: 'mine-history' },
|
||||
{ text: '编辑个人资料', icon: 'mine_set', line: true, pathName: 'mine-setting' },
|
||||
// { text: '我的账户', icon: 'mine_mine', pathName: 'mine-account' },
|
||||
{ text: '我的收藏', icon: 'mine_like', line: true, pathName: 'mine-likes' },
|
||||
{ text: '登出', icon: 'mine_logout', action: logout },
|
||||
]
|
||||
|
||||
function logout() {
|
||||
localStorage.clear()
|
||||
sessionStorage.clear()
|
||||
clearnIndexDb()
|
||||
window.location.replace('/')
|
||||
}
|
||||
|
||||
function cellAction(menu: { action?: () => void, pathName?: string }) {
|
||||
if (menu.action) {
|
||||
menu.action()
|
||||
} else {
|
||||
router.push({ name: menu.pathName })
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.slide-menu {
|
||||
min-height: 460px;
|
||||
|
||||
&__top {
|
||||
height: 96px;
|
||||
background: rgba(6,252,160) linear-gradient(270deg, rgba(255, 211, 12, 0.01) 0%, #FFD401 100%);
|
||||
color: 18px;
|
||||
color: black;
|
||||
|
||||
.avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 0 32px;
|
||||
|
||||
.cell-item {
|
||||
color: #ececec;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
color: #FFCD3F;
|
||||
}
|
||||
|
||||
.cell {
|
||||
height: 56px;
|
||||
|
||||
svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 13px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 8px 0;
|
||||
background-color: #fff2;
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="mine-page flex">
|
||||
<div class="left-menu">
|
||||
<SlideMenu></SlideMenu>
|
||||
</div>
|
||||
<div class="right-content">
|
||||
<RouterView></RouterView>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue';
|
||||
import SlideMenu from './components/SlideMenu.vue'
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.mine-page {
|
||||
.left-menu {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.right-content {
|
||||
flex: 1;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<div class="movie-play">
|
||||
<!-- <div class="banner-wrap">
|
||||
<div class="banner-list">
|
||||
<div class="item" v-for="(item, index) in getAdItem(7)" :key="index" @click="jumpAdv(item)">
|
||||
<span> {{ item.title }}</span>
|
||||
<span class="href"> {{ item.href }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="play-box">
|
||||
<div class="left-wrap">
|
||||
<div class="play" :key="videoData.id">
|
||||
<HorizontalVideo v-if="videoData.id" :videoInfo="videoData" ref="verticalVideoRef" />
|
||||
</div>
|
||||
<div class="like" @click="collect(videoData.vidStatus.hasCollected, videoData.id as string)">
|
||||
<img v-if="videoData?.vidStatus?.hasCollected" src="@/assets/images/pc/like.png" alt="" />
|
||||
<img v-else src="@/assets/images/pc/unlike.png" alt="" />
|
||||
{{ videoData.collectCount }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="recommend-wrap">
|
||||
<div class="title">相关推荐</div>
|
||||
<div class="recommend-list">
|
||||
<recommendCard :itemData="item" class="item" v-for="(item, index) in state.relationVideos" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="movie-name">{{ videoData.title }}</div>
|
||||
<div class="movie-tags">
|
||||
<span v-for="(tag, index) in videoData.tags" :key="index" @click="toTagPage(tag)">{{ tag.name }}</span>
|
||||
</div>
|
||||
<!-- <div class="top-box">
|
||||
<div class="top" v-if="route.query.topindex">top {{ route.query.topindex }}</div>
|
||||
<i class="line" v-if="route.query.topindex"></i>
|
||||
<span class="text">{{ watchCount(videoData.playCount) }}</span>
|
||||
<i class="line"></i>
|
||||
<span class="text">{{ fromNowTime(videoData.createdAt) }}</span>
|
||||
</div> -->
|
||||
<div class="adv-wrap" v-if="showAdv && getAdItem(7).length > 0">
|
||||
<div class="advItem" v-for="(item, index) in getAdItem(7).slice(0, 2)" :key="index" @click="jumpAdv(item)">
|
||||
<DecryptImg class="banner-img" :src="item.cover" />
|
||||
</div>
|
||||
<!-- <swiper loop :autoplay="{ delay: 5000, disableOnInteraction: false, pauseOnMouseEnter: true, }" :modules="[Autoplay]">
|
||||
<swiper-slide class="adv" v-for="(item, index) in getAdItem(7)" :key="index" @click="jumpAdv(item)">
|
||||
<DecryptImg class="banner-img" :src="item.cover" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<img @click="showAdv = false" class="close-adv" src="@/assets/images/pc/close2.png" /> -->
|
||||
</div>
|
||||
<div class="everyone-watch-wrap">
|
||||
<div class="title">大家正在看💦</div>
|
||||
<div class="movie-list">
|
||||
<movieCard :itemData="item" v-for="(item, index) in state.everyoneWatchData" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-center-center" v-if="state.total > 0">
|
||||
<Pagination :total="state.total" :pageSize="pageSize" @changePage="changePage" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { jumpAdv } from '@/utils/index';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import insertAdv from '@/utils/insertAdv';
|
||||
import { watch, onMounted, ref, reactive, computed } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import { fromNowTime, watchCount } from '@/utils/index';
|
||||
import recommendCard from './recommend-card.vue';
|
||||
import VideoApi from '@/service/video';
|
||||
import MineApi from '@/service/mine';
|
||||
import { eventBus } from '@/utils/eventBus';
|
||||
import { Autoplay } from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import 'swiper/swiper.min.css';
|
||||
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo');
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
let pageSize = 14;
|
||||
interface VideoData {
|
||||
id?: string;
|
||||
collectCount?: number;
|
||||
title?: string;
|
||||
tags?: Array<any>;
|
||||
playCount?: number;
|
||||
createdAt?: string;
|
||||
}
|
||||
const state = reactive({
|
||||
everyoneWatchData: [],
|
||||
relationVideos: [],
|
||||
total: 0,
|
||||
pageNumber: 1,
|
||||
});
|
||||
const videoData = ref<VideoData>({});
|
||||
// 获取视频详情
|
||||
const getDetails = async () => {
|
||||
try {
|
||||
const req = {
|
||||
videoID: route.query.vid,
|
||||
};
|
||||
const res: any = await VideoApi.queryVideoInfo(req);
|
||||
videoData.value = res;
|
||||
getEveryoneWatch();
|
||||
getRelation();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const getEveryoneWatch = async () => {
|
||||
try {
|
||||
const req = {
|
||||
id: route.query.vid,
|
||||
pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
};
|
||||
const res: any = await VideoApi.queryvidPlay(req);
|
||||
const updatedArray = insertAdv({
|
||||
insertNum: 6,
|
||||
pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
originArray: res.videos,
|
||||
advNum: 5,
|
||||
});
|
||||
state.everyoneWatchData = updatedArray;
|
||||
state.total = res.total;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const getRelation = async () => {
|
||||
try {
|
||||
const req = {
|
||||
vid: route.query.vid,
|
||||
pageSize: 15,
|
||||
pageNumber: 1,
|
||||
};
|
||||
const res: any = await VideoApi.queryvidRelation(req);
|
||||
state.relationVideos = res.videos;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const toTagPage = (tag: any) => {
|
||||
router.push({
|
||||
path: '/tagPageList',
|
||||
query: {
|
||||
id: tag.id,
|
||||
tagName: tag.name,
|
||||
page: 1,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const collect = async (isCollect: boolean, id: string) => {
|
||||
if (!userInfo.value.account) {
|
||||
console.log('登陆');
|
||||
eventBus.emit('SHOW_LOGIN');
|
||||
return;
|
||||
}
|
||||
await MineApi.mineCollect({
|
||||
isCollect: !isCollect,
|
||||
objID: id,
|
||||
type: 'video',
|
||||
});
|
||||
if (isCollect) {
|
||||
videoData.value.vidStatus.hasCollected = false;
|
||||
showToast('取消收藏成功');
|
||||
} else {
|
||||
videoData.value.vidStatus.hasCollected = true;
|
||||
showToast('收藏成功');
|
||||
}
|
||||
};
|
||||
const changePage = (page: number) => {
|
||||
state.pageNumber = page;
|
||||
router.push({
|
||||
pāth: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
page,
|
||||
},
|
||||
});
|
||||
getEveryoneWatch();
|
||||
};
|
||||
onMounted(() => {
|
||||
getDetails();
|
||||
});
|
||||
watch(
|
||||
() => route.query.vid,
|
||||
(value: any) => {
|
||||
if (value) {
|
||||
getDetails();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const showAdv = ref(true);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.movie-play {
|
||||
width: 1516px;
|
||||
margin: 0 auto;
|
||||
.banner-wrap {
|
||||
width: 100%;
|
||||
height: 72px;
|
||||
overflow: hidden;
|
||||
background: #23252a;
|
||||
color: #a9a9ac;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
.banner-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
height: 72px;
|
||||
width: max-content;
|
||||
animation: slide 15s linear infinite;
|
||||
}
|
||||
}
|
||||
.banner-wrap:hover {
|
||||
.banner-list {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
.play-box {
|
||||
width: 100%;
|
||||
height: 692px;
|
||||
background: #1a1c22;
|
||||
display: flex;
|
||||
margin-top: 74px;
|
||||
.left-wrap {
|
||||
flex: 1;
|
||||
.play {
|
||||
width: 100%;
|
||||
height: 648px;
|
||||
background: #333;
|
||||
}
|
||||
.like {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0px 20px 0px 15px;
|
||||
color: #a9a9ac;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.recommend-wrap {
|
||||
width: 366px;
|
||||
height: 100%;
|
||||
padding: 35px 20px;
|
||||
.title {
|
||||
color: #9b9b9b;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
.recommend-list {
|
||||
width: 100%;
|
||||
height: 590px;
|
||||
overflow: auto;
|
||||
.item {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.movie-name {
|
||||
color: #fff;
|
||||
font-size: 34px;
|
||||
font-weight: 600;
|
||||
line-height: 42px;
|
||||
padding-top: 36px;
|
||||
}
|
||||
.movie-tags {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding-top: 10px;
|
||||
span {
|
||||
padding: 4px 8px;
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #fff;
|
||||
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.top-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-top: 18px;
|
||||
.top {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #ffc303 0%, #ffdd01 100%);
|
||||
padding: 0 8px;
|
||||
color: #502d1e;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background: #2d2f34;
|
||||
}
|
||||
.text {
|
||||
color: #ececec;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.adv-wrap {
|
||||
width: 1512px;
|
||||
// height: 260px;
|
||||
|
||||
margin-top: 47px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
// grid-row-gap: 8px;
|
||||
grid-column-gap: 24px;
|
||||
// position: relative;
|
||||
// .close-adv {
|
||||
// width: 86px;
|
||||
// height: 38px;
|
||||
// position: absolute;
|
||||
// top: 15px;
|
||||
// right: 15px;
|
||||
// z-index: 11111;
|
||||
// }
|
||||
.advItem {
|
||||
width: 744px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 155px;
|
||||
}
|
||||
}
|
||||
.everyone-watch-wrap {
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
padding-top: 55px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
.movie-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(20%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="recommend-card" @click="jumpPage('/moviePlay?page=1&vid=' + itemData.id)">
|
||||
<DecryptImg class="cover" :src="itemData.cover" />
|
||||
<div class="described">
|
||||
<p class="name ellipsis2">{{ itemData.title }}</p>
|
||||
<p class="tags">
|
||||
<span v-for="(item, index) in itemData.tags.slice(0,3)" :key="index">{{ item.name }}</span>
|
||||
</p>
|
||||
<!-- <div class="time-view">
|
||||
<div class="time">
|
||||
<img src="@/assets/images/pc/time.png" alt="">
|
||||
{{ fromNowTime(itemData.createdAt) }}
|
||||
</div>
|
||||
<div class="view">
|
||||
<img src="@/assets/images/pc/view.png" alt="">
|
||||
{{ itemData.playCount }}
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { fromNowTime, videoTime } from '@/utils/index';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
itemData: any
|
||||
}>(),
|
||||
{
|
||||
itemData: () => ({}),
|
||||
},
|
||||
);
|
||||
|
||||
const jumpPage = (path: string) => {
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.recommend-card {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.cover {
|
||||
width: 128px;
|
||||
height: 71px;
|
||||
border-radius: 4px;
|
||||
margin-right: 9px;
|
||||
}
|
||||
.described {
|
||||
flex: 1;
|
||||
.name {
|
||||
color: #ECECEC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
span {
|
||||
padding: 3px 6px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
color: rgba(255, 255, 255, 0.40);
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.time-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.40);
|
||||
margin-top: 6px;
|
||||
|
||||
img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.time,
|
||||
.view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="left-wrap">
|
||||
<div class="thematic">
|
||||
<div class="title">
|
||||
{{ route.query.name }}
|
||||
<div class="more">
|
||||
<div class="all">{{ sortText }} <img class="arrow" src="@/assets/images/pc/down_arrow.png" alt=""></div>
|
||||
<div class="meun-wrap">
|
||||
<div class="meun-list">
|
||||
<div @click="changeSort(item, index)" :class="{ active: activeIndex === index }"
|
||||
v-for="(item, index) in sortList" :key="index">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-list">
|
||||
<movieCard :itemData="item" v-for="(item, index) in videoList" :key="index"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-wrap" v-if="total > 0">
|
||||
<Pagination :total="total" :pageSize="pageSize" @changePage="changePage" />
|
||||
</div>
|
||||
</div>
|
||||
<tag-list />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch, watchEffect } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import HomeApi from '@/service/home';
|
||||
import insertAdv from '@/utils/insertAdv'
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const activeIndex = ref(0)
|
||||
const sortText = ref('全部視頻')
|
||||
const sortList = ref(['最新上架', '最多观看', '最多收藏'])
|
||||
const videoList = ref([])
|
||||
const pageSize = 21
|
||||
let pageNumber = 1
|
||||
const total = ref(0)
|
||||
const changeSort = (item: string, index: number) => {
|
||||
activeIndex.value = index
|
||||
sortText.value = item
|
||||
getDataList(index + 1)
|
||||
}
|
||||
watchEffect(() => {
|
||||
pageNumber = Number(route.query.page || 1)
|
||||
})
|
||||
const getDataList = async (moduleSort: number) => {
|
||||
const res: any = await HomeApi.getModuleList({
|
||||
id: route.query.id,
|
||||
data: {
|
||||
pageNumber,
|
||||
pageSize,
|
||||
moduleSort
|
||||
}
|
||||
})
|
||||
const updatedArray = insertAdv({
|
||||
insertNum: 6,
|
||||
pageSize,
|
||||
pageNumber,
|
||||
originArray: res.allVideoInfo,
|
||||
advNum: 5
|
||||
})
|
||||
videoList.value = updatedArray
|
||||
total.value = res.total
|
||||
}
|
||||
|
||||
// function insertEveryFive(array: Array<any>, insertNum: number) {
|
||||
// // insertNum 每隔insertNum个元素插入一次
|
||||
// const result = [];
|
||||
// let j = 0
|
||||
// const currentPageAdvCout = Math.floor(pageSize / insertNum)
|
||||
// // 当前页广告下标开始的值
|
||||
// j = currentPageAdvCout * pageNumber
|
||||
// for (let i = 0; i < array.length; i++) {
|
||||
// result.push(array[i]);
|
||||
// // 每页多少条广告
|
||||
// if ((i + 1) % insertNum === 0) {
|
||||
// if (!getAdItem(5)[j]) {
|
||||
// j = 0
|
||||
// }
|
||||
// result.push(Object.assign({}, getAdItem(5)[j], {isAdv: true}));
|
||||
// j++
|
||||
// }
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
const changePage = (page: number) => {
|
||||
pageNumber = page
|
||||
router.push({
|
||||
pāth: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
page
|
||||
}
|
||||
})
|
||||
getDataList(activeIndex.value + 1)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList(1)
|
||||
})
|
||||
|
||||
watch(() => route.query.id, (id) => {
|
||||
if (id) {
|
||||
getDataList(1)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: 1560px;
|
||||
margin: auto;
|
||||
padding-top: 65px;
|
||||
display: flex;
|
||||
.left-wrap {
|
||||
width: 1128px;
|
||||
margin: 0 42px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.thematic {
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
color: #FFF;
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 28px;
|
||||
box-sizing: content-box;
|
||||
@include flexBetween;
|
||||
|
||||
.more {
|
||||
@include flexBetween;
|
||||
display: inline-flex;
|
||||
height: 48px;
|
||||
padding: 0px 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
.all {
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
|
||||
img {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.meun-wrap {
|
||||
width: 100%;
|
||||
height: 143px;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.2s;
|
||||
}
|
||||
|
||||
.meun-list {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 105px;
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.more:hover {
|
||||
.meun-wrap {
|
||||
height: 143px;
|
||||
transition: height 0.1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
padding-top: 63px;
|
||||
@include flexCenter;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="privacy">
|
||||
<div class="title">USC 2257</div>
|
||||
<div class="text">
|
||||
DHP.Video 不是网站 (DHP.Video) 上任何和所有内容的制作者(主要或次要)。关于根据 18 USC 2257 记录本网站上的任何和所有内容,请将您的请求直接发送给制作内容的网站。<br/>
|
||||
DHP.Video 是一个视频共享网站,允许上传、共享和观看各种类型的成人内容。虽然 DHP.Video 尽最大努力验证合规性,但它可能不是 100% 准确。<br/>
|
||||
DHP.Video 遵守以下程序以确保合规:<br/>
|
||||
要求所有用户必须年满 18 岁才能观看和上传视频。<br/>
|
||||
上传时,用户必须核实内容;保证自己已年满 18 岁;证明自己保留了内容中模特的记录并且这些模特已年满 18 岁。<br/>
|
||||
如需进一步帮助和/或信息以查找内容的原始站点,请通过支持表格联系 DHP.Video 合规部门。<br/>
|
||||
DHP.Video 允许将内容标记为不当内容。如果任何内容被标记为非法、违法、骚扰、有害、冒犯或其他各种原因,DHP.Video 应立即将其从网站上删除。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.privacy {
|
||||
width: 1150px;
|
||||
margin: 100px auto;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
line-height: 41px;
|
||||
padding-bottom: 41px;
|
||||
}
|
||||
.text {
|
||||
color: #FFF;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="publishingAddress">
|
||||
<div class="title">备用地址</div>
|
||||
<div class="wrap">
|
||||
<div class="list" v-for="(item, index) in urlList" :key="index">
|
||||
<span class="name">{{ item.desc }} :</span>
|
||||
<span class="link" @click="openPage(item.url)">{{ item.url }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
const configStore = useConfigStore();
|
||||
const state = reactive({});
|
||||
const urlList: any = computed(() => {
|
||||
return configStore.sourceInfo.Download;
|
||||
});
|
||||
const openPage = (url: string) => {
|
||||
window.open(url);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.publishingAddress {
|
||||
color: #fff;
|
||||
width: 1104px;
|
||||
margin: 112px auto 0;
|
||||
padding-bottom: 70px;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 41px;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
.wrap {
|
||||
flex-wrap: wrap;
|
||||
font-size: 18px;
|
||||
.list {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.link {
|
||||
color: #FFCD3F;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,396 @@
|
||||
<template>
|
||||
<div class="search-page">
|
||||
<div class="search-box">
|
||||
<div class="movie-tag-list">
|
||||
<div class="all">{{ state.sortText }} <img class="arrow" src="@/assets/images/pc/down_arrow.png" alt=""></div>
|
||||
<div class="meun-wrap">
|
||||
<div class="meun-list">
|
||||
<div @click="changeSort(item, index)" :class="{ active: state.activeIndex === index }"
|
||||
v-for="(item, index) in state.sortList" :key="index">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search" @click.stop>
|
||||
<input type="text" @focus.stop="state.showHistoryList = true" @keydown.enter="search" v-model="state.searchValue" placeholder="輸入番號,女優或視頻名">
|
||||
<div class="history-list" v-show="state.showHistoryList">
|
||||
<div class="history-box">
|
||||
<div class="title">
|
||||
<span>搜索历史</span>
|
||||
<img class="arrow" @click="clearnAll" src="@/assets/images/pc/clearn.png" alt="">
|
||||
</div>
|
||||
<div class="history">
|
||||
<div class="item" v-for="(item, index) in state.historyData" :key="index" @click="selectTag(item)">
|
||||
{{ item }}
|
||||
<img @click.stop="clearnTag(index)" src="@/assets/images/pc/close1.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hot-search-tag-box">
|
||||
<div class="title">
|
||||
热门搜索
|
||||
</div>
|
||||
<div class="hot-item" v-for="(item, index) in state.hotTagList.slice(0,6)" :key="index" @click="selectTag(item.name)">
|
||||
<span :class="['index', index < 3 ? 'top3' : '']">{{ index + 1 }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-btn" @click="search">搜索一下</div>
|
||||
</div>
|
||||
<div class="tag-lst">
|
||||
<span v-for="(item, index) in state.hotTagList.slice(0,10)" :key="index" @click="selectTag(item.name)">{{ item.name }}</span>
|
||||
</div>
|
||||
<!-- 搜索结果 -->
|
||||
<div class="results-wrap">
|
||||
<div class="title">搜索结果</div>
|
||||
<div class="result">全部视频 <span>({{ state.total }})</span></div>
|
||||
<div class="card-list">
|
||||
<movieCard :itemData="item" v-for="(item, index) in state.searchResults" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-center-center" v-if="state.total > 0">
|
||||
<Pagination :pageSize="pageSize" :total="state.total" @changePage="changePage" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import insertAdv from '@/utils/insertAdv'
|
||||
import { onMounted, reactive, ref, watch } from 'vue'
|
||||
import CommunityApi from '@/service/community';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageSize = 24
|
||||
|
||||
const state = reactive({
|
||||
searchValue: '',
|
||||
activeIndex: 0,
|
||||
sortText: '全部視頻',
|
||||
sortList: ['最新上架', '最多观看', '最多收藏'],
|
||||
hotTagList: [],
|
||||
pageNumber: 1,
|
||||
total: 0,
|
||||
showHistoryList: false,
|
||||
historyData: [],
|
||||
searchResults: [],
|
||||
history
|
||||
})
|
||||
|
||||
// 获热搜列表
|
||||
const getHotTag = async () => {
|
||||
const res: any = await CommunityApi.getHotRecommend({
|
||||
pageNumber: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
state.hotTagList = res.list
|
||||
}
|
||||
getHotTag()
|
||||
|
||||
|
||||
const changeSort = (item: string, index: number) => {
|
||||
state.activeIndex = index
|
||||
state.sortText = item
|
||||
search()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const search = async () => {
|
||||
AddSearchHistory()
|
||||
const res: any = await CommunityApi.searchPost({
|
||||
SortV: state.activeIndex,
|
||||
keyWords: [state.searchValue],
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize,
|
||||
realm: 'video'
|
||||
})
|
||||
const updatedArray = insertAdv({
|
||||
insertNum: 6,
|
||||
pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
originArray: res.list || [],
|
||||
advNum: 5
|
||||
})
|
||||
state.searchResults = updatedArray
|
||||
state.total = res.total || 0
|
||||
getSearchHistoryData()
|
||||
}
|
||||
|
||||
// 分页
|
||||
const changePage = (page: number) => {
|
||||
state.pageNumber = page
|
||||
router.push({
|
||||
pāth: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
page
|
||||
}
|
||||
})
|
||||
search()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const value = route.query.id
|
||||
if (value) {
|
||||
state.pageNumber = Number(route.query.page || 1)
|
||||
state.searchValue = value as string
|
||||
search()
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => route.query.id, (value) => {
|
||||
if (value) {
|
||||
state.pageNumber = Number(route.query.page || 1)
|
||||
state.searchValue = value as string
|
||||
search()
|
||||
}
|
||||
})
|
||||
|
||||
const selectTag = (item: string) => {
|
||||
state.searchValue = item
|
||||
state.showHistoryList = false
|
||||
search()
|
||||
}
|
||||
|
||||
const clearnTag = (index: number) => {
|
||||
state.historyData.splice(index, 1)
|
||||
localStorage.setItem('SEARCH_HISTORY', state.historyData.join(','))
|
||||
}
|
||||
const clearnAll = () => {
|
||||
state.historyData = []
|
||||
localStorage.removeItem('SEARCH_HISTORY')
|
||||
}
|
||||
// 添加历史记录
|
||||
const AddSearchHistory = () => {
|
||||
// 搜索记录加数据
|
||||
if (state.historyData.length > 0) {
|
||||
// 去重
|
||||
const arr = [...new Set(state.historyData)]
|
||||
const narr = arr.slice(0,3)
|
||||
narr.unshift(state.searchValue)
|
||||
localStorage.setItem('SEARCH_HISTORY', narr.join(','))
|
||||
} else {
|
||||
localStorage.setItem('SEARCH_HISTORY', state.searchValue)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => state.showHistoryList, (value) => {
|
||||
if (value) {
|
||||
getSearchHistoryData()
|
||||
}
|
||||
})
|
||||
// 获取历史记录
|
||||
const getSearchHistoryData = () => {
|
||||
const data = localStorage.getItem('SEARCH_HISTORY')
|
||||
if (data) {
|
||||
const arr = data.split(',')
|
||||
state.historyData = [...new Set(arr)];
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', () => {
|
||||
state.showHistoryList = false
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.search-page {
|
||||
width: 1516px;
|
||||
color: #fff;
|
||||
padding-top: 120px;
|
||||
margin: auto;
|
||||
|
||||
.search-box {
|
||||
@include flexCenter;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.movie-tag-list {
|
||||
height: 60px;
|
||||
padding: 0px 25px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
@include flexCenter;
|
||||
cursor: pointer;
|
||||
|
||||
.all {
|
||||
color: #A0A6B4;
|
||||
font-size: 18px;
|
||||
|
||||
img {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.meun-wrap {
|
||||
width: 100%;
|
||||
height: 143px;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.2s;
|
||||
}
|
||||
|
||||
.meun-list {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 105px;
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.active {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
.movie-tag-list:hover {
|
||||
.meun-wrap {
|
||||
height: 143px;
|
||||
transition: height 0.1s;
|
||||
}
|
||||
}
|
||||
.search {
|
||||
max-width: 700px;
|
||||
display: flex;
|
||||
height: 60px;
|
||||
padding: 16px 21px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1 0 0;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #FFC400;
|
||||
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
input {
|
||||
all: unset;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
color: #FFF;
|
||||
}
|
||||
.history-list {
|
||||
width: 100%;
|
||||
height: 486px;
|
||||
padding: 17px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
background: #1A1C22;
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
.title {
|
||||
@include flexBetween;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
height: 32px;
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.history {
|
||||
.item {
|
||||
@include flexBetween;
|
||||
color: #BCBDBE;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 16px;
|
||||
margin-top: 24px;
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hot-search-tag-box {
|
||||
color: #BCBDBE;
|
||||
font-size: 14px;
|
||||
padding-top: 30px;
|
||||
.hot-item {
|
||||
margin-top: 24px;
|
||||
.index {
|
||||
color: #CCC;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.top3 {
|
||||
color: #FFCD3F;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
height: 60px;
|
||||
width: 112px;
|
||||
@include flexCenter;
|
||||
color: #482F28;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
border-radius: 10px;
|
||||
background: #FFC400;
|
||||
box-shadow: 0px -3px 8px 0px rgba(249, 255, 139, 0.60) inset, 0px 0px 8px 0px rgba(255, 196, 0, 0.50);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tag-lst {
|
||||
@include flexCenter;
|
||||
padding-top: 20px;
|
||||
column-gap: 20px;
|
||||
span {
|
||||
font-size: 20px;
|
||||
color: #E5E7EB;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
span:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.results-wrap {
|
||||
width: 1516px;
|
||||
margin: 70px auto;
|
||||
.title {
|
||||
color: #FFF;
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
line-height: 48px;
|
||||
}
|
||||
.result {
|
||||
color: #FFF;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
margin-top: 12px;
|
||||
span {
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.card-list {
|
||||
padding-top: 34px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="left-wrap">
|
||||
<div class="thematic">
|
||||
<div class="title">
|
||||
{{ route.query.tagName }}
|
||||
<div class="more">
|
||||
<div class="all">{{ sortText }} <img class="arrow" src="@/assets/images/pc/down_arrow.png" alt=""></div>
|
||||
<div class="meun-wrap">
|
||||
<div class="meun-list">
|
||||
<div @click="changeSort(item, index)" :class="{ active: activeIndex === index }"
|
||||
v-for="(item, index) in sortList" :key="index">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-list">
|
||||
<movieCard :itemData="item" v-for="(item, index) in videoList" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-wrap" v-if="total > 0">
|
||||
<Pagination :total="total" :pageSize="pageSize" @changePage="changePage" />
|
||||
</div>
|
||||
</div>
|
||||
<tag-list />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch, watchEffect } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import VideoApi from '@/service/video';
|
||||
import insertAdv from '@/utils/insertAdv'
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const pageSize = 10
|
||||
const activeIndex = ref(0)
|
||||
const sortText = ref('全部視頻')
|
||||
const sortList = ref(['最新上架', '最多观看', '最多收藏'])
|
||||
const videoList = ref([])
|
||||
let pageNumber = 1
|
||||
const total = ref(0)
|
||||
const changeSort = (item: string, index: number) => {
|
||||
activeIndex.value = index
|
||||
sortText.value = item
|
||||
getDataList()
|
||||
}
|
||||
watchEffect(() => {
|
||||
pageNumber = Number(route.query.page || 1)
|
||||
})
|
||||
const getDataList = async () => {
|
||||
const res: any = await VideoApi.searchTagPost({
|
||||
pageNumber,
|
||||
pageSize,
|
||||
tagID: route.query.id,
|
||||
sortType: activeIndex.value + 1,
|
||||
})
|
||||
const updatedArray = insertAdv({
|
||||
insertNum: 6,
|
||||
pageSize,
|
||||
pageNumber,
|
||||
originArray: res.list,
|
||||
advNum: 5
|
||||
})
|
||||
videoList.value = updatedArray
|
||||
total.value = res.total
|
||||
}
|
||||
|
||||
const changePage = (page: number) => {
|
||||
pageNumber = page
|
||||
router.push({
|
||||
pāth: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
page
|
||||
}
|
||||
})
|
||||
getDataList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList()
|
||||
})
|
||||
|
||||
watch(() => route.query.id, (id) => {
|
||||
if (id) {
|
||||
getDataList()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: 1560px;
|
||||
margin: auto;
|
||||
padding-top: 65px;
|
||||
display: flex;
|
||||
.left-wrap {
|
||||
width: 1128px;
|
||||
margin: 0 42px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.thematic {
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
color: #FFF;
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 28px;
|
||||
box-sizing: content-box;
|
||||
@include flexBetween;
|
||||
|
||||
.more {
|
||||
@include flexBetween;
|
||||
display: inline-flex;
|
||||
height: 48px;
|
||||
padding: 0px 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
.all {
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
|
||||
img {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.meun-wrap {
|
||||
width: 100%;
|
||||
height: 143px;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: height 0.2s;
|
||||
}
|
||||
|
||||
.meun-list {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 105px;
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
color: #A0A6B4;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.more:hover {
|
||||
.meun-wrap {
|
||||
height: 143px;
|
||||
transition: height 0.1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
row-gap: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
padding-top: 63px;
|
||||
@include flexCenter;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,351 @@
|
||||
<template>
|
||||
<div class="welfare-page">
|
||||
<div class="box1">
|
||||
<div class="img-list">
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(0, 1)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(1, 3)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(3, 6)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(6, 9)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(9, 13)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(13, 16)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(16, 20)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(20, 23)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(23, 26)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(26, 28)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(28, 29)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="img-list-one">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure?.slice(29, 30)"
|
||||
class="cover"
|
||||
@click="jumpAdv(item, true)"
|
||||
:src="item.icon"
|
||||
:key="item.icon + index"
|
||||
/>
|
||||
</div>
|
||||
<div class="title">
|
||||
<p>在線視頻</p>
|
||||
<p>来自一站式你所熟悉的蛋黄派</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-wrap">
|
||||
<div class="title">
|
||||
<p v-for="(item, index) in state.bgImage1" :key="index">{{ item }}</p>
|
||||
</div>
|
||||
<swiper
|
||||
:modules="[Autoplay, Pagination, FreeMode]"
|
||||
free-mode
|
||||
:loop="true"
|
||||
:slides-per-view="6.2"
|
||||
:space-between="16"
|
||||
>
|
||||
<swiper-slide v-for="(item, index) in state.verticalSlide" :key="index">
|
||||
<div class="card-item" @click="jumpAdv(item, true)">
|
||||
<DecryptImg class="cover" :src="item.icon" :key="item.icon + index" />
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
<div class="game-wrap1">
|
||||
<div class="title">
|
||||
<p v-for="(item, index) in state.bgImage2" :key="index">{{ item }}</p>
|
||||
</div>
|
||||
<swiper :modules="[Autoplay, Pagination, FreeMode]" free-mode loop :slides-per-view="6.2" :space-between="16">
|
||||
<swiper-slide v-for="(item, index) in state.verticalTwoSlide" :key="index">
|
||||
<div class="card-item">
|
||||
<DecryptImg class="cover" :src="item.icon" :key="item.icon + index" @click="jumpAdv(item, true)" />
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<swiper
|
||||
style="padding: 60px 0"
|
||||
:modules="[Autoplay, Pagination, FreeMode]"
|
||||
loop
|
||||
free-mode
|
||||
:slides-per-view="6.2"
|
||||
:space-between="16"
|
||||
>
|
||||
<swiper-slide v-for="(item, index) in state.smallSqureSlide" :key="index">
|
||||
<div class="card-item">
|
||||
<DecryptImg class="cover" :src="item.icon" :key="item.icon + index" @click="jumpAdv(item, true)" />
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { jumpAdv } from '@/utils/index';
|
||||
import { Autoplay, Pagination, FreeMode } from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import request from '@/utils/request';
|
||||
import 'swiper/swiper.min.css';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
const state = reactive({
|
||||
smallSuure: [],
|
||||
verticalSlide: [],
|
||||
bgImage1: [],
|
||||
verticalTwoSlide: [],
|
||||
bgImage2: [],
|
||||
smallSqureSlide: [],
|
||||
});
|
||||
|
||||
request.get('recreation/list_v2').then((res: any) => {
|
||||
const resdata = res.list
|
||||
console.log(resdata);
|
||||
|
||||
const data = resdata.find((item: any) => item.desc === '福利模块') || {};
|
||||
const dataList = data.comboList || [];
|
||||
console.log(dataList);
|
||||
|
||||
state.smallSuure = dataList.find((item: any) => item.displayType === 'smallSuure').list;
|
||||
state.verticalSlide = dataList.find((item: any) => item.displayType === 'verticalSlide').list;
|
||||
// #换行
|
||||
const bgImage1List = dataList.find((item: any) => item.displayType === 'bgImage1').list
|
||||
state.bgImage1 = bgImage1List && bgImage1List[0]?.desc?.split('#') || [];
|
||||
state.verticalTwoSlide = dataList.find((item: any) => item.displayType === 'verticalTwoSlide').list;
|
||||
// #换行
|
||||
const bgImage2 = dataList.find((item: any) => item.displayType === 'bgImage2').list
|
||||
state.bgImage2 = bgImage2 && bgImage2[0]?.desc?.split('#') || [];
|
||||
state.smallSqureSlide = dataList.find((item: any) => item.displayType === 'smallSqureSlide').list;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media (min-width: 768px) {
|
||||
.box1 {
|
||||
width: 100%;
|
||||
height: 1100px;
|
||||
background-color: #fff;
|
||||
.title {
|
||||
padding-top: 70px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
font-size: 70px;
|
||||
font-weight: 900;
|
||||
line-height: 76px;
|
||||
margin-bottom: 157px;
|
||||
p {
|
||||
padding-top: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.img-list {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
padding: 0 137px;
|
||||
display: flex;
|
||||
gap: 26px;
|
||||
overflow: hidden;
|
||||
.item-list {
|
||||
width: 126px;
|
||||
}
|
||||
.cover {
|
||||
width: 126px;
|
||||
height: 126px;
|
||||
border-radius: 17px;
|
||||
overflow: hidden;
|
||||
margin: 18px 0;
|
||||
}
|
||||
}
|
||||
.img-list-one {
|
||||
margin: auto;
|
||||
width: 173px;
|
||||
height: 126px;
|
||||
animation: slide1 10s linear infinite;
|
||||
.cover {
|
||||
width: 173px;
|
||||
height: 173px;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
margin: 18px 0;
|
||||
}
|
||||
}
|
||||
.item-list:nth-child(2n) {
|
||||
animation: slide 10s linear infinite;
|
||||
}
|
||||
.item-list:nth-child(2n + 1) {
|
||||
animation: slide1 10s linear infinite;
|
||||
}
|
||||
.box1:hover {
|
||||
.item-list,
|
||||
.img-list-one {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
.game-wrap {
|
||||
width: 100%;
|
||||
height: 1028px;
|
||||
background: url('@/assets/images/pc/game_bg.png') no-repeat #000;
|
||||
background-position: bottom center;
|
||||
background-size: 100% 100%;
|
||||
.title {
|
||||
padding-top: 170px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
font-size: 76px;
|
||||
font-weight: 900;
|
||||
line-height: 76px;
|
||||
margin-bottom: 157px;
|
||||
p {
|
||||
padding-top: 32px;
|
||||
}
|
||||
}
|
||||
.card-item {
|
||||
width: 15vw;
|
||||
// height: 532px;
|
||||
border-radius: 12px;
|
||||
.cover {
|
||||
width: 15vw;
|
||||
height: 360px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
.game-wrap1 {
|
||||
width: 100%;
|
||||
// height: 1144px;
|
||||
background: #fff;
|
||||
.title {
|
||||
padding-top: 170px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
font-size: 76px;
|
||||
font-weight: 900;
|
||||
line-height: 76px;
|
||||
margin-bottom: 157px;
|
||||
p {
|
||||
padding-top: 32px;
|
||||
}
|
||||
}
|
||||
.card-item {
|
||||
width: 15vw;
|
||||
height: 360px;
|
||||
border-radius: 18px;
|
||||
.cover {
|
||||
width: 15vw;
|
||||
// height: 532px;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 关键帧动画 */
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateY(-63px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-63px);
|
||||
}
|
||||
}
|
||||
/* 关键帧动画 */
|
||||
@keyframes slide1 {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-63px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user