This commit is contained in:
谢宇宁
2026-10-09 17:39:29 +07:00
parent 64e47a7b4b
commit 406b575139
464 changed files with 0 additions and 11222 deletions
-70
View File
@@ -1,70 +0,0 @@
<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>
-258
View File
@@ -1,258 +0,0 @@
<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>
-201
View File
@@ -1,201 +0,0 @@
<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>
-312
View File
@@ -1,312 +0,0 @@
<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>
-240
View File
@@ -1,240 +0,0 @@
<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>
-256
View File
@@ -1,256 +0,0 @@
<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>
-154
View File
@@ -1,154 +0,0 @@
<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>
-170
View File
@@ -1,170 +0,0 @@
<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>
-128
View File
@@ -1,128 +0,0 @@
<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>
-111
View File
@@ -1,111 +0,0 @@
<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>
-28
View File
@@ -1,28 +0,0 @@
<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>
-387
View File
@@ -1,387 +0,0 @@
<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>
-97
View File
@@ -1,97 +0,0 @@
<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>
-212
View File
@@ -1,212 +0,0 @@
<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>
-396
View File
@@ -1,396 +0,0 @@
<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>
-190
View File
@@ -1,190 +0,0 @@
<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>
-351
View File
@@ -1,351 +0,0 @@
<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>