[Human] chore(project): 初始化导入 91dj H5 客户端项目代码

从本地工程首次入库;.npmrc(含私有 npm 源 token)与 tsbuildinfo 已加入 .gitignore,不随仓库提交。
This commit is contained in:
谢宇宁
2026-10-08 14:22:35 +07:00
commit eada71e211
1421 changed files with 144498 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
<template>
<div class="recommendedList">
<van-tabs
v-model:active="state.tabActive"
class="oepnX"
title-active-color="rgba(255, 255, 255, 0.90)"
title-inactive-color="rgba(255, 255, 255, 0.55)"
:scrollspy="false"
:swipeable="false"
:lazy-render="true"
>
<van-tab :name="index" :title="item.label" v-for="(item, index) in tabList" :key="item.value">
<Video
:type="type"
:tagId="tagId"
:currentType="item.value"
v-if="index == state.tabActive"
@changeVideo="
(videoInfo) => {
emit('changeVideo', videoInfo);
}
"
/>
</van-tab>
</van-tabs>
</div>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue';
import Video from './video.vue';
const emit = defineEmits(['changeVideo']);
const props = withDefaults(
defineProps<{
type: string;
tagId?: string;
}>(),
{
type: 'SP',
},
);
const state = reactive({
tabActive: 0,
});
const tabList = computed(() => {
if (props.type == 'video') {
return [
{
label: '动漫推荐',
value: 'video',
},
{
label: '视频推荐',
value: 'SP',
},
{
label: '漫画推荐',
value: 'image',
},
];
}
if (props.type == 'image') {
return [
{
label: '漫画推荐',
value: 'image',
},
{
label: '视频推荐',
value: 'SP',
},
{
label: '动漫推荐',
value: 'video',
},
];
}
return [
{
label: '视频推荐',
value: 'SP',
},
{
label: '动漫推荐',
value: 'video',
},
{
label: '漫画推荐',
value: 'image',
},
];
});
</script>
<style lang="scss" scoped>
.recommendedList {
height: calc(100vh - 250px);
--van-tabs-line-height: 44px;
:deep(.van-tabs) {
.van-tabs__wrap {
position: sticky;
top: -1px;
background-color: var(--app-background);
border-bottom: 0 none;
padding: 0 10px;
z-index: 1;
.van-tabs__nav {
background: unset;
}
.van-tab {
flex: 1 !important;
width: unset !important;
justify-content: center !important;
}
.van-tabs__line {
width: 13px;
height: 3px;
border-radius: 12px;
background: linear-gradient(90deg, rgba(255, 192, 0, 0) 0%, var(--app-primary) 100%);
bottom: 22px;
}
}
}
}
</style>
+145
View File
@@ -0,0 +1,145 @@
<template>
<div class="videos">
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh">
<div class="moreMoviesBox" :class="['video', 'image'].includes(currentType) ? 'moreAcgBox' : ''">
<HorizontalVideoBox
class="videoBox"
:videoList="state.list"
:videoInfo="item"
:isMovie="type == 'SP' && type == currentType"
v-for="item in state.list"
:key="item.id"
:showTimeAndComment="currentType == 'SP'"
@changeVideo="changeVideo"
/>
</div>
</PullRefresh>
</div>
</template>
<script lang="ts" setup>
import VideoApi from '@/service/video';
import AcgApi from '@/service/acg';
import { reactive } from 'vue';
const props = withDefaults(
defineProps<{
type: string;
tagId?: string;
currentType: string;
}>(),
{
type: 'SP',
},
);
const state = reactive({
tabActive: 0,
pageNumber: 1,
pageSize: 10,
list: <any>[],
});
const moreParams = reactive({
loading: true,
refreshing: false,
finished: false,
isNoData: false,
error: false,
isHigehtMax: false,
});
const emit = defineEmits(['changeVideo']);
const changeVideo = (videoInfo: any) => {
emit('changeVideo', videoInfo);
};
// 获取更多视频
const getList = async () => {
try {
let req: any = {
pageNumber: state.pageNumber,
pageSize: state.pageSize,
};
let ajax: any = VideoApi.queryRecommend;
if (props.currentType == 'video' || props.currentType == 'image') {
req.mediaType = props.currentType;
if (props.type == props.currentType) {
req.tagId = props.tagId || null;
}
ajax = AcgApi.getMediaRecommend;
} else {
req.newsType = props.currentType;
if (props.type == 'SP') {
req.tagId = props.tagId || null;
}
}
let res: any = await ajax(req);
let list = res.list || [];
if (state.pageNumber == 1) {
state.list = list;
} else {
state.list = state.list.concat(list);
}
if (state.pageNumber == 1 && state.list.length == 0) {
moreParams.isNoData = true;
}
if (!res.hasNext) {
moreParams.finished = true;
}
} catch (e) {
moreParams.error = true;
} finally {
moreParams.loading = false;
moreParams.refreshing = false;
}
};
const onLoad = () => {
if (moreParams.refreshing) return;
moreParams.loading = true;
state.pageNumber++;
getList();
};
const onRefresh = () => {
state.pageNumber = 1;
moreParams.refreshing = true;
moreParams.finished = false;
moreParams.isNoData = false;
getList();
};
const init = () => {
getList();
};
init();
</script>
<style lang="scss" scoped>
.videos {
.moreMoviesBox {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-column-gap: 7px;
width: 100%;
box-sizing: border-box;
.videoBox {
width: 100%;
min-width: 0;
:deep(.coverBox) {
height: 93px;
}
}
}
.moreAcgBox {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-column-gap: 5px;
.videoBox {
width: 100%;
min-width: 0;
:deep(.coverBox) {
height: 148px;
}
:deep(.videoTitleBox) {
.videoTitle {
height: 20px;
-webkit-line-clamp: 1;
}
}
}
}
}
</style>