初始化黄果短剧 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,333 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="loadingBox" v-if="state.dataloading">
|
||||
<van-loading class="loading" color="#fff" vertical>加载中...</van-loading>
|
||||
</div>
|
||||
<div v-else>
|
||||
<mobile v-if="isMobile" />
|
||||
<pc v-else />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onUnmounted, reactive } from 'vue';
|
||||
import { useAppStore } from '@/stores/app';
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { getDevType } from '@/utils';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { AdType, SourceType } from '@/enums/app';
|
||||
import { useHomeStore } from '@/stores/home';
|
||||
import axios from 'axios';
|
||||
import AppApi from '@/service/app';
|
||||
import mobile from './../mobile/index.vue';
|
||||
import pc from './../pc/index.vue';
|
||||
|
||||
const isMobile = ref(false);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const configStore = useConfigStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const homeStore = useHomeStore();
|
||||
const state = reactive({
|
||||
isCheckError: false, //是否选线失败
|
||||
checkLineErrorNum: 0, //选线失败次数
|
||||
isResLock: false, // 请求结果锁
|
||||
resResult: <any>[], // 响应结果
|
||||
pingCheckCount: 0, // 请求的ping失败次数
|
||||
loading: true, // 选线loading
|
||||
videoPlayer: true,
|
||||
dataloading: true,
|
||||
});
|
||||
|
||||
const setDeviceType = () => {
|
||||
isMobile.value = window.innerWidth <= 768;
|
||||
// if (isMobile.value) {
|
||||
// router.push('/home')
|
||||
// } else {
|
||||
// router.push('/welfare')
|
||||
// }
|
||||
};
|
||||
|
||||
setDeviceType();
|
||||
// 监听屏幕大小变化
|
||||
window.addEventListener('resize', () => {
|
||||
setDeviceType();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', setDeviceType);
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 如果没有用户ID就生成一个
|
||||
*/
|
||||
const generateUid = () => {
|
||||
if (!appStore.uid) {
|
||||
appStore.generateUid();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 登陆
|
||||
* @returns 是否登陆成功
|
||||
*/
|
||||
const login = async (): Promise<boolean> => {
|
||||
try {
|
||||
const uid = appStore.uid;
|
||||
const sysType = getDevType();
|
||||
const cutInfos = Object.keys(route.query).length > 0 ? JSON.stringify(route.query) : undefined;
|
||||
let userInfo = await AppApi.login(uid, sysType, cutInfos);
|
||||
userStore.saveUserInfo(userInfo);
|
||||
userStore.saveToken(userInfo.token);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 初始化资源域名配置
|
||||
* @param list 资源列表
|
||||
*/
|
||||
const initSourceInfo = (list: Array<Types.App.SourceListItem>): void => {
|
||||
let sourceInfo: Types.App.SourceInfo = {
|
||||
GROUP: '',
|
||||
GUIDE: '',
|
||||
QRCODE: '',
|
||||
IMAGE: '',
|
||||
VID: [],
|
||||
LAND: '',
|
||||
PROXYRULE: '',
|
||||
FAQ: '',
|
||||
WELFARE: '',
|
||||
ACT: '',
|
||||
AppStore: '',
|
||||
UP: '',
|
||||
TELEGRAM: '',
|
||||
EMAIL: '',
|
||||
AUDIO: '',
|
||||
Download: '',
|
||||
};
|
||||
for (let item of list) {
|
||||
switch (item.type) {
|
||||
case SourceType.GROUP:
|
||||
sourceInfo.GROUP = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.TELEGRAM:
|
||||
sourceInfo.TELEGRAM = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.GUIDE:
|
||||
sourceInfo.GUIDE = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.IMAGE:
|
||||
sourceInfo.IMAGE = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.VID:
|
||||
sourceInfo.VID = item.domain;
|
||||
break;
|
||||
case SourceType.LAND:
|
||||
sourceInfo.LAND = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.PROXYRULE:
|
||||
sourceInfo.PROXYRULE = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.FAQ:
|
||||
sourceInfo.FAQ = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.QRCODE:
|
||||
sourceInfo.QRCODE = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.WELFARE:
|
||||
sourceInfo.WELFARE = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.AppStore:
|
||||
sourceInfo.AppStore = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.ACT:
|
||||
sourceInfo.ACT = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.UP:
|
||||
sourceInfo.UP = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.EMAIL:
|
||||
sourceInfo.EMAIL = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.AUDIO:
|
||||
sourceInfo.AUDIO = item.domain[0]?.url;
|
||||
break;
|
||||
case SourceType.Download:
|
||||
sourceInfo.Download = item.domain;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
configStore.saveSourceInfo(sourceInfo);
|
||||
};
|
||||
/**
|
||||
* @description 获取配置信息
|
||||
* @returns 获取配置信息是否成功
|
||||
*/
|
||||
const getConfig = async (): Promise<boolean> => {
|
||||
try {
|
||||
const configList = await AppApi.queryDomain();
|
||||
state.dataloading = false;
|
||||
configStore.saveAppConfig(configList);
|
||||
initSourceInfo(configList.sourceList);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const pingErrorPop = (hosts: any) => {
|
||||
// 只有最后一条域名出错时,判断响应结果不存在true的时候,说明全部不通,即弹出选线失败
|
||||
state.pingCheckCount++;
|
||||
if (state.pingCheckCount == hosts.length) {
|
||||
state.pingCheckCount = 0;
|
||||
let index = state.resResult.indexOf(true);
|
||||
if (index == -1) {
|
||||
if (state.checkLineErrorNum == 1) {
|
||||
state.isCheckError = true;
|
||||
return;
|
||||
}
|
||||
// showDialog({
|
||||
// title: '温馨提示',
|
||||
// message: '选线失败',
|
||||
// confirmButtonText: '重试',
|
||||
// theme: 'round-button',
|
||||
// }).then(() => {
|
||||
// state.checkLineErrorNum++;
|
||||
// appStore.$reset();
|
||||
// userStore.$reset();
|
||||
// configStore.$reset();
|
||||
// init();
|
||||
// });
|
||||
}
|
||||
}
|
||||
};
|
||||
const queryContactInfo = async () => {
|
||||
try {
|
||||
let req = {
|
||||
type: 2,
|
||||
};
|
||||
const res: any = await AppApi.getBusiness(req);
|
||||
useConfigStore().setContactInfo(res || []);
|
||||
// console.log(res);
|
||||
// contactInfo
|
||||
// configStore.saveAppConfig(configList);
|
||||
// initSourceInfo(configList.sourceList);
|
||||
// return true;
|
||||
} catch (error) {
|
||||
// return false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
const init = async () => {
|
||||
generateUid();
|
||||
const baseUrls: string = process.env.VITE_BASE_URL || '[]';
|
||||
const hosts = JSON.parse(baseUrls);
|
||||
state.resResult = new Array(hosts.length);
|
||||
hosts.forEach((url: any, index: number) => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: url + '/api/app/ping/check',
|
||||
timeout: 1000 * 10, // 10秒超时
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (res.data.code == 200) {
|
||||
if (!state.isResLock) {
|
||||
state.isResLock = true;
|
||||
state.resResult[index] = true;
|
||||
//选线成功
|
||||
useConfigStore().saveBaseUrl(url);
|
||||
console.log(configStore.sourceInfo.VID[0]);
|
||||
state.loading = false;
|
||||
//登陆
|
||||
if (!(userStore.userInfo && userStore.token)) {
|
||||
let userInfo: any = localStorage.getItem('userInfo');
|
||||
let token = localStorage.getItem('token');
|
||||
if (userInfo && token) {
|
||||
userStore.saveUserInfo(JSON.parse(userInfo));
|
||||
userStore.saveToken(JSON.parse(token));
|
||||
} else {
|
||||
const loginOk = await login();
|
||||
if (!loginOk) {
|
||||
// showDialog({
|
||||
// title: '温馨提示',
|
||||
// message: '用户登陆失败',
|
||||
// confirmButtonText: '重试',
|
||||
// theme: 'round-button',
|
||||
// }).then(() => {
|
||||
// appStore.$reset();
|
||||
// userStore.$reset();
|
||||
// configStore.$reset();
|
||||
// init();
|
||||
// });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
homeStore.getHomeData();
|
||||
queryContactInfo();
|
||||
//获取配置信息
|
||||
const configOk = await getConfig();
|
||||
globalStore.setVideoRoadLine(configStore.sourceInfo.VID[0]); // 默认选择第一个线路
|
||||
if (!configOk) {
|
||||
// showDialog({
|
||||
// title: '温馨提示',
|
||||
// message: '获取配置信息失败',
|
||||
// confirmButtonText: '重试',
|
||||
// theme: 'round-button',
|
||||
// }).then(() => {
|
||||
// appStore.$reset();
|
||||
// userStore.$reset();
|
||||
// configStore.$reset();
|
||||
// init();
|
||||
// userStore.saveToken('');
|
||||
// localStorage.removeItem('userInfo');
|
||||
// localStorage.removeItem('token');
|
||||
// });
|
||||
return;
|
||||
}
|
||||
// const launchList = getAdItem(AdType.START);
|
||||
// if (launchList.length > 0) {
|
||||
// state.launchInfo = launchList[0];
|
||||
// state.advsList = launchList || [];
|
||||
// }
|
||||
//开机广告
|
||||
// adControl();
|
||||
}
|
||||
} else {
|
||||
pingErrorPop(hosts);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
pingErrorPop(hosts);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* created
|
||||
*/
|
||||
init();
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.loadingBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
// .loading {
|
||||
// width: 40px;
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="collect overflow-y-auto" ref="contentRef">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">我的收藏</div>
|
||||
<div class="btnBox">
|
||||
<div class="cancelBtn" v-if="state.isEdit" @click="cancelEdit">取消</div>
|
||||
<div class="editBtn" :class="{ disableEdit: !state.delList.length }" v-if="state.isEdit" @click="delVideo">
|
||||
删除
|
||||
</div>
|
||||
<div class="editBtn" @click="edit" v-else>编辑</div>
|
||||
</div>
|
||||
<div class="allSelectBox" v-if="state.isEdit" @click="allSelect">
|
||||
<SvgIcon name="selectedIcon" class="selectedIcon" v-if="state.delList.length == state.list.length" />
|
||||
<div class="selectIcon" v-else></div>
|
||||
全选
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="mobileVideoBox" v-for="item in state.list" :key="item.id">
|
||||
<MobileVideoBox :videoInfo="item" />
|
||||
<div class="videoMask" v-if="state.isEdit" @click.stop="selectVideo(item)">
|
||||
<SvgIcon name="selectedIcon" class="selected" v-if="handleSelectSta(item.id)" />
|
||||
<div class="select" v-else></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { showToast } from 'vant';
|
||||
import MineApi from '@/service/mine';
|
||||
const globalStore = useGlobalStore();
|
||||
const contentRef = ref();
|
||||
const state = reactive({
|
||||
isEdit: false,
|
||||
pageNumber: 1,
|
||||
pageSize: 6,
|
||||
total: 0,
|
||||
list: <any>[],
|
||||
delList: <any>[],
|
||||
});
|
||||
const userInfo = computed(() => {
|
||||
const data = localStorage.getItem('userInfo');
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
|
||||
const edit = () => {
|
||||
state.isEdit = true;
|
||||
};
|
||||
const cancelEdit = () => {
|
||||
state.isEdit = false;
|
||||
state.delList = [];
|
||||
};
|
||||
|
||||
const selectVideo = (item: any) => {
|
||||
let index = state.delList.findIndex((i: any) => {
|
||||
return item.id == i;
|
||||
});
|
||||
if (index != -1) {
|
||||
state.delList.splice(index, 1);
|
||||
} else {
|
||||
state.delList.push(item.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectSta = (id: any) => {
|
||||
let result = state.delList.some((i: any) => {
|
||||
return i == id;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const allSelect = () => {
|
||||
if (state.delList.length) {
|
||||
state.delList = [];
|
||||
} else {
|
||||
let arr: any = [];
|
||||
state.list.forEach((item: any) => {
|
||||
arr.push(item.id);
|
||||
});
|
||||
state.delList = arr;
|
||||
}
|
||||
};
|
||||
|
||||
const delVideo = async () => {
|
||||
let req = {
|
||||
objIds: state.delList,
|
||||
type: 'video',
|
||||
};
|
||||
await MineApi.batchUnCollect(req);
|
||||
// const res: any = await delAllData(HistoryVideoType.VIDEO, state.delList);
|
||||
queryList();
|
||||
state.isEdit = false;
|
||||
state.delList = [];
|
||||
showToast('删除成功');
|
||||
};
|
||||
|
||||
const changePage = (pageNumber: number) => {
|
||||
state.pageNumber = pageNumber;
|
||||
contentRef.value.scrollTo({ top: 0 });
|
||||
queryList();
|
||||
};
|
||||
|
||||
const queryList = async () => {
|
||||
let req = {
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
type: 'video',
|
||||
uid: userInfo.value.uid,
|
||||
};
|
||||
const res: any = await MineApi.queryCollectionList(req);
|
||||
state.total = res.total;
|
||||
state.list = res.list || [];
|
||||
};
|
||||
queryList();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.collect {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 26px 16px 150px;
|
||||
.subTitle {
|
||||
display: flex;
|
||||
width: 80.323px;
|
||||
height: 41px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.btnBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 24px;
|
||||
.cancelBtn {
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ffc303;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
color: #ffcd3f;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
.editBtn {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #ffc303 0%, #ffdd01 100%);
|
||||
}
|
||||
.disableEdit {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
.allSelectBox {
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
font-size: 16px;
|
||||
.selectIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
border: 2.2px solid #999;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.selectedIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 16px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 45px;
|
||||
.mobileVideoBox {
|
||||
position: relative;
|
||||
.videoMask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
.select {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 2.2px solid #999;
|
||||
}
|
||||
.selected {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="classification">
|
||||
<div class="topicBox" v-for="i in state.topicList" :key="i.id">
|
||||
<div class="topicTitle">{{ i.subModuleName }}</div>
|
||||
<div class="tagList">
|
||||
<div class="tagItem" v-for="item in i.allSection" :key="item.id" @click="jumpTopice(item)">
|
||||
{{ item.name }}
|
||||
<span>({{ item.videoCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import HomeApi from '@/service/home';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
topicList: <any>[],
|
||||
topicName: route.query.name,
|
||||
});
|
||||
|
||||
const jumpTopice = (item: any) => {
|
||||
let queryData = {
|
||||
tid: item.id,
|
||||
name: item.name,
|
||||
};
|
||||
router
|
||||
.push({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
})
|
||||
// .then(() => {
|
||||
// location.reload();
|
||||
// });
|
||||
};
|
||||
const getTagList = async () => {
|
||||
const res: any = await HomeApi.getTabs({ isSection: true });
|
||||
state.topicList = res.homePage || [];
|
||||
};
|
||||
const init = () => {
|
||||
getTagList();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.classification {
|
||||
padding: 18px 16px 0;
|
||||
box-sizing: border-box;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
.topicBox {
|
||||
margin-bottom: 24px;
|
||||
.topicTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px; /* 155.556% */
|
||||
margin-bottom: 12.25px;
|
||||
}
|
||||
.tagList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
.tagItem {
|
||||
padding: 4px 12px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
margin: 0 8px 8px 0;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topicBox:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.dropdownBox {
|
||||
width: 76px;
|
||||
height: 33px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #212121;
|
||||
background: #151515;
|
||||
margin-bottom: 13.6px;
|
||||
// background: skyblue;
|
||||
:deep(.van-dropdown-menu__bar) {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title) {
|
||||
padding: 0;
|
||||
padding-right: 6px;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title--active:after) {
|
||||
border-color: transparent transparent #a0a6b4 #a0a6b4;
|
||||
}
|
||||
:deep(.van-ellipsis) {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.van-dropdown-item) {
|
||||
width: 76px;
|
||||
right: 16px;
|
||||
left: auto;
|
||||
// right: 16px !important;
|
||||
// top: 69px !important;
|
||||
|
||||
.van-dropdown-item__content {
|
||||
border-radius: 5.548px;
|
||||
border: 0.693px solid #212121;
|
||||
background: #151515 !important;
|
||||
padding: 7px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
.van-dropdown-item__option {
|
||||
background: #151515 !important;
|
||||
padding: 0 13.87px;
|
||||
// height: 20px;
|
||||
}
|
||||
.van-cell__title {
|
||||
color: #a0a6b4;
|
||||
font-size: 9.709px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell:after {
|
||||
display: none !important;
|
||||
}
|
||||
.van-cell__value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="homeItem">
|
||||
<van-notice-bar
|
||||
class="homeNotice"
|
||||
scrollable
|
||||
background="#2B2C2E"
|
||||
color="#FFF"
|
||||
:speed="60"
|
||||
v-if="state.announceText && state.announceText.length"
|
||||
>
|
||||
<span v-for="(item, index) in state.announceText" :key="index">{{ item }}</span>
|
||||
</van-notice-bar>
|
||||
<BannerSwiper :list="state.bannerList" class="bannerSwiper" v-if="state.bannerList && state.bannerList.length" />
|
||||
<MobileSwiper :list="state.rankList" v-if="state.rankList && state.rankList.length" class="rankSwiper" />
|
||||
<div class="topicBox" v-for="(item, index) in state.topicsList" :key="item.sectionID">
|
||||
<div class="topicTitleBox">
|
||||
<div class="topicTitle">{{ item.sectionName }}</div>
|
||||
<div class="moreBtn" @click="jumpTopic(item)">
|
||||
<SvgIcon name="moreIcon" class="moreIcon" />
|
||||
<span>更多</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<MobileVideoBox :videoInfo="i" v-for="i in item.allVideoInfo" :key="i.id" />
|
||||
</div>
|
||||
<AdvBanner
|
||||
class="advBanner"
|
||||
:advInfo="state.advList[index % state.advList.length]"
|
||||
:key="state.advList[index % state.advList.length]"
|
||||
v-if="state.advList.length"
|
||||
/>
|
||||
<!-- <AdvBanner class="advBanner" :advInfo="i" v-for="i in state.advList" :key="i.id" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { AdType } from '@/enums/app';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import HomeApi from '@/service/home';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
tid: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
bannerList: [],
|
||||
announceText: '',
|
||||
topicsList: <any>[],
|
||||
rankList: <any>[],
|
||||
advList: <any>[],
|
||||
});
|
||||
|
||||
const queryList = async () => {
|
||||
let req = {
|
||||
id: props.tid,
|
||||
data: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
videoCount: 8,
|
||||
},
|
||||
};
|
||||
let res: any = await HomeApi.getModuleList(req);
|
||||
let allSection = res.allSection || [];
|
||||
allSection.forEach((item: any) => {
|
||||
if (item.showType == 2) {
|
||||
state.bannerList = state.bannerList.concat(item.allVideoInfo || []);
|
||||
} else {
|
||||
state.topicsList = state.topicsList.concat(item || []);
|
||||
}
|
||||
// console.log(state.topicsList, '-=-==-');
|
||||
});
|
||||
};
|
||||
const queryRanks = async () => {
|
||||
let req = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
let res: any = await HomeApi.getRangking(req);
|
||||
state.rankList = res.videos || [];
|
||||
console.log(state.rankList);
|
||||
};
|
||||
|
||||
const getAnnounce = async () => {
|
||||
let res: any = await HomeApi.getAnnounce();
|
||||
state.announceText = res.announcement || [];
|
||||
};
|
||||
|
||||
const jumpTopic = (item: any) => {
|
||||
const queryData = {
|
||||
tid: item.sectionID,
|
||||
name: item.sectionName,
|
||||
};
|
||||
router.push({ path: '/sectionDetails', query: queryData }).then(() => {
|
||||
// location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
state.advList = getAdItem(AdType.MOBILEHOMETOPIC);
|
||||
queryList();
|
||||
queryRanks();
|
||||
getAnnounce();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.homeItem {
|
||||
background: #000;
|
||||
.homeNotice {
|
||||
height: 31px;
|
||||
padding: 0;
|
||||
span {
|
||||
margin-right: 60px;
|
||||
}
|
||||
}
|
||||
.bannerSwiper {
|
||||
height: 162px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.rankSwiper {
|
||||
margin-bottom: 24.36px;
|
||||
}
|
||||
.videoSwiper {
|
||||
margin-bottom: 24.36px;
|
||||
}
|
||||
.topicBox {
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 24px;
|
||||
.topicTitleBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
.topicTitle {
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 18px; /* 112.5% */
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.moreBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.moreIcon {
|
||||
margin-right: 3.43px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
span {
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 13.714px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 16px;
|
||||
grid-column-gap: 12px;
|
||||
}
|
||||
.advBanner {
|
||||
margin-top: 24px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 67px;
|
||||
:deep(img) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.topicBox:last-child {
|
||||
margin-bottom: 29px;
|
||||
}
|
||||
}
|
||||
.showAdvBanner {
|
||||
height: calc(100vh - 160px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<div class="topicDetails">
|
||||
<div class="optBox">
|
||||
<div class="subTitle">{{ state.topicName }}</div>
|
||||
<van-dropdown-menu ref="menuRef" class="dropdownBox" :overlay="false">
|
||||
<van-dropdown-item v-model="state.moduleSort" :options="state.options" @change="changeOpt" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="videoItem" v-for="item in state.list" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
<div class="topicBox" v-for="i in state.topicList" :key="i.id">
|
||||
<div class="topicTitle">{{ i.subModuleName }}</div>
|
||||
<div class="tagList">
|
||||
<div class="tagItem" v-for="item in i.allSection" :key="item.id" @click="jumpTopice(item)">
|
||||
{{ item.name }}
|
||||
<span>({{ item.videoCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { AdType } from '@/enums/app';
|
||||
import insertAdv from '@/utils/insertAdv'
|
||||
import HomeApi from '@/service/home';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
options: [
|
||||
{ text: '默认', value: 1 },
|
||||
{ text: '最新上架', value: 2 },
|
||||
{ text: '最多观看', value: 3 },
|
||||
{ text: '最多收藏', value: 4 },
|
||||
],
|
||||
pageNumber: <any>1,
|
||||
pageSize: 7,
|
||||
moduleSort: route.query.sort || 1,
|
||||
list: <any>[],
|
||||
advList: <any>[],
|
||||
topicList: <any>[],
|
||||
total: 0,
|
||||
topicName: route.query.name,
|
||||
});
|
||||
|
||||
const queryList = async () => {
|
||||
let req = {
|
||||
id: route.query.tid,
|
||||
data: {
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
moduleSort: state.moduleSort,
|
||||
},
|
||||
};
|
||||
let res: any = await HomeApi.getModuleList(req);
|
||||
// const updatedArray = insertAdv({
|
||||
// insertNum: 6,
|
||||
// pageSize,
|
||||
// pageNumber: pageNumber,
|
||||
// originArray: res.videos,
|
||||
// advNum: 5
|
||||
// })
|
||||
let videoList = res.allVideoInfo || [];
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.list = insertAdv(params);
|
||||
// console.log(newarr);
|
||||
// videoList.forEach((i,j) => {
|
||||
// let index = state.pageSize * (state.pageNumber - 1) + j;
|
||||
// })
|
||||
// state.list = res.allVideoInfo || [];
|
||||
state.total = res.total || 0;
|
||||
// console.log(state.list);
|
||||
// allSection.forEach((item: any) => {
|
||||
// // if (item.showType == 2) {
|
||||
// // state.bannerList = state.bannerList.concat(item.allVideoInfo || []);
|
||||
// // } else {
|
||||
// // state.topicsList = state.topicsList.concat(item || []);
|
||||
// // }
|
||||
// // // console.log(state.topicsList, '-=-==-');
|
||||
// });
|
||||
};
|
||||
const changePage = (pageNumber: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
stype: route.query.stype,
|
||||
pageNumber: pageNumber,
|
||||
sort: route.query.sort,
|
||||
name: route.query.name,
|
||||
};
|
||||
router
|
||||
.push({
|
||||
path: '/home',
|
||||
query: queryData,
|
||||
})
|
||||
.then(() => {
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const changeOpt = (val: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
stype: route.query.stype,
|
||||
name: route.query.name,
|
||||
pageNumber: 1,
|
||||
sort: val,
|
||||
};
|
||||
router.replace({
|
||||
path: '/home',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// // location.reload();
|
||||
// });
|
||||
|
||||
queryList();
|
||||
};
|
||||
|
||||
const jumpTopice = (item: any) => {
|
||||
let queryData = {
|
||||
tid: item.id,
|
||||
name: item.name,
|
||||
};
|
||||
router.push({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// location.reload();
|
||||
// });
|
||||
};
|
||||
const getTagList = async () => {
|
||||
const res: any = await HomeApi.getTabs({ isSection: true });
|
||||
state.topicList = res.homePage || [];
|
||||
};
|
||||
const init = () => {
|
||||
state.advList = getAdItem(AdType.HOMETOPBANNER);
|
||||
state.pageNumber = Number(route.query.pageNumber) || 1;
|
||||
state.moduleSort = Number(route.query.sort) || 1;
|
||||
queryList();
|
||||
getTagList();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.topicDetails {
|
||||
padding: 18px 16px 0;
|
||||
box-sizing: border-box;
|
||||
// padding-top: 27px;
|
||||
// box-sizing: border-box;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.optBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 16px; /* 88.889% */
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.videoNum {
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 153.846% */
|
||||
margin-bottom: 16px;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 28px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.topicBox {
|
||||
margin-bottom: 24px;
|
||||
.topicTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px; /* 155.556% */
|
||||
margin-bottom: 12.25px;
|
||||
}
|
||||
.tagList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
.tagItem {
|
||||
padding: 4px 12px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
margin: 0 8px 8px 0;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topicBox:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.dropdownBox {
|
||||
width: 76px;
|
||||
height: 33px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #212121;
|
||||
background: #151515;
|
||||
margin-bottom: 13.6px;
|
||||
// background: skyblue;
|
||||
:deep(.van-dropdown-menu__bar) {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title) {
|
||||
padding: 0;
|
||||
padding-right: 6px;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title--active:after) {
|
||||
border-color: transparent transparent #a0a6b4 #a0a6b4;
|
||||
}
|
||||
:deep(.van-ellipsis) {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.van-dropdown-item) {
|
||||
width: 76px;
|
||||
right: 16px;
|
||||
left: auto;
|
||||
// right: 16px !important;
|
||||
// top: 69px !important;
|
||||
|
||||
.van-dropdown-item__content {
|
||||
border-radius: 5.548px;
|
||||
border: 0.693px solid #212121;
|
||||
background: #151515 !important;
|
||||
padding: 7px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
.van-dropdown-item__option {
|
||||
background: #151515 !important;
|
||||
padding: 0 13.87px;
|
||||
// height: 20px;
|
||||
}
|
||||
.van-cell__title {
|
||||
color: #a0a6b4;
|
||||
font-size: 9.709px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell:after {
|
||||
display: none !important;
|
||||
}
|
||||
.van-cell__value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="welfare">
|
||||
<div class="box1">
|
||||
<div class="img-list">
|
||||
<div class="item-list">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure.slice(0, 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, 10)"
|
||||
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(10, 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, 17)"
|
||||
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(17, 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>
|
||||
<div class="img-list-one">
|
||||
<DecryptImg
|
||||
v-for="(item, index) in state.smallSuure.slice(23, 24)"
|
||||
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="box2">
|
||||
<div class="subTitle">
|
||||
<p v-for="(item, index) in state.bgImage1" :key="index">{{ item }}</p>
|
||||
</div>
|
||||
<swiper
|
||||
ref="gameSwiper"
|
||||
slides-per-view="auto"
|
||||
:observer="true"
|
||||
:navigation="true"
|
||||
:space-between="6"
|
||||
:slidesOffsetBefore="0"
|
||||
class="gameSwiper"
|
||||
>
|
||||
<swiper-slide v-for="i in state.verticalSlide" :key="i.id" class="gameSwiperItem" @click="jumpAdv(i, true)">
|
||||
<DecryptImg :src="i.icon" class="gameSwiperImg" :key="i.id" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
<div class="box3">
|
||||
<div class="subTitle">
|
||||
<p v-for="(item, index) in state.bgImage2" :key="index">{{ item }}</p>
|
||||
</div>
|
||||
<swiper
|
||||
ref="topGameSwiper"
|
||||
slides-per-view="auto"
|
||||
:observer="true"
|
||||
:navigation="true"
|
||||
:space-between="6"
|
||||
:slidesOffsetBefore="-20"
|
||||
class="topGameSwiper"
|
||||
>
|
||||
<swiper-slide
|
||||
v-for="i in state.verticalTwoSlide"
|
||||
:key="i.id"
|
||||
class="topGameSwiperItem"
|
||||
@click="jumpAdv(i, true)"
|
||||
>
|
||||
<DecryptImg :src="i.icon" class="topGameSwiperImg" :key="i.id" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
<swiper
|
||||
ref="topGameSwiper"
|
||||
slides-per-view="auto"
|
||||
:observer="true"
|
||||
:navigation="true"
|
||||
:space-between="6"
|
||||
:slidesOffsetBefore="14"
|
||||
class="topGameSwiper"
|
||||
>
|
||||
<swiper-slide
|
||||
v-for="i in state.smallSqureSlide"
|
||||
:key="i.id"
|
||||
class="topGameSwiperItem"
|
||||
@click="jumpAdv(i, true)"
|
||||
>
|
||||
<DecryptImg :src="i.icon" class="topGameSwiperImg" :key="i.id" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, nextTick } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { jumpAdv } from '@/utils/index';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import { Autoplay, Pagination, FreeMode } from 'swiper';
|
||||
import request from '@/utils/request';
|
||||
import 'swiper/swiper.min.css';
|
||||
import AppApi from '@/service/app';
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
smallSuure: <any>[],
|
||||
verticalSlide: <any>[],
|
||||
bgImage1: <any>[],
|
||||
verticalTwoSlide: <any>[],
|
||||
bgImage2: <any>[],
|
||||
smallSqureSlide: <any>[],
|
||||
isScroll: true,
|
||||
});
|
||||
|
||||
const queryConfig = async () => {
|
||||
try {
|
||||
let res = await AppApi.queryWelfare();
|
||||
const resdata = res.list || []
|
||||
const data = resdata.find((item: any) => item.desc === '福利模块') || {};
|
||||
const dataList = data.comboList || [];
|
||||
state.smallSuure = dataList.find((item: any) => item.displayType === 'smallSuure').list;
|
||||
state.verticalSlide = dataList.find((item: any) => item.displayType === 'verticalSlide').list;
|
||||
// #换行
|
||||
state.bgImage1 = dataList.find((item: any) => item.displayType === 'bgImage1').list[0]?.desc?.split('#') || [];
|
||||
state.verticalTwoSlide = dataList.find((item: any) => item.displayType === 'verticalTwoSlide').list;
|
||||
// #换行
|
||||
state.bgImage2 = dataList.find((item: any) => item.displayType === 'bgImage2').list[0]?.desc?.split('#') || [];
|
||||
state.smallSqureSlide = dataList.find((item: any) => item.displayType === 'smallSqureSlide').list;
|
||||
} catch (error) {
|
||||
console.log();
|
||||
}
|
||||
};
|
||||
|
||||
nextTick(() => {
|
||||
queryConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.welfare {
|
||||
.box1 {
|
||||
width: 100%;
|
||||
height: 425px;
|
||||
background: #fff;
|
||||
.img-list {
|
||||
display: flex;
|
||||
// align-items: center;
|
||||
justify-content: space-between;
|
||||
.item-list:nth-child(2n) {
|
||||
padding-top: 18px !important;
|
||||
}
|
||||
.cover {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin-bottom: 8.4px;
|
||||
}
|
||||
}
|
||||
|
||||
.img-list-one {
|
||||
width: 63.4px;
|
||||
height: 63.4px;
|
||||
margin: 0 auto 52.11px;
|
||||
}
|
||||
.title {
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
line-height: 30px; /* 77.921% */
|
||||
}
|
||||
}
|
||||
.box2 {
|
||||
height: 348px;
|
||||
background: url('../../../assets/images/pc/game_bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-color: #000;
|
||||
padding-top: 79px;
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
line-height: 18.333px; /* 83.333% */
|
||||
margin-bottom: 57.45px;
|
||||
p {
|
||||
text-align: center;
|
||||
margin-bottom: 11.55px;
|
||||
}
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.gameSwiper {
|
||||
.gameSwiperItem {
|
||||
width: 112px;
|
||||
height: 138px;
|
||||
.gameSwiperImg {
|
||||
width: 112px;
|
||||
height: 138px;
|
||||
border-radius: 4.67px;
|
||||
overflow: hidden;
|
||||
:deep(img) {
|
||||
border-radius: 4.67px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.box3 {
|
||||
background-color: #fff;
|
||||
padding-top: 59px;
|
||||
box-sizing: border-box;
|
||||
.subTitle {
|
||||
color: #000;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
line-height: 18.333px; /* 83.333% */
|
||||
padding-bottom: 49px;
|
||||
p {
|
||||
text-align: center;
|
||||
margin-bottom: 11.55px;
|
||||
}
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.topGameSwiper {
|
||||
margin-bottom: 17.4px;
|
||||
.topGameSwiperItem {
|
||||
width: 112px;
|
||||
height: 138px;
|
||||
.topGameSwiperImg {
|
||||
width: 112px;
|
||||
height: 138px;
|
||||
border-radius: 4.67px;
|
||||
overflow: hidden;
|
||||
:deep(img) {
|
||||
border-radius: 4.67px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topGameSwiper:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 48.6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="connectInfo overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">官方联系</div>
|
||||
<div class="connectItem" v-for="(item, index) in contactInfo" :key="index" @click="openPage(item.officialUrl)">
|
||||
<lazyImg class="connectImg" :src="item.officialImg" />
|
||||
<div class="infoBox">
|
||||
<div class="conectName">{{ item.officialName }}</div>
|
||||
<div class="conectDesc">{{ item.officialDesc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
const configStore = useConfigStore();
|
||||
const contactInfo = computed(() => {
|
||||
return configStore.contactInfo;
|
||||
// return route.query.tid ? homeStore.homeData[0];
|
||||
});
|
||||
const openPage = (url: string) => {
|
||||
window.open(url);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.connectInfo {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 31px 16px 90px;
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 205% */
|
||||
padding-bottom: 7px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 31px;
|
||||
}
|
||||
.connectItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 39.5px;
|
||||
.connectImg {
|
||||
width: 47.5px;
|
||||
height: 47.5px;
|
||||
margin-right: 9.4px;
|
||||
}
|
||||
.infoBox {
|
||||
.conectName {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 9.945px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 9.945px; /* 100% */
|
||||
margin-bottom: 5.5px;
|
||||
}
|
||||
.conectDesc {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 7.735px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 9.945px; /* 128.571% */
|
||||
}
|
||||
}
|
||||
}
|
||||
.connectItem:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div class="mobileHome overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<HomeItem v-if="currTab?.showType == 1" :tid="currTab.id" />
|
||||
<TopicDetails v-else-if="currTab?.showType == 2" :tid="currTab.id" :key="currTab.id" />
|
||||
<Welfare v-else-if="currTab?.showType == 4" :tid="currTab.id" />
|
||||
<Classification v-else-if="currTab?.showType == 5" :tid="currTab.id" :key="'calss' + currTab.id" />
|
||||
<MobileBottom />
|
||||
|
||||
<!-- 弹窗广告 -->
|
||||
<HomePopupAdv
|
||||
v-if="isShowAdvPopup"
|
||||
:advList="state.advList"
|
||||
@close="closeHomePopupAdv"
|
||||
/>
|
||||
<!-- <van-popup v-model:show="showTip" overlay-class="tipPop" class="tip18">
|
||||
<div class="tipPopBox">
|
||||
<div class="logo"></div>
|
||||
<div class="tipTitle">欢迎</div>
|
||||
<div class="tipPopLine"></div>
|
||||
<div class="tipPopContent">
|
||||
🔞 您欲觀看的頁面包含成人內容 🔞
|
||||
<br />
|
||||
本站內容均遵守本國法律規定若您是未成年者,
|
||||
<br />
|
||||
請選擇離開,滿 18 歲。
|
||||
<br />
|
||||
点击确认进入。
|
||||
</div>
|
||||
<div class="tipConfirm" @click="confirm">确认进入</div>
|
||||
</div>
|
||||
</van-popup> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useHomeStore } from '@/stores/home';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { AdType } from '@/enums/app';
|
||||
import { useRoute } from 'vue-router';
|
||||
import HomeItem from './components/HomeItem.vue';
|
||||
import TopicDetails from './components/TopicDetails.vue';
|
||||
import Welfare from './components/Welfare.vue';
|
||||
import Classification from './components/Classification.vue';
|
||||
const route = useRoute();
|
||||
const homeStore = useHomeStore();
|
||||
const globalStore = useGlobalStore();
|
||||
// const showTip = computed(() => {
|
||||
// return !globalStore.showTipFirst;
|
||||
// });
|
||||
// 首页弹窗广告
|
||||
const isShowAdvPopup = computed(() => {
|
||||
return state.advList?.length > 0;
|
||||
});
|
||||
const state = reactive({
|
||||
advList: <any>[],
|
||||
});
|
||||
|
||||
const currTab = computed(() => {
|
||||
if (route.query.tid) {
|
||||
// let tabObj = homeStore.homeData.filter((i) => {
|
||||
// return i.id == route.query.tid;
|
||||
// });
|
||||
const tabObj = {
|
||||
id: route.query.tid,
|
||||
showType: route.query.stype,
|
||||
name: route.query.name,
|
||||
};
|
||||
|
||||
return tabObj;
|
||||
} else {
|
||||
return homeStore.homeData[0];
|
||||
}
|
||||
// return route.query.tid ? homeStore.homeData[0];
|
||||
});
|
||||
|
||||
const confirm = () => {
|
||||
globalStore.setShowTipFirst();
|
||||
};
|
||||
|
||||
const closeHomePopupAdv = () => {
|
||||
globalStore.setHomeAdvFirst();
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
state.advList = getAdItem(AdType.HOMEPOPUPADV);
|
||||
state.advList.reverse();
|
||||
console.log(state.advList, 9999);
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobileHome {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
// .navTab {
|
||||
// :deep(.van-tabs__line) {
|
||||
// display: none;
|
||||
// }
|
||||
// .tabTitle {
|
||||
// font-size: 16px;
|
||||
// }
|
||||
// :deep(.van-tab--active) {
|
||||
// .tabTitle {
|
||||
// font-size: 18px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.tipPop {
|
||||
// opacity: 0.6;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
.tip18 {
|
||||
.tipPopBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.logo {
|
||||
width: 130px;
|
||||
height: 84px;
|
||||
background: url('../../assets/images/mobile/logo.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.tipTitle {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 30px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 136.667% */
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tipPopLine {
|
||||
width: 302px;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.tipPopContent {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 28px; /* 200% */
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
.tipConfirm {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 70.8px;
|
||||
height: 30.2px;
|
||||
border-radius: 6px;
|
||||
background: #ffc400;
|
||||
box-shadow: 0px -1.8px 4.8px 0px rgba(249, 255, 139, 0.6) inset, 0px 0px 4.8px 0px rgba(255, 196, 0, 0.5);
|
||||
color: #482f28;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 10.5px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 10.5px; /* 100% */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div class="connectInfo overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">商务合作</div>
|
||||
<div
|
||||
class="connectItem"
|
||||
v-for="(item, index) in state.contactInfo"
|
||||
:key="index"
|
||||
@click="openPage(item.officialUrl)"
|
||||
>
|
||||
<lazyImg class="connectImg" :src="item.officialImg" />
|
||||
<div class="conectName">{{ item.officialName }}:</div>
|
||||
<div class="conectDesc">点击这里</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
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) {
|
||||
// return false;
|
||||
}
|
||||
};
|
||||
queryContactInfo();
|
||||
const openPage = (url: string) => {
|
||||
window.open(url);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.connectInfo {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 31px 16px 90px;
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 205% */
|
||||
padding-bottom: 7px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 31px;
|
||||
}
|
||||
.connectItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 39.5px;
|
||||
.connectImg {
|
||||
width: 47.5px;
|
||||
height: 47.5px;
|
||||
margin-right: 9.4px;
|
||||
}
|
||||
.conectName {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 10.047px; /* 71.761% */
|
||||
}
|
||||
.conectDesc {
|
||||
color: var(--Style, #ffcd3f);
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 10.047px;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-skip-ink: auto;
|
||||
text-decoration-thickness: auto;
|
||||
text-underline-offset: auto;
|
||||
text-underline-position: from-font;
|
||||
}
|
||||
}
|
||||
.connectItem:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="collect overflow-y-auto" ref="contentRef">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">观看历史</div>
|
||||
<div class="btnBox">
|
||||
<div class="cancelBtn" v-if="state.isEdit" @click="cancelEdit">取消</div>
|
||||
<div class="editBtn" :class="{ disableEdit: !state.delList.length }" v-if="state.isEdit" @click="delVideo">
|
||||
删除
|
||||
</div>
|
||||
<div class="editBtn" @click="edit" v-else>编辑</div>
|
||||
</div>
|
||||
<div class="allSelectBox" v-if="state.isEdit" @click="allSelect">
|
||||
<SvgIcon name="selectedIcon" class="selectedIcon" v-if="state.delList.length == state.historyList.length" />
|
||||
<div class="selectIcon" v-else></div>
|
||||
全选
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="mobileVideoBox" v-for="item in state.historyList" :key="item.id">
|
||||
<MobileVideoBox :videoInfo="item" />
|
||||
<div class="videoMask" v-if="state.isEdit" @click.stop="selectVideo(item)">
|
||||
<SvgIcon name="selectedIcon" class="selected" v-if="handleSelectSta(item.id)" />
|
||||
<div class="select" v-else></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import { queryHistoryVideo, queryHistoryTotal, delAllData } from '@/utils';
|
||||
import { showToast } from 'vant';
|
||||
const globalStore = useGlobalStore();
|
||||
const contentRef = ref();
|
||||
const state = reactive({
|
||||
isEdit: false,
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
historyList: <any>[],
|
||||
delList: <any>[],
|
||||
});
|
||||
|
||||
const edit = () => {
|
||||
state.isEdit = true;
|
||||
};
|
||||
const cancelEdit = () => {
|
||||
state.isEdit = false;
|
||||
state.delList = [];
|
||||
};
|
||||
|
||||
const selectVideo = (item: any) => {
|
||||
let index = state.delList.findIndex((i: any) => {
|
||||
return item.id == i;
|
||||
});
|
||||
if (index != -1) {
|
||||
state.delList.splice(index, 1);
|
||||
} else {
|
||||
state.delList.push(item.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectSta = (id: any) => {
|
||||
let result = state.delList.some((i: any) => {
|
||||
return i == id;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const allSelect = () => {
|
||||
if (state.delList.length) {
|
||||
state.delList = [];
|
||||
} else {
|
||||
let arr: any = [];
|
||||
state.historyList.forEach((item: any) => {
|
||||
arr.push(item.id);
|
||||
});
|
||||
state.delList = arr;
|
||||
}
|
||||
};
|
||||
|
||||
const delVideo = async () => {
|
||||
const res: any = await delAllData(HistoryVideoType.VIDEO, state.delList);
|
||||
getHistoryList();
|
||||
state.isEdit = false;
|
||||
state.delList = [];
|
||||
showToast('删除成功');
|
||||
};
|
||||
|
||||
const changePage = (pageNumber: number) => {
|
||||
state.pageNumber = pageNumber;
|
||||
contentRef.value.scrollTo({ top: 0 });
|
||||
getHistoryList();
|
||||
};
|
||||
|
||||
const getHistoryList = async () => {
|
||||
const list: any = await queryHistoryVideo(state.pageNumber, state.pageSize, HistoryVideoType.VIDEO);
|
||||
const total: any = await queryHistoryTotal(HistoryVideoType.VIDEO);
|
||||
state.total = total;
|
||||
state.historyList = list;
|
||||
};
|
||||
getHistoryList();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.collect {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 26px 16px 150px;
|
||||
.subTitle {
|
||||
display: flex;
|
||||
width: 80.323px;
|
||||
height: 41px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.btnBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 24px;
|
||||
.cancelBtn {
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ffc303;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
color: #ffcd3f;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
.editBtn {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
width: 72px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(270deg, #ffc303 0%, #ffdd01 100%);
|
||||
}
|
||||
.disableEdit {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
.allSelectBox {
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
font-size: 16px;
|
||||
.selectIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
border: 2.2px solid #999;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.selectedIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 16px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 45px;
|
||||
.mobileVideoBox {
|
||||
position: relative;
|
||||
.videoMask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
.select {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 2.2px solid #999;
|
||||
}
|
||||
.selected {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
<div class="search overflow-y-auto" ref="searchRef">
|
||||
<MobileAdvBanner
|
||||
class="advBanner"
|
||||
:list="state.advList"
|
||||
v-if="showAdvBanner && state.advList && state.advList.length"
|
||||
/>
|
||||
<div class="contentBox">
|
||||
<van-dropdown-menu ref="menuRef" class="dropdownBox" :overlay="false">
|
||||
<van-dropdown-item v-model="state.activeOpt" :options="state.options" @change="changeOpt" />
|
||||
</van-dropdown-menu>
|
||||
<div class="searchBox">
|
||||
<van-field v-model="state.searchValue" :border="false" class="searchInput" />
|
||||
<div class="searchBtn" @click="search()">搜索一下</div>
|
||||
</div>
|
||||
<div class="hotSearch">
|
||||
<div class="hotSearchItem" v-for="i in state.hotKeywords.slice(0, 10)" :key="i" @click="search(i.name)">
|
||||
{{ i.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="result" v-if="state.isSearch">
|
||||
<div class="subTitle">搜索结果</div>
|
||||
<div class="videoNum">
|
||||
全部视频
|
||||
<span>({{ state.total }})</span>
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="videoItem" v-for="item in state.resultList" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recommend" v-else>
|
||||
<div class="subTitle">大家都在搜</div>
|
||||
<div class="videoList">
|
||||
<div class="videoItem" v-for="item in state.recommendList" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:total="state.total"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
v-if="state.total > state.pageSize && state.isSearch"
|
||||
@changePage="changePage"
|
||||
/>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed, ref } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { AdType } from '@/enums/app';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import CommunityApi from '@/service/community';
|
||||
import insertAdv from '@/utils/insertAdv';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const globalStore = useGlobalStore();
|
||||
const searchRef = ref();
|
||||
const state = reactive({
|
||||
options: [
|
||||
{ text: '默认', value: undefined },
|
||||
{ text: '最新上架', value: 0 },
|
||||
{ text: '最多观看', value: 2 },
|
||||
{ text: '最多收藏', value: 3 },
|
||||
],
|
||||
activeOpt: undefined,
|
||||
searchValue: '',
|
||||
hotKeywords: <any>[],
|
||||
advList: <any>[],
|
||||
resultList: <any>[],
|
||||
recommendList: <any>[],
|
||||
pageSize: 7,
|
||||
pageNumber: 1,
|
||||
total: 0,
|
||||
isSearch: false,
|
||||
});
|
||||
|
||||
const showAdvBanner = computed(() => {
|
||||
return globalStore.showAdvBanner;
|
||||
});
|
||||
|
||||
const changeOpt = (val: any) => {
|
||||
search();
|
||||
};
|
||||
|
||||
const search = async (value?: string, isChangePage?: boolean) => {
|
||||
state.isSearch = true;
|
||||
if (value) {
|
||||
state.searchValue = value;
|
||||
}
|
||||
if (!isChangePage) {
|
||||
state.pageNumber = 1;
|
||||
}
|
||||
let req = {
|
||||
SortV: state.activeOpt,
|
||||
keyWords: [state.searchValue],
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
realm: 'video',
|
||||
};
|
||||
const res: any = await CommunityApi.searchPost(req);
|
||||
let videoList = res.list || [];
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.resultList = insertAdv(params);
|
||||
// state.resultList = res.list || [];
|
||||
state.total = res.total || 0;
|
||||
};
|
||||
|
||||
const queryRecommend = async () => {
|
||||
try {
|
||||
const res: any = await CommunityApi.searchVideo({});
|
||||
let videoList = res.videos || [];
|
||||
console.log(videoList);
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: videoList.length,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.recommendList = insertAdv(params);
|
||||
// state.recommendList = res.videos || [];
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const changePage = (pageNumber: number) => {
|
||||
state.pageNumber = pageNumber;
|
||||
searchRef.value.scrollTo({ top: 0 });
|
||||
search(undefined, true);
|
||||
};
|
||||
const getHotTag = async () => {
|
||||
const res: any = await CommunityApi.getHotRecommend({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
state.hotKeywords = res.list || [];
|
||||
};
|
||||
const init = () => {
|
||||
state.advList = getAdItem(AdType.HOMETOPBANNER);
|
||||
getHotTag();
|
||||
queryRecommend();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.search {
|
||||
height: 100vh;
|
||||
// padding-top: 27px;
|
||||
// box-sizing: border-box;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 27px 16px 0;
|
||||
box-sizing: border-box;
|
||||
.searchBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
.searchInput {
|
||||
width: 250px;
|
||||
height: 42px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #ffc400;
|
||||
background: #000;
|
||||
padding: 0 14.85px;
|
||||
font-size: 14px;
|
||||
|
||||
:deep(.van-field__control) {
|
||||
color: #fff;
|
||||
}
|
||||
:deep(.van-cell__value) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.searchBtn {
|
||||
width: 78.7px;
|
||||
height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6.986px;
|
||||
background: #ffc400;
|
||||
box-shadow: 0px 0.873px 1.747px 0px rgba(0, 0, 0, 0.05);
|
||||
color: #482f28;
|
||||
font-size: 12.226px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 12.226px;
|
||||
}
|
||||
}
|
||||
.hotSearch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 17.1px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 50.58px;
|
||||
.hotSearchItem {
|
||||
margin: 0 7px;
|
||||
height: 21px;
|
||||
color: #e5e7eb;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 13.972px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20.958px; /* 150% */
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-skip-ink: auto;
|
||||
text-decoration-thickness: auto;
|
||||
text-underline-offset: auto;
|
||||
text-underline-position: from-font;
|
||||
}
|
||||
}
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 16px; /* 88.889% */
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.videoNum {
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 153.846% */
|
||||
margin-bottom: 16px;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 28px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
.dropdownBox {
|
||||
width: 76px;
|
||||
height: 33px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #212121;
|
||||
background: #151515;
|
||||
margin-bottom: 13.6px;
|
||||
// background: skyblue;
|
||||
:deep(.van-dropdown-menu__bar) {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title) {
|
||||
padding: 0;
|
||||
padding-right: 6px;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title--active:after) {
|
||||
border-color: transparent transparent #a0a6b4 #a0a6b4;
|
||||
}
|
||||
:deep(.van-ellipsis) {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.van-dropdown-item) {
|
||||
width: 76px;
|
||||
left: 16px;
|
||||
// top: 69px !important;
|
||||
|
||||
.van-dropdown-item__content {
|
||||
border-radius: 5.548px;
|
||||
border: 0.693px solid #212121;
|
||||
background: #151515 !important;
|
||||
padding: 7px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
.van-dropdown-item__option {
|
||||
background: #151515 !important;
|
||||
padding: 0 13.87px;
|
||||
// height: 20px;
|
||||
}
|
||||
.van-cell__title {
|
||||
color: #a0a6b4;
|
||||
font-size: 9.709px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell:after {
|
||||
display: none !important;
|
||||
}
|
||||
.van-cell__value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="privacyPolicy overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="title">USC 2257</div>
|
||||
<div class="contentText">
|
||||
DHP.Video 不是网站 (DHP.Video) 上任何和所有内容的制作者(主要或次要)。关于根据 18 USC 2257
|
||||
记录本网站上的任何和所有内容,请将您的请求直接发送给制作内容的网站。 DHP.Video
|
||||
是一个视频共享网站,允许上传、共享和观看各种类型的成人内容。虽然 DHP.Video 尽最大努力验证合规性,但它可能不是
|
||||
100% 准确。 DHP.Video 遵守以下程序以确保合规:
|
||||
<p>
|
||||
<span>1.</span>
|
||||
要求所有用户必须年满 18 岁才能观看和上传视频。
|
||||
</p>
|
||||
<p>
|
||||
<span>2.</span>
|
||||
上传时,用户必须核实内容;保证自己已年满 18 岁;证明自己保留了内容中模特的记录并且这些模特已年满 18 岁。
|
||||
</p>
|
||||
如需进一步帮助和/或信息以查找内容的原始站点,请通过支持表格联系 DHP.Video 合规部门。 DHP.Video
|
||||
允许将内容标记为不当内容。如果任何内容被标记为非法、违法、骚扰、有害、冒犯或其他各种原因,DHP.Video
|
||||
应立即将其从网站上删除。
|
||||
</div>
|
||||
</div>
|
||||
<!-- <HomeItem /> -->
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import HomeItem from '../components/HomeItem.vue';
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.privacyPolicy {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 31.2px 24.5px 50px;
|
||||
.title {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 205% */
|
||||
padding-bottom: 7px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.contentText {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 171.429% */
|
||||
p {
|
||||
display: flex;
|
||||
span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="releaseAddress overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">备用地址</div>
|
||||
<div class="urlItem" v-for="(item, index) in urlList" :key="index">
|
||||
<div class="urlTitle">{{ item.desc }}: </div>
|
||||
<div class="url" @click="openPage(item.url)">{{ item.url }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
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 scoped lang="scss">
|
||||
.releaseAddress {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 31px 16px 200px;
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 205% */
|
||||
padding-bottom: 7px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.urlItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 56px;
|
||||
.urlTitle {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 10.047px; /* 71.761% */
|
||||
}
|
||||
.url {
|
||||
color: var(--Style, #ffcd3f);
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 10.047px;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-skip-ink: auto;
|
||||
text-decoration-thickness: auto;
|
||||
text-underline-offset: auto;
|
||||
text-underline-position: from-font;
|
||||
}
|
||||
}
|
||||
.urlItem:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div class="mobileHome overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="topicDetails">
|
||||
<div class="optBox">
|
||||
<div class="subTitle">{{ state.topicName }}</div>
|
||||
<van-dropdown-menu ref="menuRef" class="dropdownBox" :overlay="false">
|
||||
<van-dropdown-item v-model="state.moduleSort" :options="state.options" @change="changeOpt" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="videoItem" v-for="item in state.list" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
<!-- <MobileVideoBox :videoInfo="item" v-for="item in state.list" :key="item.id" /> -->
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
<div class="topicBox" v-for="i in state.topicList" :key="i.id">
|
||||
<div class="topicTitle">{{ i.subModuleName }}</div>
|
||||
<div class="tagList">
|
||||
<div class="tagItem" v-for="item in i.allSection" :key="item.id" @click="jumpTopice(item)">
|
||||
{{ item.name }}
|
||||
<span>({{ item.videoCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import insertAdv from '@/utils/insertAdv';
|
||||
import VideoApi from '@/service/video';
|
||||
import HomeApi from '@/service/home';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
options: [
|
||||
{ text: '默认', value: undefined },
|
||||
{ text: '最新上架', value: 'new' },
|
||||
{ text: '最多观看', value: 'watch' },
|
||||
{ text: '最多收藏', value: 'like' },
|
||||
],
|
||||
pageNumber: <any>1,
|
||||
pageSize: 7,
|
||||
moduleSort: route.query.sort || undefined,
|
||||
list: <any>[],
|
||||
topicList: <any>[],
|
||||
total: 0,
|
||||
topicName: route.query.name,
|
||||
});
|
||||
|
||||
const queryList = async () => {
|
||||
let req = {
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
sortType: state.moduleSort,
|
||||
playTimeType: 1,
|
||||
};
|
||||
const id = route.query.tid as string;
|
||||
const res: any = await HomeApi.getVidSectionList(id, req);
|
||||
|
||||
let videoList = res.videos || [];
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.list = insertAdv(params);
|
||||
// state.list = res.videos || [];
|
||||
state.total = res.total || 0;
|
||||
};
|
||||
const changePage = (pageNumber: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
pageNumber: pageNumber,
|
||||
sort: route.query.sort,
|
||||
name: route.query.name,
|
||||
};
|
||||
router
|
||||
.push({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
})
|
||||
.then(() => {
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const changeOpt = (val: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
name: route.query.name,
|
||||
pageNumber: 1,
|
||||
sort: val,
|
||||
};
|
||||
router.replace({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// // location.reload();
|
||||
// });
|
||||
|
||||
queryList();
|
||||
};
|
||||
|
||||
const jumpTopice = (item: any) => {
|
||||
let queryData = {
|
||||
tid: item.id,
|
||||
stype: 2,
|
||||
pageNumber: 1,
|
||||
sort: 'new',
|
||||
name: item.name,
|
||||
};
|
||||
router
|
||||
.push({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
})
|
||||
.then(() => {
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
const getTagList = async () => {
|
||||
const res: any = await HomeApi.getTabs({ isSection: true });
|
||||
state.topicList = res.homePage || [];
|
||||
};
|
||||
const init = () => {
|
||||
state.pageNumber = Number(route.query.pageNumber) || 1;
|
||||
state.moduleSort = route.query.sort || 'new';
|
||||
queryList();
|
||||
getTagList();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobileHome {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.topicDetails {
|
||||
padding: 18px 16px 0;
|
||||
box-sizing: border-box;
|
||||
// padding-top: 27px;
|
||||
// box-sizing: border-box;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.optBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 16px; /* 88.889% */
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.videoNum {
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 153.846% */
|
||||
margin-bottom: 16px;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 28px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.topicBox {
|
||||
margin-bottom: 24px;
|
||||
.topicTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px; /* 155.556% */
|
||||
margin-bottom: 12.25px;
|
||||
}
|
||||
.tagList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
.tagItem {
|
||||
padding: 4px 12px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
margin: 0 8px 8px 0;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topicBox:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.dropdownBox {
|
||||
width: 76px;
|
||||
height: 33px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #212121;
|
||||
background: #151515;
|
||||
margin-bottom: 13.6px;
|
||||
// background: skyblue;
|
||||
:deep(.van-dropdown-menu__bar) {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title) {
|
||||
padding: 0;
|
||||
padding-right: 6px;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title--active:after) {
|
||||
border-color: transparent transparent #a0a6b4 #a0a6b4;
|
||||
}
|
||||
:deep(.van-ellipsis) {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.van-dropdown-item) {
|
||||
width: 76px;
|
||||
right: 16px;
|
||||
left: auto;
|
||||
// right: 16px !important;
|
||||
// top: 69px !important;
|
||||
|
||||
.van-dropdown-item__content {
|
||||
border-radius: 5.548px;
|
||||
border: 0.693px solid #212121;
|
||||
background: #151515 !important;
|
||||
padding: 7px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
.van-dropdown-item__option {
|
||||
background: #151515 !important;
|
||||
padding: 0 13.87px;
|
||||
// height: 20px;
|
||||
}
|
||||
.van-cell__title {
|
||||
color: #a0a6b4;
|
||||
font-size: 9.709px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell:after {
|
||||
display: none !important;
|
||||
}
|
||||
.van-cell__value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="mobileHome overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="topicDetails">
|
||||
<div class="optBox">
|
||||
<div class="subTitle">{{ state.topicName }}</div>
|
||||
<van-dropdown-menu ref="menuRef" class="dropdownBox" :overlay="false">
|
||||
<van-dropdown-item v-model="state.moduleSort" :options="state.options" @change="changeOpt" />
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
<div class="videoList">
|
||||
<div class="videoItem" v-for="item in state.list" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
<div class="topicBox" v-for="i in state.topicList" :key="i.id">
|
||||
<div class="topicTitle">{{ i.subModuleName }}</div>
|
||||
<div class="tagList">
|
||||
<div class="tagItem" v-for="item in i.allSection" :key="item.id" @click="jumpTopice(item)">
|
||||
{{ item.name }}
|
||||
<span>({{ item.videoCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import VideoApi from '@/service/video';
|
||||
import HomeApi from '@/service/home';
|
||||
import insertAdv from '@/utils/insertAdv';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
options: [
|
||||
{ text: '默认', value: undefined },
|
||||
{ text: '最新上架', value: 0 },
|
||||
{ text: '最多观看', value: 1 },
|
||||
{ text: '最多收藏', value: 2 },
|
||||
],
|
||||
pageNumber: <any>1,
|
||||
pageSize: 7,
|
||||
moduleSort: route.query.sort || undefined,
|
||||
list: <any>[],
|
||||
topicList: <any>[],
|
||||
total: 0,
|
||||
topicName: route.query.name,
|
||||
});
|
||||
|
||||
const queryList = async () => {
|
||||
let req = {
|
||||
tagID: route.query.tid,
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
sortType: state.moduleSort,
|
||||
};
|
||||
const res: any = await VideoApi.searchTagPost(req);
|
||||
let videoList = res.list || [];
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.list = insertAdv(params);
|
||||
// state.list = res.list || [];
|
||||
state.total = res.total || 0;
|
||||
};
|
||||
const changePage = (pageNumber: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
pageNumber: pageNumber,
|
||||
sort: route.query.sort,
|
||||
name: route.query.name,
|
||||
};
|
||||
router.push({
|
||||
path: '/tagDetails',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// location.reload();
|
||||
// });
|
||||
};
|
||||
|
||||
const changeOpt = (val: any) => {
|
||||
let queryData = {
|
||||
tid: route.query.tid,
|
||||
name: route.query.name,
|
||||
pageNumber: 1,
|
||||
sort: val,
|
||||
};
|
||||
router.replace({
|
||||
path: '/tagDetails',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// // location.reload();
|
||||
// });
|
||||
|
||||
queryList();
|
||||
};
|
||||
|
||||
const jumpTopice = (item: any) => {
|
||||
let queryData = {
|
||||
tid: item.id,
|
||||
name: item.name,
|
||||
};
|
||||
router.push({
|
||||
path: '/sectionDetails',
|
||||
query: queryData,
|
||||
});
|
||||
// .then(() => {
|
||||
// location.reload();
|
||||
// });
|
||||
};
|
||||
const getTagList = async () => {
|
||||
const res: any = await HomeApi.getTabs({ isSection: true });
|
||||
state.topicList = res.homePage || [];
|
||||
};
|
||||
const init = () => {
|
||||
state.pageNumber = Number(route.query.pageNumber) || 1;
|
||||
state.moduleSort = Number(route.query.sort) || 1;
|
||||
queryList();
|
||||
getTagList();
|
||||
};
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobileHome {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.topicDetails {
|
||||
padding: 18px 16px 0;
|
||||
box-sizing: border-box;
|
||||
// padding-top: 27px;
|
||||
// box-sizing: border-box;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.optBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 16px; /* 88.889% */
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.videoNum {
|
||||
color: #fff;
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 153.846% */
|
||||
margin-bottom: 16px;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.videoList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 28px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.topicBox {
|
||||
margin-bottom: 24px;
|
||||
.topicTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px; /* 155.556% */
|
||||
margin-bottom: 12.25px;
|
||||
}
|
||||
.tagList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
.tagItem {
|
||||
padding: 4px 12px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
border-radius: 8px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
margin: 0 8px 8px 0;
|
||||
span {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topicBox:last-child {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.dropdownBox {
|
||||
width: 76px;
|
||||
height: 33px;
|
||||
border-radius: 6.986px;
|
||||
border: 0.873px solid #212121;
|
||||
background: #151515;
|
||||
margin-bottom: 13.6px;
|
||||
// background: skyblue;
|
||||
:deep(.van-dropdown-menu__bar) {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title) {
|
||||
padding: 0;
|
||||
padding-right: 6px;
|
||||
}
|
||||
:deep(.van-dropdown-menu__title--active:after) {
|
||||
border-color: transparent transparent #a0a6b4 #a0a6b4;
|
||||
}
|
||||
:deep(.van-ellipsis) {
|
||||
color: #a0a6b4;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.van-dropdown-item) {
|
||||
width: 76px;
|
||||
right: 16px;
|
||||
left: auto;
|
||||
// right: 16px !important;
|
||||
// top: 69px !important;
|
||||
|
||||
.van-dropdown-item__content {
|
||||
border-radius: 5.548px;
|
||||
border: 0.693px solid #212121;
|
||||
background: #151515 !important;
|
||||
padding: 7px 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
.van-dropdown-item__option {
|
||||
background: #151515 !important;
|
||||
padding: 0 13.87px;
|
||||
// height: 20px;
|
||||
}
|
||||
.van-cell__title {
|
||||
color: #a0a6b4;
|
||||
font-size: 9.709px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-cell:after {
|
||||
display: none !important;
|
||||
}
|
||||
.van-cell__value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="userHomepage overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="contentBox">
|
||||
<div class="subTitle">編輯個人資料</div>
|
||||
<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>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { showToast } from 'vant';
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import UserApi from '@/service/user';
|
||||
const userStore = 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 (state.password && !validatePassword(state.password)) {
|
||||
showToast('密码格式有误');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await UserApi.editPersonInfo({ name: state.name, passWord: state.password });
|
||||
showToast('设置成功');
|
||||
userInfo.value.name = state.name;
|
||||
userStore.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">
|
||||
.userHomepage {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 24px 16px 40px;
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 41px; /* 205% */
|
||||
padding-bottom: 7px;
|
||||
// border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
// margin-bottom: 35px;
|
||||
}
|
||||
.input-box {
|
||||
width: 100%;
|
||||
margin-top: 35px;
|
||||
height: 56px;
|
||||
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.4);
|
||||
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,352 @@
|
||||
<template>
|
||||
<div class="videoDetails overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<div class="video">
|
||||
<HorizontalVideo v-if="state.videoInfo.id" :videoInfo="state.videoInfo" ref="verticalVideoRef" />
|
||||
</div>
|
||||
<div class="contentBox">
|
||||
<div class="videoTitle ellipsis">{{ state.videoInfo.title }}</div>
|
||||
<div class="videoInfo">
|
||||
<div class="rankBox" v-if="state.rankNum">
|
||||
<div class="rank">TOP {{ state.rankNum }}</div>
|
||||
<div class="rankTitle">热播榜</div>
|
||||
<div class="infoLine"></div>
|
||||
</div>
|
||||
<!-- <div class="palyerCount">
|
||||
{{ watchCount(state.videoInfo.playCount) }}
|
||||
<div class="infoLine"></div>
|
||||
</div> -->
|
||||
<!-- <div class="palyerTime">{{ fromNowTime(state.videoInfo.createdAt) }}</div> -->
|
||||
</div>
|
||||
<div class="tagBox">
|
||||
<div class="tagList" v-if="state.videoInfo.tags && state.videoInfo.tags.length">
|
||||
<div
|
||||
class="tagItem"
|
||||
v-for="item in state.videoInfo.tags.slice(0, 4)"
|
||||
:key="item.id"
|
||||
@click.stop="jumpTag(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="likeBox" @click="collect(state.videoInfo.vidStatus?.hasCollected)">
|
||||
<SvgIcon :name="state.videoInfo.vidStatus?.hasCollected ? 'likeedIcon' : 'likeIcon'" class="likeIcon" />
|
||||
{{ watchCount(state.videoInfo.collectCount || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <BannerSwiper :list="state.advList" type="adv" class="bannerSwiper" /> -->
|
||||
<div class="bannerBox" v-if="state.advList && state.advList.length">
|
||||
<DecryptImg class="bannerItem" :src="item.cover" v-for="item in state.advList.slice(0, 2)" :key="item.id" />
|
||||
</div>
|
||||
<div class="subTitle">大家都在看💦</div>
|
||||
<div class="recommendList">
|
||||
<!-- <MobileVideoBox class="recommendItem" v-for="i in state.recommendList" :videoInfo="i" :key="i.id" /> -->
|
||||
<div class="recommendItem" v-for="item in state.recommendList" :key="item.id">
|
||||
<AdvBox :advInfo="item" v-if="item.isAdv" />
|
||||
<MobileVideoBox :videoInfo="item" v-else />
|
||||
</div>
|
||||
</div>
|
||||
<MobilePagination
|
||||
class="pagination"
|
||||
:pageNumber="state.pageNumber"
|
||||
:pageSize="state.pageSize"
|
||||
:total="state.total"
|
||||
@changePage="changePage"
|
||||
v-if="state.total > state.pageSize"
|
||||
/>
|
||||
</div>
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, nextTick } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getAdItem } from '@/utils/getConfig';
|
||||
import { AdType } from '@/enums/app';
|
||||
import { watchCount, fromNowTime } from '@/utils/index';
|
||||
import VideoApi from '@/service/video';
|
||||
import MineApi from '@/service/mine';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { eventBus } from '@/utils/eventBus';
|
||||
import insertAdv from '@/utils/insertAdv';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({
|
||||
advList: <any>[],
|
||||
videoInfo: <any>{},
|
||||
rankNum: route.query.rankNum,
|
||||
pageNumber: 1,
|
||||
pageSize: 7,
|
||||
total: 0,
|
||||
recommendList: <any>[],
|
||||
userInfo: <Types.User.UserInfoType>{},
|
||||
});
|
||||
|
||||
const queryVideoInfo = async () => {
|
||||
try {
|
||||
const req = {
|
||||
videoID: route.query.vid,
|
||||
};
|
||||
const res: any = await VideoApi.queryVideoInfo(req);
|
||||
state.videoInfo = res;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const getRecommendList = async () => {
|
||||
try {
|
||||
const req = {
|
||||
id: route.query.vid,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
};
|
||||
const res: any = await VideoApi.queryvidPlay(req);
|
||||
let videoList = res.videos || [];
|
||||
let params = {
|
||||
insertNum: 6,
|
||||
pageSize: state.pageSize,
|
||||
pageNumber: state.pageNumber,
|
||||
advNum: 6,
|
||||
originArray: videoList,
|
||||
};
|
||||
state.recommendList = insertAdv(params);
|
||||
// state.recommendList = res.videos || [];
|
||||
state.total = res.total;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
nextTick(async () => {
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
state.userInfo = userStore.userInfo;
|
||||
});
|
||||
const collect = async (isCollect: boolean) => {
|
||||
if (!state.userInfo.account) {
|
||||
eventBus.emit('SHOW_LOGIN');
|
||||
return;
|
||||
}
|
||||
await MineApi.mineCollect({
|
||||
isCollect: !isCollect,
|
||||
objID: state.videoInfo.id,
|
||||
type: 'video',
|
||||
});
|
||||
if (isCollect) {
|
||||
state.videoInfo.vidStatus.hasCollected = false;
|
||||
if (state.videoInfo.collectCount > 0) {
|
||||
state.videoInfo.collectCount--;
|
||||
}
|
||||
showToast('取消收藏成功');
|
||||
} else {
|
||||
state.videoInfo.vidStatus.hasCollected = true;
|
||||
state.videoInfo.collectCount++;
|
||||
showToast('收藏成功');
|
||||
}
|
||||
};
|
||||
|
||||
const changePage = (pageNumber: number) => {
|
||||
state.pageNumber = pageNumber;
|
||||
getRecommendList();
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
state.advList = getAdItem(AdType.VIDEODETAILSADV);
|
||||
queryVideoInfo();
|
||||
getRecommendList();
|
||||
};
|
||||
init();
|
||||
|
||||
const jumpTag = (item: any) => {
|
||||
router.push({
|
||||
path: '/tagDetails',
|
||||
query: {
|
||||
tid: item.id,
|
||||
name: item.name,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.videoDetails {
|
||||
height: 100vh;
|
||||
.advBanner {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
.video {
|
||||
width: 387px;
|
||||
height: 218px;
|
||||
margin-bottom: 38.5px;
|
||||
}
|
||||
.contentBox {
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
.videoTitle {
|
||||
font-size: 19.231px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 23.077px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.videoInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
.rankBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.rank {
|
||||
height: 17.3px;
|
||||
padding: 0 3.85px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 1.923px 0px 0px 1.923px;
|
||||
background: linear-gradient(270deg, #ffc303 0%, #ffdd01 100%);
|
||||
color: #111319;
|
||||
font-size: 11.538px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.rankTitle {
|
||||
height: 17.3px;
|
||||
padding: 0 3.85px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 1.923px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
font-size: 11.538px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.palyerCount {
|
||||
color: #ececec;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 13.462px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 17.308px; /* 128.571% */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.palyerTime {
|
||||
color: #ececec;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 100% */
|
||||
}
|
||||
.infoLine {
|
||||
width: 1px;
|
||||
height: 9.6px;
|
||||
margin: 0 5.77px;
|
||||
background: #2d2f34;
|
||||
}
|
||||
}
|
||||
.tagBox {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
.tagList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.tagItem {
|
||||
height: 17.8px;
|
||||
padding: 0 3.85px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 9px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 9px; /* 100% */
|
||||
border-radius: 2px;
|
||||
border: 1px solid #212121;
|
||||
background: #151515;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bannerBox {
|
||||
width: 100%;
|
||||
margin-bottom: 25.75px;
|
||||
.bannerItem {
|
||||
width: 100%;
|
||||
height: 71.25px;
|
||||
margin-bottom: 11.75px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
:deep(img) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.bannerItem:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.likeBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #a9a9ac;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
.likeIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
.bannerSwiper {
|
||||
height: 58.5px;
|
||||
width: 100%;
|
||||
margin-bottom: 25px;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
:deep(.name) {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
:deep(img) {
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
.subTitle {
|
||||
color: #fff;
|
||||
font-family: 'PingFang SC';
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 18px; /* 112.5% */
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.recommendList {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 16px;
|
||||
grid-column-gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 72px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="welfare overflow-y-auto">
|
||||
<MobileHeader class="header" />
|
||||
<MobileBottom />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts"></script>
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useGlobalStore } from '@/stores/global';
|
||||
const globalStore = useGlobalStore();
|
||||
const state = reactive({});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.welfare {
|
||||
height: 100vh;
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="img-decypt">
|
||||
<van-image :src="decyImg" class="vanImage" fit="cover" :round="round" @load="emit('load')" @error="emit('error')">
|
||||
<template v-slot:loading>
|
||||
<div class="loading" v-if="!isSplash" />
|
||||
<div class="splash-loading-img" v-else></div>
|
||||
</template>
|
||||
<template v-slot:error>
|
||||
<div class="loading" v-if="!isSplash" />
|
||||
<div class="splash-error-img" v-else></div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</van-image>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Image as VanImage } from 'vant';
|
||||
import { ref } from 'vue';
|
||||
import AppApi from '@/service/app';
|
||||
const props = defineProps({
|
||||
//图片地址
|
||||
src: {
|
||||
type: String,
|
||||
required: true,
|
||||
default() {
|
||||
return '';
|
||||
},
|
||||
},
|
||||
baseUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
default() {
|
||||
return 'https://dkatmweejckwm.cloudfront.net';
|
||||
},
|
||||
},
|
||||
isSplash: {
|
||||
//是否来自启动页
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
round: {
|
||||
//图片是否显示为圆形
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
});
|
||||
let decyImg = ref('');
|
||||
const emit = defineEmits(['load', 'error']);
|
||||
|
||||
const init = () => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const imgSrc = props.baseUrl + '/' + props.src
|
||||
AppApi.imgDecypt(imgSrc)
|
||||
.then((url: any) => {
|
||||
decyImg.value = url;
|
||||
})
|
||||
.catch(() => {
|
||||
emit('error');
|
||||
});
|
||||
} catch (error) {
|
||||
emit('error');
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.van-image__loading) {
|
||||
// background-color: #2d2d2d !important;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.splash-loading-img,
|
||||
.splash-error-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('../../assets/images/png/splash.webp') no-repeat;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.img-decypt {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.vanImage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// background-color: #2d2d2d !important;
|
||||
|
||||
.loading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.van-image__error) {
|
||||
// background-color: #2d2d2d !important;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getDevType } from '@/utils';
|
||||
export default function jumpAdvClick(item: any) {
|
||||
window.open(item.url);
|
||||
fetch('https://dhpht.cestalt.com/api/app/recreation/click',{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: item.id,
|
||||
sysType: getDevType(),
|
||||
type: item.type
|
||||
}),
|
||||
}).then((response) => response.json())
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<div id="polymerization">
|
||||
<div class="box0">
|
||||
<div class="contactOfficial" @click="tolinkUrl">联系官方</div>
|
||||
<div class="adv-box0">
|
||||
<div class="slid-list slid-list1" :style="{ '--slid-size': 5 }">
|
||||
<MyDecryptImg
|
||||
class="cover"
|
||||
:baseUrl="state.baseImgUrl"
|
||||
:src="item.icon"
|
||||
v-for="(item, index) in state.smallSqureSlide"
|
||||
:key="index"
|
||||
@click="jumpAdvClick(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="slid-list slid-list2">
|
||||
<MyDecryptImg
|
||||
class="cover"
|
||||
:baseUrl="state.baseImgUrl"
|
||||
:src="item.icon"
|
||||
v-for="(item, index) in state.smallSqureSlide2"
|
||||
:key="index"
|
||||
@click="jumpAdvClick(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="slid-list slid-list3">
|
||||
<MyDecryptImg
|
||||
class="cover"
|
||||
:baseUrl="state.baseImgUrl"
|
||||
:src="item.icon"
|
||||
v-for="(item, index) in state.smallSqureSlide3"
|
||||
:key="index"
|
||||
@click="jumpAdvClick(item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-box"></div>
|
||||
<div class="swiper-list">
|
||||
<swiper :modules="[Pagination, FreeMode]" loop free-mode :slides-per-view="1.5" :space-between="15">
|
||||
<swiper-slide v-for="(item, index) in state.verticalSlide" :key="index">
|
||||
<div class="card-item" @click="jumpAdvClick(item)">
|
||||
<MyDecryptImg class="cover" :baseUrl="state.baseImgUrl" :src="item.icon" />
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box1">
|
||||
<div class="title">蛋黄派推荐🔥🔥🔥</div>
|
||||
<div class="download-box" v-for="(item, index) in state.hengOneUP" :key="index" @click="jumpAdvClick(item)">
|
||||
<MyDecryptImg class="cover" :baseUrl="state.baseImgUrl" :src="item.icon" />
|
||||
<div class="introduced">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
<div class="btn">下载</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box2">
|
||||
<div class="title">精品推荐😍😍😍</div>
|
||||
<div class="box2-list">
|
||||
<div class="box2-item" v-for="(item, index) in state.hengTwoUp" :key="index" @click="jumpAdvClick(item)">
|
||||
<MyDecryptImg class="cover" :baseUrl="state.baseImgUrl" :src="item.icon" />
|
||||
<div class="introduced">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box1">
|
||||
<div class="title">精品老牌💦💦💦</div>
|
||||
<div class="download-box" v-for="(item, index) in state.hengOneDown" :key="index" @click="jumpAdvClick(item)">
|
||||
<MyDecryptImg class="cover" :baseUrl="state.baseImgUrl" :src="item.icon" />
|
||||
<div class="introduced">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
<div class="btn">下载</div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box4">
|
||||
<div class="title">最新上线🥳🥳🥳</div>
|
||||
<div class="box4-list">
|
||||
<div class="box4-item" v-for="(item, index) in state.hengFour" :key="index" @click="jumpAdvClick(item)">
|
||||
<MyDecryptImg class="cover" :src="item.icon" :baseUrl="state.baseImgUrl" :key="item.icon + index" />
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="btn">下载</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import MyDecryptImg from './MyDecryptImg/index.vue'
|
||||
import { Pagination, FreeMode } from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import 'swiper/swiper.min.css';
|
||||
import { reactive } from 'vue';
|
||||
import jumpAdvClick from './advClick'
|
||||
|
||||
const state = reactive({
|
||||
hengOneUP: [],
|
||||
hengTwoUp: [],
|
||||
hengTwoDown: [],
|
||||
hengOneDown: [],
|
||||
hengFour: [],
|
||||
smallSqureSlide: [],
|
||||
smallSqureSlide2: [],
|
||||
smallSqureSlide3: [],
|
||||
verticalSlide: [],
|
||||
baseImgUrl: ''
|
||||
});
|
||||
fetch('https://dhpht.cestalt.com/api/app/recreation/list_v2').then((response) => response.json()).then((res: any) => {
|
||||
const resdata = res.data.list
|
||||
const obj = resdata.find((item: any) => item.name === '聚合下载') || {};
|
||||
const data = obj.comboList || [];
|
||||
state.hengOneUP = data.find((item: any) => item.displayType === 'hengOneUP').list;
|
||||
state.hengTwoUp = data.find((item: any) => item.displayType === 'hengTwoUp').list;
|
||||
state.hengTwoDown = data.find((item: any) => item.displayType === 'hengTwoDown').list;
|
||||
state.hengOneDown = data.find((item: any) => item.displayType === 'hengOneDown').list;
|
||||
state.hengFour = data.find((item: any) => item.displayType === 'hengFour').list;
|
||||
state.verticalSlide = data.find((item: any) => item.displayType === 'verticalSlide').list;
|
||||
state.smallSqureSlide = data.find((item: any) => item.displayType === 'smallSqureSlide').list;
|
||||
state.smallSqureSlide2 = data.find((item: any) => item.displayType === 'smallSqureSlide2').list;
|
||||
state.smallSqureSlide3 = data.find((item: any) => item.displayType === 'smallSqureSlide3').list;
|
||||
state.baseImgUrl = res.data.imgUrl?.domain[0]?.url
|
||||
});
|
||||
|
||||
const tolinkUrl = () => {
|
||||
window.open('https://t.me/gfqunzhu')
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
#polymerization {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
padding-bottom: 18px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
.box0 {
|
||||
width: 100%;
|
||||
height: 980px;
|
||||
background: url('./bg.jpg') no-repeat center;
|
||||
background-size: 100%;
|
||||
position: relative;
|
||||
.contactOfficial {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 41px;
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
width: 82px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #000;
|
||||
background: #fff;
|
||||
z-index: 10;
|
||||
@include flexCenter;
|
||||
}
|
||||
.adv-box0 {
|
||||
padding-top: 188px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.slid-list {
|
||||
display: flex;
|
||||
padding-bottom: 9px;
|
||||
padding-left: 20px;
|
||||
width: max-content;
|
||||
.cover {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
margin-right: 9px;
|
||||
}
|
||||
}
|
||||
.slid-list1 {
|
||||
animation: slide 50s linear infinite;
|
||||
}
|
||||
.slid-list2 {
|
||||
animation: slide 50s linear alternate-reverse infinite;
|
||||
animation-delay: -10s;
|
||||
}
|
||||
.slid-list3 {
|
||||
animation: slide 50s linear infinite;
|
||||
}
|
||||
.text-box {
|
||||
width: 299px;
|
||||
height: 105px;
|
||||
background: url('./text.png') no-repeat center;
|
||||
background-size: 100%;
|
||||
margin: 30px auto;
|
||||
}
|
||||
.swiper-list {
|
||||
.card-item {
|
||||
width: 248px;
|
||||
height: 306px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
.box1 {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.download-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
position: relative;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
.cover {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.introduced {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
flex: 1;
|
||||
.desc {
|
||||
color: #555664;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
width: 72px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(270deg, rgba(255, 195, 3, 0.2) 0%, rgba(255, 221, 1, 0.2) 100%);
|
||||
color: #ffb420;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.line {
|
||||
width: 261px;
|
||||
height: 1px;
|
||||
background: rgba(198, 198, 200, 0.5);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.box2 {
|
||||
padding: 0 18px;
|
||||
|
||||
.box2-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 11px 16px;
|
||||
}
|
||||
.box2-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 160px;
|
||||
height: 64px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(198, 198, 200, 0.5);
|
||||
.cover {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 9px;
|
||||
overflow: hidden;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.introduced {
|
||||
.name {
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
}
|
||||
.desc {
|
||||
color: #999aa2;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box4 {
|
||||
padding: 0 20px;
|
||||
padding-bottom: 50px;
|
||||
.box4-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 26px 9px;
|
||||
.box4-item {
|
||||
width: 76px;
|
||||
text-align: center;
|
||||
.cover {
|
||||
width: 76px;
|
||||
height: 76px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.name {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
margin-top: 7px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.btn {
|
||||
width: 72px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(270deg, rgba(255, 195, 3, 0.2) 0%, rgba(255, 221, 1, 0.2) 100%);
|
||||
color: #ffb420;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 关键帧动画 */
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Reference in New Issue
Block a user