初始化
This commit is contained in:
@@ -0,0 +1,773 @@
|
||||
<template>
|
||||
<div class="detail-wrap overflow-y-auto" @scroll="onScroll">
|
||||
<template v-if="state.info.id">
|
||||
<div class="header flex-center-between" :style="headStyle">
|
||||
<SvgIcon name="back_white_round1" class="back" @click="$router.go(-1)" />
|
||||
<p v-show="scrollTop >= 100" class="title ellipsis">{{ state.info.title }}</p>
|
||||
<div class="bookself">
|
||||
<div class="menuItem">
|
||||
<SvgIcon
|
||||
class="collected"
|
||||
name="acg-collect-img-1"
|
||||
v-if="historyData?.mediaStatus?.hasCollected"
|
||||
@click="onToogleCollected"
|
||||
></SvgIcon>
|
||||
<SvgIcon class="collected" name="acg-collect-img-0" v-else @click="onToogleCollected"></SvgIcon>
|
||||
<SvgIcon name="acg-share1" @click="openShare" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cover-box flex-vcenter">
|
||||
<lazyImg class="cover" :src="state.info.verticalCover || state.info.horizontalCover"></lazyImg>
|
||||
<div class="content">
|
||||
<h1 class="title ellipsis">{{ state.info.title }}</h1>
|
||||
<div class="status">
|
||||
<!-- <template v-if="state.info.updateStatus == 2">已完结 共{{ state.info.totalEpisode }}话</template>
|
||||
<template v-else>更新至第{{ state.info.currentEpisode || state.info.totalEpisode }}话</template> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<div class="watch" :class="state.info.mediaType">
|
||||
<div class="info">
|
||||
<div class="count flex-vcenter">
|
||||
<div class="count-item">
|
||||
<SvgIcon name="eye-open" />
|
||||
{{ watchCount(state.info.countBrowse) }}
|
||||
</div>
|
||||
<div class="count-item">
|
||||
<SvgIcon name="acg-heart-2" />
|
||||
{{ watchCount(state.info.countLike) }}
|
||||
</div>
|
||||
<div class="count-item">
|
||||
{{ state.info.permission == 0 ? 'VIP' : (state.info.price || '0') + '金币' }}
|
||||
</div>
|
||||
|
||||
<!-- <div class="collected flex-vcenter" @click="onToogleCollected">
|
||||
<SvgIcon v-if="!hasCollected" name="acg-collect-star-0" />
|
||||
<SvgIcon v-else name="acg-collect-star-1" style="color: #f52c56" />
|
||||
{{ watchCount(state.info.countCollect) }}
|
||||
</div> -->
|
||||
</div>
|
||||
<div :class="['summary', !showMoreSummary && 'ellipsis3']">{{ state.info.summary || '' }}</div>
|
||||
<div
|
||||
v-if="state.info.summary"
|
||||
:class="['more-summary', showMoreSummary && 'showMoreSummary']"
|
||||
@click="showMoreSummary = !showMoreSummary"
|
||||
>
|
||||
更多简介
|
||||
<SvgIcon name="rightArrow1" />
|
||||
</div>
|
||||
<div class="tags" v-if="tagDetails.length">
|
||||
<div
|
||||
class="tag"
|
||||
v-for="item in tagDetails"
|
||||
:key="item.id"
|
||||
@click="goAcgTagPage(item.id, item.name, 'image')"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav" @click="visibleCatalogue = true">
|
||||
<span class="title">选集</span>
|
||||
<span class="count flex-vcenter">
|
||||
{{ EpisodeTitle }}
|
||||
<SvgIcon name="rightArrow1" />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="episode-box"
|
||||
ref="episodeBox"
|
||||
@touchstart.stop
|
||||
@touchmove.stop
|
||||
@touchend.stop
|
||||
@scroll="onScrollEpisodeBox"
|
||||
>
|
||||
<Episode
|
||||
showType="text"
|
||||
:id="'episode-' + item.id"
|
||||
class="item"
|
||||
:episodeNumber="childState.episodeNumber"
|
||||
:info="item"
|
||||
v-for="item in autoList?.slice(0, 4) || []"
|
||||
:key="item.id"
|
||||
@click="childState.episodeNumber = item.episodeNumber"
|
||||
></Episode>
|
||||
</div>
|
||||
</div>
|
||||
<RecommendedList :type="state.info.mediaType" :tagId="tagDetails[0]?.id" class="recommendedBox" />
|
||||
<div class="action">
|
||||
<div class="btn flex-vcenter" @click="onToogleLiked">
|
||||
<SvgIcon v-if="historyData?.mediaStatus?.hasLiked" name="acg-heart-1" />
|
||||
<SvgIcon v-else name="acg-heart-0" />
|
||||
喜欢
|
||||
</div>
|
||||
<div class="btn flex-vcenter go-page" @click="goWatch">
|
||||
{{ currentEpisodeName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<Loading v-else />
|
||||
</div>
|
||||
<van-popup v-model:show="visibleCatalogue" position="bottom" z-index="1001" :style="{ height: '438px' }">
|
||||
<div class="play-list-box">
|
||||
<div class="line"></div>
|
||||
<div class="title flex-center-between">
|
||||
漫画列表
|
||||
<div class="right">
|
||||
<SvgIcon name="acg_sort_0" v-if="sort" />
|
||||
<SvgIcon name="acg_sort_1" v-else />
|
||||
<span @click="sort = !sort">
|
||||
{{ sort ? '正序' : '倒序' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-box overflow-y-auto">
|
||||
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh">
|
||||
<div class="list">
|
||||
<Episode
|
||||
showType="text"
|
||||
class="item"
|
||||
:episodeNumber="childState.episodeNumber"
|
||||
:info="item"
|
||||
v-for="item in autoList || []"
|
||||
:key="item.id"
|
||||
@click="handlePopupSwitchEpisode(item.episodeNumber)"
|
||||
></Episode>
|
||||
</div>
|
||||
</PullRefresh>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<PayForWatch
|
||||
v-if="childState.validateItem && visiblePayForWatch"
|
||||
ref="dvPayForWatch"
|
||||
:isOpen="visiblePayForWatch"
|
||||
:info="childState.validateItem"
|
||||
@close="visiblePayForWatch = false"
|
||||
@buySuccess="beforeBuySuccess"
|
||||
></PayForWatch>
|
||||
<AppSharePopup
|
||||
coverType="portrait"
|
||||
:is-open="visibleShare"
|
||||
:cover="state.info.verticalCover || state.info.horizontalCover"
|
||||
@close="visibleShare = false"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'AcgImageDetail',
|
||||
};
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRaw, watch, computed, watchEffect, provide, onDeactivated, onActivated } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import PayForWatch from '../components/pay/PayForWatch.vue';
|
||||
import tabAction from '@/utils/tabAction';
|
||||
import { AdType } from '@/enums/app';
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import MediaInfo from '../utils/mediaInfo';
|
||||
import mediaContentInfo from '../utils/mediaContentInfo';
|
||||
import Episode from '../components/episode.vue';
|
||||
import { handleCollected } from '../utils/collect';
|
||||
import { dateFormatYyyyMmDd, watchCount } from '@/utils/index';
|
||||
import { Pay_parent_on, Pay_child_on } from '../utils/pay-mitt';
|
||||
import type { PayInfo } from '../utils/pay-mitt';
|
||||
import { goAcgTagPage } from '../utils/common';
|
||||
import CommunityApi from '@/service/community';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const dvComment = ref();
|
||||
const showMoreSummary = ref(false);
|
||||
|
||||
const { state, historyEpisodeNumber, historyData, addHistory } = MediaInfo({
|
||||
dbType: HistoryVideoType.AcgImage,
|
||||
});
|
||||
Pay_parent_on({ id: route.query.id as string, state, historyData, addHistory });
|
||||
provide('historyData', historyData);
|
||||
const {
|
||||
state: childState,
|
||||
autoList,
|
||||
visibleCatalogue,
|
||||
handlePopupSwitchEpisode,
|
||||
moreParams,
|
||||
sort,
|
||||
onLoad,
|
||||
onRefresh,
|
||||
visiblePayForWatch,
|
||||
validateHasBuy,
|
||||
currentItem,
|
||||
} = mediaContentInfo({ dbType: HistoryVideoType.AcgImage, isGetHistoryInit: true, isGetInfo: false });
|
||||
|
||||
Pay_child_on(childState.id, childState);
|
||||
const { id = '' } = route.query;
|
||||
|
||||
watch(historyEpisodeNumber, (num) => {
|
||||
childState.episodeNumber = num;
|
||||
});
|
||||
const currentEpisodeName = computed(() => {
|
||||
return '开始阅读';
|
||||
});
|
||||
|
||||
const EpisodeTitle = computed(() => {
|
||||
return '全' + state.info.totalEpisode + '话';
|
||||
});
|
||||
|
||||
const tagDetails = computed(() => {
|
||||
const list = state.info.tagDetails || [];
|
||||
const seenIds = new Set<string>();
|
||||
const seenNames = new Set<string>();
|
||||
return list.filter((item) => {
|
||||
if (!item) return false;
|
||||
if (seenIds.has(item.id) || seenNames.has(item.name)) return false;
|
||||
seenIds.add(item.id);
|
||||
seenNames.add(item.name);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
const goWatch = () => {
|
||||
if (validateHasBuy(currentItem.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push({
|
||||
path: '/AcgImageDetailWatch',
|
||||
query: {
|
||||
id,
|
||||
episode: childState.episodeNumber,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
watch(dvComment, (el) => {
|
||||
if (!el) return;
|
||||
if (!id) return;
|
||||
dvComment.value[0].state.videoInfo = toRaw(state.info);
|
||||
dvComment.value[0].getCommentList();
|
||||
});
|
||||
|
||||
const beforeBuySuccess = (obj: PayInfo) => {
|
||||
if (obj.type === 'all') {
|
||||
state.info.mediaStatus.hasPaid = true;
|
||||
historyData.value.mediaStatus.hasPaid = true;
|
||||
addHistory();
|
||||
childState.list.forEach((item) => {
|
||||
item.hasBuy = true;
|
||||
});
|
||||
}
|
||||
|
||||
buySuccess(obj);
|
||||
};
|
||||
|
||||
const buySuccess = ({ type }: PayInfo) => {
|
||||
if (type === 'one') {
|
||||
const item = (childState.list as Types.Acg.MediaContentListItem[]).find(
|
||||
(item) => item.episodeNumber === currentItem.value.episodeNumber,
|
||||
);
|
||||
if (item) {
|
||||
item.hasBuy = true;
|
||||
}
|
||||
}
|
||||
|
||||
visiblePayForWatch.value = false;
|
||||
goWatch();
|
||||
};
|
||||
|
||||
let isInitCollected = false;
|
||||
watch(
|
||||
() => state.info,
|
||||
(obj) => {
|
||||
if (isInitCollected) return;
|
||||
isInitCollected = true;
|
||||
},
|
||||
);
|
||||
// 收藏
|
||||
const onToogleCollected = async () => {
|
||||
try {
|
||||
const tempIsCollect = !historyData.value.mediaStatus.hasCollected;
|
||||
handleCollected(state.info.id, tempIsCollect).then(() => {
|
||||
historyData.value.mediaStatus.hasCollected = tempIsCollect;
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
// 点赞
|
||||
const onToogleLiked = () => {
|
||||
const zan = !historyData.value.mediaStatus.hasLiked;
|
||||
|
||||
(zan ? CommunityApi.zan : CommunityApi.cancleZan)(
|
||||
zan
|
||||
? {
|
||||
type: 'image',
|
||||
objID: state.info.id,
|
||||
}
|
||||
: {
|
||||
type: 'image',
|
||||
objIDs: [state.info.id],
|
||||
},
|
||||
).then(() => {
|
||||
historyData.value.mediaStatus.hasLiked = zan;
|
||||
});
|
||||
};
|
||||
const scrollTop = ref(0);
|
||||
const onScroll = (e: Event) => {
|
||||
const currentTop = (e.target as HTMLElement).scrollTop;
|
||||
scrollTop.value = currentTop;
|
||||
};
|
||||
const headStyle = computed(() => {
|
||||
return {
|
||||
background: `rgba(0, 0, 0, ${scrollTop.value / 100})`,
|
||||
};
|
||||
});
|
||||
|
||||
const visibleShare = ref(false);
|
||||
|
||||
const openShare = () => {
|
||||
const { id, verticalCover, horizontalCover } = historyData.value;
|
||||
let obj = {
|
||||
id,
|
||||
cover: verticalCover || horizontalCover,
|
||||
};
|
||||
// globalStore.setShareInfo(obj);
|
||||
visibleShare.value = true;
|
||||
};
|
||||
const episodeBox = ref();
|
||||
let episodeBoxLeft = 0;
|
||||
let isDeactivated = false;
|
||||
const onScrollEpisodeBox = (e: Event) => {
|
||||
if (!episodeBox.value) return;
|
||||
const scrollLeft = (e.target as HTMLElement).scrollLeft;
|
||||
episodeBoxLeft = scrollLeft;
|
||||
};
|
||||
onDeactivated(() => {
|
||||
isDeactivated = true;
|
||||
});
|
||||
onActivated(() => {
|
||||
if (episodeBox.value && episodeBoxLeft) {
|
||||
episodeBox.value.scrollLeft = episodeBoxLeft;
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => childState.episodeNumber,
|
||||
(num: number) => {
|
||||
if (isDeactivated) {
|
||||
handlePopupSwitchEpisode(num);
|
||||
isDeactivated = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/mixins.scss';
|
||||
@import '../assets/style.scss';
|
||||
.detail-wrap {
|
||||
$themeBgColor2: #222324;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include vanTab();
|
||||
|
||||
--van-tab-active-text-color: rgba(255, 255, 255, 0.9);
|
||||
.borderBottomDiv {
|
||||
margin: 0 14px;
|
||||
width: calc(100% - 28px);
|
||||
}
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
// background: $themeBgColor2;
|
||||
height: 39px;
|
||||
z-index: 10;
|
||||
padding: 0 16px;
|
||||
.back {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
color: #292929;
|
||||
}
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
line-height: 24px; /* 133.333% */
|
||||
margin: 0 16px;
|
||||
}
|
||||
.bookself {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
border-radius: 11px;
|
||||
color: #ffffff99;
|
||||
.menuItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.collected {
|
||||
margin-right: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cover-box {
|
||||
width: 100%;
|
||||
height: 216px;
|
||||
padding: 11px 16px 23px;
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
align-items: flex-end;
|
||||
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000 100%);
|
||||
}
|
||||
}
|
||||
.content {
|
||||
z-index: 3;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
}
|
||||
.status {
|
||||
color: #e0e0e0;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
margin: 4px 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.info-box {
|
||||
width: 100%;
|
||||
border-radius: 24px 24px 0px 0px;
|
||||
background: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
.watch {
|
||||
padding: 16px 0 6px;
|
||||
margin-top: -50px;
|
||||
border-radius: 20px 20px 0px 0px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
.info {
|
||||
padding: 0 10px 10px;
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 10px;
|
||||
margin: 12px 0 14px;
|
||||
color: #e0e0e0;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
.tag {
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 4px 8px;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
}
|
||||
.summary {
|
||||
color: #989898;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 142.857% */
|
||||
}
|
||||
.more-summary {
|
||||
color: #efefef;
|
||||
font-size: 12px;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: #fff;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.showMoreSummary {
|
||||
svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
.count {
|
||||
color: #a7a7a7;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 180% */
|
||||
.count-item {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 24px 16px 0;
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #fff;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 14px;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
.count {
|
||||
font-size: 12px;
|
||||
color: #989898;
|
||||
font-weight: 400;
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.episode-box {
|
||||
margin: 9px 0 0;
|
||||
padding-left: 16px;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
.item {
|
||||
flex-shrink: 0;
|
||||
margin-right: 11px;
|
||||
}
|
||||
}
|
||||
// .btn {
|
||||
// display: flex;
|
||||
// width: 249px;
|
||||
// height: 39px;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// border-radius: 19px;
|
||||
// background: $themeBgLgColor;
|
||||
|
||||
// color: #fff;
|
||||
// font-size: 16px;
|
||||
// font-weight: 500;
|
||||
// margin: 0 auto;
|
||||
// }
|
||||
}
|
||||
|
||||
.play-list {
|
||||
@include categoryWrap();
|
||||
border: none;
|
||||
|
||||
margin-top: 0;
|
||||
padding-top: 16px;
|
||||
.title {
|
||||
padding: 0 14px;
|
||||
@include categoryTitle();
|
||||
}
|
||||
|
||||
.list {
|
||||
@include categoryList();
|
||||
padding-left: 14px;
|
||||
|
||||
.item {
|
||||
height: 118px;
|
||||
flex-shrink: 0;
|
||||
width: 97px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 97px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
z-index: 1;
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
}
|
||||
.count {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 97px;
|
||||
height: 26px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.play {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.title-box {
|
||||
width: 100%;
|
||||
height: 21px;
|
||||
padding: 0 5px;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
flex: 1;
|
||||
color: $textColor;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
@include textEllipsis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.action {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 1) 0%, #000 100%);
|
||||
height: 68px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
@include SafeArea('padding', 'bottom', 0);
|
||||
display: flex;
|
||||
.btn {
|
||||
width: 108px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
height: 44px;
|
||||
color: #989898;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
justify-content: center;
|
||||
&.go-page {
|
||||
background: $themeColor;
|
||||
width: 223px;
|
||||
color: #fff;
|
||||
}
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.play-list-box {
|
||||
$paddingBottom: 38px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16px 16px 0px 0px;
|
||||
background: $popupBg;
|
||||
padding: 18px 16px $paddingBottom;
|
||||
@include SafeArea('padding', 'bottom', 0);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: $textColor;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
|
||||
> .title {
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
line-height: 25px; /* 140% */
|
||||
margin: 16px 0 0;
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
svg {
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
margin-right: 4px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
span {
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
}
|
||||
}
|
||||
}
|
||||
.line {
|
||||
width: 36px;
|
||||
height: 5px;
|
||||
border-radius: 100px;
|
||||
background: #989898;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.list-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.recommendedBox {
|
||||
padding: 0 16px;
|
||||
:deep(.van-tabs__wrap) {
|
||||
// top: 39px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,761 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="state.info.id"
|
||||
ref="dvCaricatureWrap"
|
||||
class="caricature-wrap overflow-y-auto"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@scroll="onContentScroll"
|
||||
>
|
||||
<Transition name="top">
|
||||
<div v-show="visibleHead" class="nav" @click.stop="visibleHead = false">
|
||||
<SvgIcon name="back_white" class="back" @click.stop="$router.go(-1)" />
|
||||
<span class="title">{{ currentItem.name }}</span>
|
||||
<div class="right flex-vcenter">
|
||||
<div
|
||||
class="collect"
|
||||
:class="{ collected: historyData.mediaStatus?.hasCollected }"
|
||||
@click.stop="onToogleCollected"
|
||||
>
|
||||
<SvgIcon name="acg-collect-img-0" class="svg-icon" v-if="!historyData.mediaStatus?.hasCollected" />
|
||||
<SvgIcon name="acg-collect-img-1" class="svg-icon" style="color: #f52c56" v-else />
|
||||
</div>
|
||||
<SvgIcon class="share" name="acg-share1" @click.stop="openShare" />
|
||||
</div>
|
||||
<!-- <SvgIcon class="back right" name="acg_catalogue" @click.stop="visibleCatalogue = true" /> -->
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="like-box" :class="isToolShow ? 'top150' : ''" v-show="visibleHead" @click.stop="onToogleLiked">
|
||||
<SvgIcon v-if="historyData?.mediaStatus?.hasLiked" name="acg-heart-1" />
|
||||
<SvgIcon v-else name="acg-heart-0" />
|
||||
喜欢
|
||||
</div>
|
||||
<div class="list" @click="onClick">
|
||||
<ImgObserver
|
||||
v-for="(url, index) in currentEpisode"
|
||||
class="item"
|
||||
:style="getImgHeight(url)"
|
||||
:class="{ move: visibleCatalogue }"
|
||||
:key="url"
|
||||
:index="index"
|
||||
:url="url"
|
||||
:init="index - currentProgress <= 1"
|
||||
@updateIndex="updateIndex"
|
||||
@updateSize="updateSize"
|
||||
></ImgObserver>
|
||||
</div>
|
||||
<div
|
||||
v-show="running"
|
||||
class="running-box"
|
||||
@click.stop="
|
||||
{
|
||||
running = false;
|
||||
visibleHead = true;
|
||||
}
|
||||
"
|
||||
>
|
||||
<img class="running" src="@/assets/gif/running.gif" />
|
||||
</div>
|
||||
<Transition name="bottom">
|
||||
<div v-show="visibleHead && !running" ref="bottomRef" class="action">
|
||||
<div class="box">
|
||||
<div class="func" @click.stop="visibleCatalogue = true">
|
||||
<SvgIcon class="icon" name="acg_catalogue" />
|
||||
目录
|
||||
</div>
|
||||
<div class="func" @click.stop="visibleComment = true">
|
||||
<SvgIcon class="icon" name="acg_comment" />
|
||||
评论
|
||||
</div>
|
||||
<div class="func" @click.stop="isToolShow = !isToolShow">
|
||||
<SvgIcon class="icon" :name="isToolShow ? 'ddown' : 'acg_progress'" />
|
||||
进度
|
||||
</div>
|
||||
<div class="func" @click.stop="running = true">
|
||||
<SvgIcon class="icon" name="acg_automatic" />
|
||||
自动
|
||||
</div>
|
||||
<!-- <div class="prev" @click.stop="switchEpisodeNumber(-1)">
|
||||
<SvgIcon class="icon" name="acg_prev" />
|
||||
上一话
|
||||
</div>
|
||||
<div class="next" @click.stop="switchEpisodeNumber(1)">
|
||||
<SvgIcon class="icon" name="acg_prev" />
|
||||
下一话
|
||||
</div> -->
|
||||
</div>
|
||||
<Transition name="van-slide-up">
|
||||
<div v-show="isToolShow && !running" class="tools">
|
||||
<div class="prev" @click.stop="switchEpisodeNumber(-1)">
|
||||
<SvgIcon class="icon" name="backWhite" />
|
||||
<span>上一篇</span>
|
||||
</div>
|
||||
<div class="slider">
|
||||
<van-slider
|
||||
style="--van-slider-button-background: #ffd300"
|
||||
:model-value="progressRatio"
|
||||
@update:model-value="(v) => (progressRatio = v)"
|
||||
active-color="#FFD300"
|
||||
inactive-color="#fff3"
|
||||
button-size="12px"
|
||||
bar-height="4px"
|
||||
@change="reposition"
|
||||
></van-slider>
|
||||
</div>
|
||||
<div class="next" @click.stop="switchEpisodeNumber(1)">
|
||||
<SvgIcon class="icon" name="backWhite" />
|
||||
<span>下一篇</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- <div v-show="!visibleHead" class="tips">
|
||||
第{{ state.episodeNumber }}话 {{ currentProgress }}/{{ currentEpisode.length }}
|
||||
</div> -->
|
||||
</div>
|
||||
<Loading v-else />
|
||||
<van-popup v-model:show="visibleCatalogue" position="left" :style="{ width: '50%', height: '100%' }">
|
||||
<div class="play-list-box">
|
||||
<div class="header">
|
||||
<!-- <SvgIcon class="close" name="popup_close" @click.stop="visibleCatalogue = false" /> -->
|
||||
<div class="header-title ellipsis">{{ historyData.title }}</div>
|
||||
<div class="sort flex-center-between">
|
||||
<span class="menu-info">
|
||||
共{{ historyData.totalEpisode }}话 {{ getUpdateStatusText(historyData.updateStatus) }}
|
||||
</span>
|
||||
<span class="btn" @click="sort = !sort">
|
||||
<SvgIcon v-show="sort" class="section" name="acg_sort_0" />
|
||||
<SvgIcon v-show="!sort" class="section" name="acg_sort_1" />
|
||||
<span>{{ sort ? '正序' : '倒序' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list overflow-y-auto">
|
||||
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh" finishedText="">
|
||||
<div
|
||||
:id="`section_${item.episodeNumber}`"
|
||||
class="item"
|
||||
:class="{ active: item.episodeNumber === state.episodeNumber }"
|
||||
v-for="item in autoList"
|
||||
:key="item.id"
|
||||
@click="switchEpisodeNumberCatalogue(item)"
|
||||
>
|
||||
{{ item.episodeNumber }}.{{ item.name }}
|
||||
</div>
|
||||
</PullRefresh>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<PayForWatch
|
||||
v-if="state.info.id"
|
||||
ref="dvPayForWatch"
|
||||
:isOpen="visiblePayForWatch"
|
||||
:info="state.validateItem.id ? state.validateItem : state.info"
|
||||
@close="closePayForWatch"
|
||||
@buySuccess="beforeBuySuccess"
|
||||
></PayForWatch>
|
||||
|
||||
<AppSharePopup
|
||||
:is-open="visibleShare"
|
||||
coverType="portrait"
|
||||
@close="visibleShare = false"
|
||||
:cover="historyData.verticalCover || state.info.cover"
|
||||
/>
|
||||
|
||||
<CommentBox
|
||||
v-if="state.info.id"
|
||||
ref="dvComment"
|
||||
class="comment"
|
||||
:isOpen="visibleComment"
|
||||
objType="cartoon"
|
||||
@close="visibleComment = false"
|
||||
:showCollected="false"
|
||||
></CommentBox>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'AcgImageDetailWatch',
|
||||
};
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed, onDeactivated, onUnmounted, onBeforeUnmount, provide, reactive, toRaw } from 'vue';
|
||||
import PayForWatch from '../components/pay/PayForWatch.vue';
|
||||
import mediaContentInfo from '../utils/mediaContentInfo';
|
||||
import ImgObserver from '../components/ImgObserver.vue';
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import { showToast } from 'vant';
|
||||
import { Pay_emit } from '../utils/pay-mitt';
|
||||
import type { PayInfo } from '../utils/pay-mitt';
|
||||
import { handleCollected, Collected_emit } from '../utils/collect';
|
||||
import CommunityApi from '@/service/community';
|
||||
import { getUpdateStatusText } from '../utils/common';
|
||||
import { debounce } from '@/utils';
|
||||
// import { useGlobalStore } from '@/stores/global';
|
||||
// const globalStore = useGlobalStore();
|
||||
const running = ref(false);
|
||||
const isToolShow = ref(false);
|
||||
const visibleHead = ref(true);
|
||||
const progressRatio = ref(0);
|
||||
const contentMaxScrollTop = ref(0);
|
||||
const contentScrollTop = ref(0);
|
||||
const visibleSides = ref(true);
|
||||
const bottomRef = ref();
|
||||
const {
|
||||
state,
|
||||
currentItem,
|
||||
visibleCatalogue,
|
||||
dvPayForWatch,
|
||||
visiblePayForWatch,
|
||||
closePayForWatch,
|
||||
buySuccess,
|
||||
switchEpisodeNumber,
|
||||
autoList,
|
||||
moreParams,
|
||||
onLoad,
|
||||
onRefresh,
|
||||
switchEpisodeNumberCatalogue,
|
||||
historyData,
|
||||
addHistoryReadData,
|
||||
sort,
|
||||
} = mediaContentInfo({ isGetHistoryInit: true, dbType: HistoryVideoType.AcgImage });
|
||||
provide('historyData', historyData);
|
||||
const dvCaricatureWrap = ref();
|
||||
const currentProgress = ref(1);
|
||||
const currentEpisode = computed(() => {
|
||||
return state.info.urlSet || [];
|
||||
});
|
||||
const reposition = () => {
|
||||
if (dvCaricatureWrap.value) {
|
||||
dvCaricatureWrap.value.scrollTop = (contentMaxScrollTop.value * progressRatio.value) / 100;
|
||||
}
|
||||
};
|
||||
const currentHistoryReadData = computed(() => {
|
||||
const arr = historyData.value.historyReadData || [];
|
||||
return arr.find((item) => item.episodeNumber === state.episodeNumber) || { scrollTop: 0, imgs: {} };
|
||||
});
|
||||
watch(
|
||||
dvCaricatureWrap,
|
||||
() => {
|
||||
dvCaricatureWrap.value.scrollTop = currentHistoryReadData.value.scrollTop || 0;
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
const onContentScroll = (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
contentScrollTop.value = target.scrollTop;
|
||||
contentMaxScrollTop.value = target.scrollHeight - target.offsetHeight;
|
||||
if (contentMaxScrollTop.value > 0) {
|
||||
progressRatio.value = (contentScrollTop.value / contentMaxScrollTop.value) * 100;
|
||||
}
|
||||
const progress =
|
||||
contentMaxScrollTop.value <= 0 ? 100 : Math.max(1, Math.min(100, Math.ceil((contentScrollTop.value / contentMaxScrollTop.value) * 100)));
|
||||
addHistoryReadData(state.episodeNumber, {
|
||||
progress,
|
||||
scrollTop: contentScrollTop.value,
|
||||
});
|
||||
const isEnd = contentScrollTop.value + 10 >= contentMaxScrollTop.value;
|
||||
if (isEnd) {
|
||||
console.log('阅读结束')
|
||||
running.value = false;
|
||||
visibleSides.value = true;
|
||||
bottomRef.value?.showTool();
|
||||
return;
|
||||
}
|
||||
if (!running.value) {
|
||||
if (contentScrollTop.value < 100) {
|
||||
visibleSides.value = true;
|
||||
}
|
||||
// visibleSides.value = contentScrollTop.value < 100
|
||||
}
|
||||
};
|
||||
const getImgHeight = (url: string) => {
|
||||
const imgs = (currentHistoryReadData.value.imgs || {})[url];
|
||||
const { width = 0, height = 0 } = imgs || {};
|
||||
if (width && height) {
|
||||
return {
|
||||
height: (window.innerWidth / width) * height + 'px',
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
const updateIndex = ({ index, url }: { index: number; url: string }) => {
|
||||
let progress = 0;
|
||||
if (dvCaricatureWrap.value.scrollTop === 0) {
|
||||
progress = 1;
|
||||
} else {
|
||||
progress = Number(index) + 1;
|
||||
}
|
||||
currentProgress.value = progress;
|
||||
addHistoryReadData(state.episodeNumber, {
|
||||
progress: Math.ceil((progress / currentEpisode.value.length) * 100),
|
||||
progressUrl: url,
|
||||
scrollTop: dvCaricatureWrap.value.scrollTop,
|
||||
});
|
||||
};
|
||||
const updateSize = (imgObj: any) => {
|
||||
addHistoryReadData(state.episodeNumber, {}, imgObj);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => state.episodeNumber,
|
||||
(num) => {
|
||||
addHistoryReadData(num, { progress: 1 });
|
||||
if (dvCaricatureWrap.value) {
|
||||
dvCaricatureWrap.value.scrollTop = 0;
|
||||
}
|
||||
visibleHead.value = true;
|
||||
},
|
||||
);
|
||||
addHistoryReadData(state.episodeNumber + 1, {});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
addHistoryReadData(state.episodeNumber, {
|
||||
scrollTop: dvCaricatureWrap.value.scrollTop,
|
||||
});
|
||||
|
||||
addHistoryReadData(state.episodeNumber + 1, {});
|
||||
});
|
||||
// 点赞
|
||||
const onToogleLiked = () => {
|
||||
const zan = !historyData.value.mediaStatus.hasLiked;
|
||||
|
||||
(zan ? CommunityApi.zan : CommunityApi.cancleZan)(
|
||||
zan
|
||||
? {
|
||||
type: 'image',
|
||||
objID: historyData.value.id,
|
||||
}
|
||||
: {
|
||||
type: 'image',
|
||||
objIDs: [historyData.value.id],
|
||||
},
|
||||
).then(() => {
|
||||
historyData.value.mediaStatus.hasLiked = zan;
|
||||
});
|
||||
};
|
||||
const onClick = (e: MouseEvent) => {
|
||||
if (running.value) {
|
||||
visibleHead.value = !visibleHead.value;
|
||||
return;
|
||||
}
|
||||
const { clientY } = e;
|
||||
let scrollTop = 0;
|
||||
const OneThird = window.innerHeight / 3.5;
|
||||
if (clientY < OneThird) {
|
||||
scrollTop = -300;
|
||||
} else if (clientY > OneThird * 2) {
|
||||
scrollTop = 300;
|
||||
} else {
|
||||
visibleHead.value = !visibleHead.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollTop !== 0) {
|
||||
visibleHead.value = false;
|
||||
dvCaricatureWrap.value.scrollTo({ top: scrollTop + dvCaricatureWrap.value.scrollTop, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
let lastScrollTop = 0;
|
||||
const onTouchStart = (e: TouchEvent) => {
|
||||
const { clientY } = e.targetTouches[0];
|
||||
lastScrollTop = clientY;
|
||||
};
|
||||
const onTouchMove = (e: TouchEvent) => {
|
||||
const { clientY } = e.targetTouches[0];
|
||||
const distance = lastScrollTop - clientY;
|
||||
if (distance > 30) {
|
||||
visibleHead.value = false;
|
||||
return;
|
||||
}
|
||||
if (distance < -30) {
|
||||
visibleHead.value = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// watch(visibleCatalogue, (bool) => {
|
||||
// if (bool) {
|
||||
// visibleHead.value = false;
|
||||
// }
|
||||
// });
|
||||
|
||||
const scrollSpeed = 1;
|
||||
let moveInterval = null as ReturnType<typeof setInterval> | null;
|
||||
|
||||
const cancelAutoMove = () => {
|
||||
moveInterval && clearInterval(moveInterval);
|
||||
moveInterval = null;
|
||||
running.value = false;
|
||||
};
|
||||
|
||||
const autoMove = () => {
|
||||
const { scrollTop, scrollHeight } = dvCaricatureWrap.value;
|
||||
|
||||
if (scrollTop >= scrollHeight) {
|
||||
// 停止自动移动
|
||||
cancelAutoMove();
|
||||
return;
|
||||
}
|
||||
|
||||
let newPosition = scrollTop + scrollSpeed;
|
||||
|
||||
dvCaricatureWrap.value.scrollTo(0, newPosition);
|
||||
};
|
||||
|
||||
onDeactivated(() => {
|
||||
cancelAutoMove();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
cancelAutoMove();
|
||||
});
|
||||
|
||||
watch(running, (bool) => {
|
||||
if (bool) {
|
||||
moveInterval = setInterval(autoMove, 10);
|
||||
showToast({
|
||||
message: '自动阅读模式',
|
||||
onClose: () => {
|
||||
if (moveInterval) {
|
||||
visibleHead.value = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
cancelAutoMove();
|
||||
}
|
||||
});
|
||||
|
||||
const beforeBuySuccess = (obj: PayInfo) => {
|
||||
Pay_emit(historyData.value.id, obj);
|
||||
buySuccess(obj.type);
|
||||
};
|
||||
|
||||
// 收藏
|
||||
const onToogleCollected = async () => {
|
||||
try {
|
||||
const {
|
||||
id,
|
||||
mediaStatus: { hasCollected },
|
||||
} = historyData.value;
|
||||
const tempIsCollect = !hasCollected;
|
||||
handleCollected(id, tempIsCollect).then(() => {
|
||||
historyData.value.mediaStatus.hasCollected = tempIsCollect;
|
||||
Collected_emit(id, tempIsCollect);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const visibleShare = ref(false);
|
||||
const openShare = () => {
|
||||
const { id, verticalCover, horizontalCover } = historyData.value;
|
||||
let obj = {
|
||||
id,
|
||||
cover: verticalCover || horizontalCover,
|
||||
};
|
||||
// globalStore.setShareInfo(obj);
|
||||
visibleShare.value = true;
|
||||
};
|
||||
|
||||
const visibleComment = ref(false);
|
||||
const dvComment = ref();
|
||||
watch(dvComment, (el) => {
|
||||
if (!el) return;
|
||||
if (!state.info.id || !dvComment.value?.state) return;
|
||||
console.log(state, 'state.info', state.validateItem);
|
||||
dvComment.value.state.videoInfo = toRaw({
|
||||
...state.info,
|
||||
id: state.id,
|
||||
});
|
||||
dvComment.value.getCommentList();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/mixins.scss';
|
||||
.caricature-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// @include SafeArea('padding', 'bottom', 60px);
|
||||
.like-box {
|
||||
position: fixed;
|
||||
bottom: 81px;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
border-radius: 999px 0px 0px 999px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
width: 86px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
svg {
|
||||
margin-right: 5px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
.top150 {
|
||||
bottom: 150px !important;
|
||||
}
|
||||
.nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background: #181824;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 6px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
.back {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
color: #fff;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.title {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
.right {
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
.collect {
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
.share {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
margin: 0 16px 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .list {
|
||||
padding-top: 1px;
|
||||
.item {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
height: auto;
|
||||
|
||||
:deep(.animationImg) {
|
||||
overflow: visible;
|
||||
.loading {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
position: fixed;
|
||||
box-sizing: content-box;
|
||||
@include SafeArea('padding', 'bottom', 0);
|
||||
// left: 88px;
|
||||
// right: 88px;
|
||||
height: 48px;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
transition: all 0.3s;
|
||||
|
||||
.box {
|
||||
z-index: 2;
|
||||
border-radius: 100px;
|
||||
padding: 12px 16px;
|
||||
// background: rgba(0, 0, 0, 0.60)
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
font-size: 12px;
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
&.next .icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .tips {
|
||||
// position: fixed;
|
||||
// @include SafeAreaFixed('bottom', 40px);
|
||||
// right: 20px;
|
||||
// border-radius: 8px;
|
||||
// background: rgba(0, 0, 0, 0.6);
|
||||
// padding: 8px 12px;
|
||||
// color: #f1f1f1;
|
||||
// font-size: 14px;
|
||||
// font-weight: 500;
|
||||
// }
|
||||
}
|
||||
|
||||
.play-list-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0f0f0f;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
.header {
|
||||
padding-top: 85px;
|
||||
width: 100%;
|
||||
.close {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.header-title {
|
||||
width: 100%;
|
||||
text-indent: 16px;
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
font-family: Roboto;
|
||||
}
|
||||
.sort {
|
||||
flex-shrink: 0;
|
||||
margin: 16px 0 14px;
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #141414;
|
||||
background: #f68804;
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.section {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
.menu-info {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
padding: 0 16px;
|
||||
margin-bottom: 11px;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
@include textEllipsis();
|
||||
&.active {
|
||||
color: #f68804;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.running-box {
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 100;
|
||||
.running {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.tools {
|
||||
position: absolute;
|
||||
top: -70px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
padding: 0 16px;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.prev,
|
||||
.next {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.next .icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.slider {
|
||||
width: 245px;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
.top-enter-active,
|
||||
.top-leave-active,
|
||||
.bottom-enter-active,
|
||||
.bottom-leave-active {
|
||||
will-change: transform;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
.top-enter-from,
|
||||
.top-leave-to {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.bottom-enter-from,
|
||||
.bottom-leave-to {
|
||||
transform: translateY(200%);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user