初始化导入 hjsq H5 项目代码

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
谢宇宁
2026-11-05 12:01:36 +07:00
co-authored by Claude Opus 5.5
commit f61585f786
1367 changed files with 136187 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
<template>
<div class="item">
<van-list
v-if="!moreParams.isNoData"
v-model:loading="moreParams.loading"
:finished="moreParams.finished"
finished-text="没有更多数据了"
@load="onLoad"
:offset="10"
v-model:error="moreParams.error"
:immediate-check="false"
error-text="请求失败,点击重新加载"
>
<div :class="!showSquare ? 'moreMoviesBox' : 'moviesBox'">
<HorizontalVideoBox
class="videoBox"
:videoInfo="item"
:videoList="state.list"
v-for="item in state.list"
:key="item.id"
showTimeAndComment
/>
</div>
</van-list>
<div class="sizeBox" v-if="!moreParams.isNoData"></div>
<Empty v-if="moreParams.isNoData" />
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { AdType } from '@/enums/app';
import { getAdItem } from '@/utils/getConfig';
import HomeApi from '@/service/home';
const props = withDefaults(
defineProps<{
moduleSort: number;
moduleId: string;
showSquare: boolean;
}>(),
{
moduleSort: 1,
moduleId: '',
showSquare: false,
},
);
const emit = defineEmits(['getLabels', 'finishedRefresh']);
const state = reactive({
happinessList: <any>[], // 列表
list: <any>[], // 真实列表
advList: <any>[], // 专题更多广告
pageNumber: 1,
pageSize: 10,
});
const moreParams = reactive({
loading: true,
finished: false,
isNoData: false,
error: false,
});
// 获取模块数据
const getList = async (type: any) => {
const data = {
id: props.moduleId,
data: {
pageNumber: state.pageNumber,
pageSize: state.pageSize,
moduleSort: props.moduleSort,
},
};
try {
let res: any = await HomeApi.getModuleList(data);
emit('getLabels', res.allSection);
moreParams.loading = false;
emit('finishedRefresh');
if (type === 'refresh') {
state.happinessList = <any>[];
state.list = <any>[];
}
let list = res.allVideoInfo || [];
state.happinessList = state.happinessList.concat(list);
if (state.happinessList.length == 0 && state.pageNumber == 1) {
moreParams.isNoData = true;
}
let advs: any = [].concat(state.advList);
state.list = [].concat(state.happinessList);
if (advs.length > 0) {
state.list.forEach((item: any, index: any) => {
if (index > 0 && index % 6 == 0) {
let adv: any = advs.splice(0, 1);
adv[0].type = 'adv';
state.list.splice(index + index / 6 - 1, 0, ...adv);
advs.push(...adv);
}
});
}
if (!res.hasNext) {
moreParams.finished = true;
}
} catch (e) {
moreParams.loading = false;
moreParams.error = true;
}
};
// 初始化
const init = () => {
// 专题之间的广告
state.advList = getAdItem(AdType.HorizontalVersion);
getList('');
};
init();
const onLoad = () => {
moreParams.loading = true;
state.pageNumber++;
getList(null);
};
const onRefresh = () => {
state.pageNumber = 1;
moreParams.finished = false;
moreParams.isNoData = false;
getList('refresh');
};
defineExpose({
onRefresh,
});
</script>
<style lang="scss" scoped>
.item {
.moreMoviesBox {
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 7px;
padding: 0 16px;
:deep(.horizontalVideoBox .coverBox) {
height: 94px !important;
}
:deep(.videoTitleBox) {
margin-bottom: 14px;
}
}
.moviesBox {
padding: 0 16px;
:deep(.horizontalVideoBox .coverBox) {
height: 193px !important;
}
:deep(.videoTitle) {
height: 18px;
line-height: 18px;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
}
.sizeBox {
height: 100px;
}
}
</style>
@@ -0,0 +1,191 @@
<template>
<div class="darkNetItem overflow-y-auto" :id="'darkNet' + tabIndex" @scroll="onScroll">
<van-pull-refresh
v-model="moreParams.refreshing"
@refresh="onRefresh"
:disabled="moreParams.disabled"
success-text="刷新成功"
:class="[{ 'my-refresh': moreParams.isHigehtMax, none: moreParams.refreshing }]"
>
<ModuleBanners :banners="banners" :moduleType="3" />
<!-- 广告轮播 -->
<HomeHeader :advType="AdType.HOME" class="advBox" v-if="!props.pureVersion" />
<div class="labelBox" v-show="state.labels && state.labels.length">
<div class="label ellipsis" @click="toTagShortVideo(item)" v-for="item in state.labels" :key="item.id">
{{ item.sectionName }}
</div>
</div>
<div class="homeItemTab">
<div class="changeBox">
<div class="changeBoxFixed flex-vcenter" @click="state.showSquare = !state.showSquare">
<span>切换</span>
<SvgIcon name="square" v-if="state.showSquare" />
<SvgIcon name="rectangle" v-else />
</div>
</div>
<van-tabs
v-model:active="state.tabActive"
class="oepnX"
title-active-color="#fff"
title-inactive-color="#999"
animated
:scrollspy="false"
:swipeable="false"
:lazy-render="true"
>
<van-tab v-for="(item, index) in state.moduleSorts" :key="item.val" :title="item.name" :name="index">
<Item
v-if="state.tabActive == index"
ref="itemRef"
@getLabels="getLabels"
@finishedRefresh="finishedRefresh"
:moduleSort="item.val"
:moduleId="state.moduleId"
:showSquare="state.showSquare"
/>
</van-tab>
</van-tabs>
</div>
</van-pull-refresh>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { AdType } from '@/enums/app';
import Item from '@/views/darkNet/components/Item.vue';
import { useRouter } from 'vue-router';
const props = withDefaults(
defineProps<{
isFirst?: boolean;
moduleId: string;
tabIndex?: number;
haiJiaoStyle: Types.Home.HaiJiaoStyle;
pureVersion?: boolean;
banners?: Types.Home.ModuleBanner[] | null;
}>(),
{
isFirst: false,
moduleId: '',
pureVersion: false,
},
);
const itemRef = ref();
const router = useRouter();
const state = reactive({
labels: <any>[], // 标签
tabActive: 0,
list: <Array<Types.Home.SectionsItem>>[],
pageNumber: 1,
pageSize: 10,
moduleId: props.moduleId,
moduleSorts: props.haiJiaoStyle.sortRules || [],
showSquare: props.haiJiaoStyle.defaultShow == 1, // 0-一排两个 1-一排一个
});
const moreParams = reactive({
refreshing: false,
isHigehtMax: true,
disabled: false,
});
const getLabels = (val: any) => {
state.labels = val;
};
const finishedRefresh = () => {
moreParams.refreshing = false;
};
const onRefresh = () => {
moreParams.refreshing = true;
try {
itemRef.value[0].onRefresh();
// itemRef.value[state.tabActive].onRefresh();
} catch (e) {
console.log(e);
}
};
const toTagShortVideo = (val: any) => {
router.push({
path: 'tagShortVideo',
query: {
id: val.sectionID,
name: val.sectionName,
},
});
};
const tabBgColor = ref('rgb(0, 0, 0 , 0)');
const onScroll = (e: Event) => {
const _scrollTop = (e.target as HTMLElement).scrollTop;
tabBgColor.value = `rgb(0, 0, 0 , ${_scrollTop / 200})`;
};
</script>
<style lang="scss" scoped>
.darkNetItem {
height: calc(100vh - 44px * 3);
width: 100vw;
.labelBox {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-row-gap: 10px;
grid-column-gap: 10px;
padding: 0 16px;
margin: 18px 0 4px;
.label {
height: 33px;
border-radius: 3px;
background: url('@/assets/images/png/darkNet/textbg.png') no-repeat;
background-color: rgba(129, 9, 6, 0.3);
text-align: center;
line-height: 33px;
font-size: 12px;
padding: 0 10px;
}
}
.advBox {
padding: 0 16px;
}
.homeItemTab {
.changeBox {
position: sticky;
top: 0px;
z-index: 9;
.changeBoxFixed {
color: rgba(255, 255, 255, 0.6);
font-size: 12px;
position: absolute;
right: 10px;
top: 12px;
z-index: 9;
> svg {
height: 18px;
width: 18px;
}
}
}
:deep(.van-tabs) {
.van-tabs__wrap {
background-color: v-bind('tabBgColor');
position: sticky;
top: 0px;
z-index: 1;
display: flex;
padding-left: 8px;
.van-tabs__nav {
background: unset;
.van-tab {
font-size: 14px !important;
padding: unset !important;
width: 75px;
}
.van-tabs__line {
display: none;
}
}
}
}
}
}
:deep(.van-pull-refresh) {
overflow: unset;
}
</style>
+329
View File
@@ -0,0 +1,329 @@
<template>
<div class="darknet">
<HeaderSearch @menuChange="menuChange" />
<van-tabs
v-if="state.isChangeList"
v-model:active="globalStore.darkNetActiveIndex"
class="darkTab oepnX"
title-active-color="#810906"
title-inactive-color="rgba(255, 255, 255, 0.55)"
animated
shrink
:scrollspy="false"
swipeable
:lazy-render="true"
@change="tabChange"
>
<van-tab v-for="(item, index) in globalStore.darkNetPageList" :key="item.id" :title="item.moduleName">
<darkNetItem
:tabIndex="index"
:moduleId="item.id"
:haiJiaoStyle="item.haiJiaoStyle"
:pureVersion="item.pureVersion"
:banners="item.banners"
/>
</van-tab>
</van-tabs>
<div class="waringBox" v-if="userStore.userInfo.vipLevel < 3">
<template v-if="configStore.appConfig.darkWebImg">
<lazyImg
:src="configStore.appConfig.darkWebImg"
alt="darkWebImg"
class="darkWebImg"
@click="$router.push(`/member?id=${configStore.appConfig.darkWebVipId}`)"
/>
</template>
<template v-else>
<div class="waringBoxTip">
<div class="waringBoxTip_content">
<img src="@/assets/images/png/darkNet/darkNet_log.png" class="waring_log" />
<div class="content_title">系统警告</div>
<p class="mess">
【暗网】含有百万全球禁播封杀资源,全球
<br />
上流社会权贵的私密丑闻!
<br />
<br />
萝莉岛事件,N号房,暴力重口,巴以/俄
<br />
乌/中东战争,血腥缅北,呦呦破处,变态
<br />
恐怖,暗网交易,等稀缺资源!
</p>
<p class="waring_info">
马上开通
<br />
<span>{{ configStore.appConfig.darkWebVipName }}</span>
<br />
进入世界的黑暗面!
</p>
<p class="waring_btn" @click="$router.push(`/member?id=${configStore.appConfig.darkWebVipId}`)">
开通专属会员
</p>
<p class="waring_info waring_color">
此板块为黑客破解内容,真实稀缺资源,
<br />
可能会引起极度不适,请谨慎进入
</p>
</div>
</div>
</template>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'darkNet',
};
</script>
<script lang="ts" setup>
import { reactive, onActivated, computed, onDeactivated, watch, nextTick } from 'vue';
import { useUserStore } from '@/stores/user';
import UserApi from '@/service/user';
import { useGlobalStore } from '@/stores/global';
import { useRoute } from 'vue-router';
import darkNetItem from './components/darkNetItem.vue';
import { setShowBackTopBtn } from '@/utils/darkNetCombinedFunc';
import { useConfigStore } from '@/stores/config';
const configStore = useConfigStore();
const route = useRoute();
const userStore = useUserStore();
const globalStore = useGlobalStore();
const state = reactive({
userInfo: <Types.User.UserInfoType>{},
scrollDistance: <any>0, // 滚动距离
isChangeList: false,
});
watch(
() => globalStore.darkNetPageList,
() => {
state.isChangeList = false;
nextTick(() => {
state.isChangeList = true;
});
},
{
immediate: true,
},
);
/**
* 获取用户信息
* @param uid
*/
const getInfo = async () => {
try {
let res: any = await UserApi.queryUserInfo(null);
state.userInfo = res;
userStore.saveUserInfo(state.userInfo);
} catch (e) {
console.log(e);
}
};
const init = async () => {
await getInfo();
};
init();
const menuChange = () => {
globalStore.setShowDrawer(!globalStore.showDarkNetDrawer, 'darkNet');
};
let keepScroll: any = computed(() => {
return globalStore.keepScroll; // 获取保存在sessionStore中的滚动距离
});
// 监听滚动执行的方法
const scroll = () => {
let dom = document.getElementById('darkNet' + globalStore.darkNetActiveIndex); // 获取滚动的dom
let arr = keepScroll.value[route.path]; // 获取当前路由下所保存的滚动距离数组
arr.map((item: any) => {
// 找到数组里当前需要更改的对象
if (item.scrollObj == globalStore.darkNetActiveIndex) {
// 更新对象里面的滚动距离
item.scrollDistance = dom?.scrollTop;
if (item.scrollDistance >= 500) {
setShowBackTopBtn(globalStore.darkNetActiveIndex, true);
} else {
setShowBackTopBtn(globalStore.darkNetActiveIndex, false);
}
}
return item;
});
// 将更新好后的数组赋值给当前路由下所保存的滚动距离数组
keepScroll.value[route.path] = arr;
};
// 切换tab的时候需要执行的事件
const tabChange = () => {
let dom = document.getElementById('darkNet' + globalStore.darkNetActiveIndex);
let arr = keepScroll.value[route.path]; // 获取当前路由下所保存的滚动距离数组
let obj = arr.find((item: any) => item.scrollObj == globalStore.darkNetActiveIndex); // 循环获取到的数组,并找出是否存在当前tab所保存的滚动对象
if (obj) {
dom?.scrollTo(0, obj.scrollDistance); // 如果存在说明已经访问过,回到滚动条高度
} else {
// 不存在说明是第一次访问,那么就创建一个对象push到数组中
arr.push({
scrollObj: globalStore.darkNetActiveIndex, // 当前的tabActive索引当作对象的key
scrollDistance: 0, // 滚动高度
});
}
window.addEventListener('scroll', scroll, true); // 添加滚动监听,并执行滚动的方法
};
onActivated(() => {
tabChange(); // 从别的页面回来时,触发缓存方法,并需要执行
});
onDeactivated(() => {
window.removeEventListener('scroll', scroll, true); // 离开当前页面时,需要将监听滚动的事件移除,否则占用内存
});
</script>
<style lang="scss" scoped>
.darknet {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #05000e;
// background: url('@/assets/images/png/darkNet/darkNetBg.png') 0 0/100% 100% no-repeat;
.search {
width: 22px;
height: 22px;
position: absolute;
top: 8px;
right: 16px;
z-index: 2;
}
// .header {
// padding: 10px 16px 0;
// .searchBtn {
// background: rgb(32, 39, 51);
// flex: 1;
// height: 42px;
// border-radius: 42px;
// font-size: 14px;
// padding: 0 16px 0 20px;
// color: rgb(67, 76, 85);
// .search {
// height: 20px;
// width: 20px;
// background: url('./../../assets/images/png/search.png') no-repeat;
// background-size: 100% 100%;
// margin-right: 10px;
// }
// }
// .logo {
// height: 37px;
// width: 45px;
// background: url('./../../assets/images/png/clickfuli.png') no-repeat;
// background-size: 100% 100%;
// margin-left: 11px;
// }
// }
.darkTab {
position: relative;
:deep(> .van-tabs__wrap) {
height: 44px;
.van-tabs__nav {
background: unset;
.van-tab {
font-size: 14px;
}
.van-tab--active {
font-size: 16px;
}
.van-tab__text {
// font-family: 'AaLongYanTi';
}
svg {
width: 64px;
height: 22px;
}
.van-tabs__line {
width: 16px;
height: 2px;
border-radius: 12px;
bottom: 18px;
background: #f68804;
}
}
}
:deep(.tab_padding > .van-tabs__wrap) {
padding: 0 80px;
box-sizing: border-box;
}
}
.waringBox {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1000;
background: rgb(0, 0, 0, 0.7);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
.darkWebImg {
width: 100%;
height: 100%;
}
}
.waringBoxTip {
.waringBoxTip_content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 308px;
color: #fff;
.waring_log {
width: 111px;
height: 96px;
margin-bottom: 30px;
}
.content_title {
box-sizing: border-box;
text-align: center;
font-size: 16px;
font-weight: 500;
color: #fff;
margin-bottom: 4px;
}
p {
box-sizing: border-box;
text-align: center;
line-height: 22px;
margin-bottom: 18px;
font-size: 16px;
}
.mess {
color: #fff;
}
.waring_btn {
width: 177px;
height: 62px;
margin: 0 auto 20px;
font-size: 14px;
padding-top: 20px;
background: url('@/assets/images/png/darkNet/button.png') no-repeat;
background-size: 100% 100%;
border-radius: 99px;
}
.waring_info {
font-size: 14px;
span {
color: #c40f0b;
}
}
.waring_color {
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
}
}
}
}
</style>