Files
hjsq_h5/src/views/lldhome/components/DramaModule.vue
T
2026-11-05 17:13:58 +07:00

291 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 首页短剧:样式 5 按专题切换作品,其余样式保留模块排序。 -->
<template>
<div class="drama-module overflow-y-auto" :id="'home' + tabIndex">
<van-pull-refresh v-model="refreshing" :head-height="0" :pull-distance="50" @refresh="onRefresh">
<template #pulling />
<template #loosing />
<template #loading />
<div v-if="isSectionStyle && sections.length" class="filters" @touchstart.stop @touchmove.stop @touchend.stop>
<button
v-for="section in sections"
:key="section.sectionID"
type="button"
class="filter"
:class="{ 'is-active': activeSectionId === section.sectionID }"
:aria-pressed="activeSectionId === section.sectionID"
@click="selectSection(section.sectionID)"
>
{{ section.sectionName }}
</button>
</div>
<div
v-else-if="!isSectionStyle && sortRules.length"
class="filters"
@touchstart.stop
@touchmove.stop
@touchend.stop
>
<button
v-for="sort in sortRules"
:key="sort.val"
type="button"
class="filter"
:class="{ 'is-active': moduleSort === sort.val }"
:aria-pressed="moduleSort === sort.val"
@click="moduleSort = sort.val"
>
{{ sort.name }}
</button>
</div>
<ModuleBanners :banners="currentBanners" :moduleType="12" />
<van-list
v-model:loading="loading"
v-model:error="error"
:finished="finished"
:immediate-check="false"
:offset="100"
:finished-text="items.length ? '没有更多数据了' : ''"
error-text="请求失败,点击重新加载"
@load="onLoad"
>
<div v-if="items.length" class="drama-grid">
<DramaCoverCard
v-for="item in items"
:key="item.series.id"
:item="item.series"
variant="home"
@click="goDramaPlay(item.series.id)"
>
<template #badge>
<span v-if="item.badge" class="permission" :class="'permission--' + item.badge">
{{ badgeLabels[item.badge] }}
</span>
</template>
</DramaCoverCard>
</div>
<Empty v-else-if="finished" />
</van-list>
</van-pull-refresh>
</div>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import HomeApi from '@/service/home';
import MediaApi from '@/service/media';
import { mapSeries } from '@/service/short-drama';
import { useConfigStore } from '@/stores/config';
import DramaCoverCard from '@/views/shortVideo/short-drama/components/DramaCoverCard.vue';
const props = defineProps<{
moduleId: string;
tabIndex: number;
haiJiaoStyle: Types.Home.HaiJiaoStyle;
pureVersion?: boolean;
}>();
type DramaModuleItem = Types.ShortDrama.DramaInfo &
Pick<Types.Media.AllMediaInfoItem, 'permission' | 'permissionIconHide' | 'price'>;
type DramaModuleSection = Types.Media.allSection & {
banners?: Types.Home.ModuleBanner[] | null;
};
type DramaModuleResponse = {
allMediaInfo?: DramaModuleItem[] | null;
list?: DramaModuleItem[] | null;
allSection?: DramaModuleSection[] | null;
banners?: Types.Home.ModuleBanner[] | null;
hasNext?: boolean;
};
type Badge = 'vip' | 'coin' | 'free';
const badgeLabels = { vip: 'VIP', coin: '金币', free: '免费' };
const router = useRouter();
const configStore = useConfigStore();
const media = ref<DramaModuleItem[]>([]);
const moduleBanners = ref<Types.Home.ModuleBanner[]>([]);
const isSectionStyle = computed(() => props.haiJiaoStyle?.sectionStyle === 5);
const sections = ref<DramaModuleSection[]>([]);
const activeSectionId = ref('');
const currentBanners = computed(() =>
isSectionStyle.value
? sections.value.find((section) => section.sectionID === activeSectionId.value)?.banners || []
: moduleBanners.value,
);
const items = computed(() =>
media.value.map((info) => {
let badge: Badge | undefined;
if (!info.permissionIconHide) {
if (info.permission === 0 && configStore.appConfig.vipMark) badge = 'vip';
if (info.permission === 2 && configStore.appConfig.freeMark) badge = 'free';
}
return { series: mapSeries(info), badge };
}),
);
const sortRules = computed(() => (props.haiJiaoStyle?.sortShow === 1 ? props.haiJiaoStyle.sortRules || [] : []));
const moduleSort = ref(sortRules.value[0]?.val || 0);
const loading = ref(false);
const refreshing = ref(false);
const finished = ref(false);
const error = ref(false);
let pageNumber = 1;
let requestVersion = 0;
let pending = false;
let sectionsLoaded = false;
async function loadPage(reset = false, reloadSections = false) {
if (!reset && (pending || finished.value)) return;
const version = ++requestVersion;
const page = reset ? 1 : pageNumber;
pending = true;
loading.value = true;
error.value = false;
if (reset) finished.value = false;
try {
let res: DramaModuleResponse;
if (isSectionStyle.value) {
if (!sectionsLoaded || reloadSections) {
const module = (await HomeApi.getModuleList({
id: props.moduleId,
data: { pageNumber: 1, pageSize: 12, moduleSort: 0 },
})) as unknown as DramaModuleResponse;
if (version !== requestVersion) return;
sections.value = module?.allSection || [];
sectionsLoaded = true;
if (!sections.value.some((section) => section.sectionID === activeSectionId.value)) {
activeSectionId.value = sections.value[0]?.sectionID || '';
media.value = [];
}
}
if (!activeSectionId.value) {
media.value = [];
finished.value = true;
return;
}
res = (await MediaApi.getMediaTopic({
sId: activeSectionId.value,
pageNumber: page,
pageSize: 20,
})) as unknown as DramaModuleResponse;
} else {
res = (await HomeApi.getModuleList({
id: props.moduleId,
data: { pageNumber: page, pageSize: 12, moduleSort: moduleSort.value },
})) as unknown as DramaModuleResponse;
}
if (version !== requestVersion) return;
if (!isSectionStyle.value && page === 1) {
moduleBanners.value = res?.banners || [];
}
// 短剧专题作品接口使用 list;模块接口使用 allMediaInfo。
const list = (isSectionStyle.value ? res?.list : res?.allMediaInfo) || [];
media.value = page === 1 ? list : [...media.value, ...list];
pageNumber = page + 1;
finished.value = !res?.hasNext || !list.length;
} catch {
if (version === requestVersion) error.value = true;
} finally {
if (version === requestVersion) {
pending = false;
loading.value = false;
refreshing.value = false;
}
}
}
const onLoad = () => loadPage();
const onRefresh = () => {
pageNumber = 1;
return loadPage(true, true);
};
const selectSection = (sectionId: string) => {
if (activeSectionId.value === sectionId) return;
activeSectionId.value = sectionId;
media.value = [];
pageNumber = 1;
void loadPage(true);
};
const goDramaPlay = (id: string) => router.push({ path: '/shortDramaPlay', query: { id, episode: '1' } });
watch(
() => [props.moduleId, isSectionStyle.value, moduleSort.value],
() => {
media.value = [];
moduleBanners.value = [];
sections.value = [];
activeSectionId.value = '';
sectionsLoaded = false;
pageNumber = 1;
void loadPage(true);
},
{ immediate: true },
);
onBeforeUnmount(() => {
requestVersion += 1;
});
</script>
<style scoped lang="scss">
.drama-module {
height: calc(100vh - 44px * 2 - 55px);
:deep(.van-pull-refresh) {
min-height: 100%;
}
:deep(.advBox) {
margin: 12px 0;
.firstRow,
.adv-line-2,
.banner_box {
padding: 0 16px;
}
}
}
.filters {
display: flex;
gap: 10px;
overflow-x: auto;
padding: 8px 16px;
}
.filter {
flex-shrink: 0;
border: 0;
border-radius: 20px;
padding: 4px 10px;
font-size: 12px;
line-height: 18px;
color: rgba(255, 255, 255, 0.55);
background: rgba(255, 255, 255, 0.05);
&.is-active {
color: #111;
background: #ffc000;
font-weight: 600;
}
}
.drama-grid {
display: flex;
flex-wrap: wrap;
gap: 12px 7px;
padding: 12px 16px;
}
.permission {
position: absolute;
top: 0;
right: 0;
padding: 0 6px;
border-radius: 3px;
font-size: 10px;
line-height: 16px;
color: #fff;
&--vip {
background: linear-gradient(60.44deg, #ff8000 10.737%, #ff7a01 66.041%);
}
&--free {
background: #1cd302;
}
&--coin {
color: #1f2029;
background: linear-gradient(135deg, #ffe580 0%, #facc15 100%);
}
}
</style>