初始化
This commit is contained in:
@@ -0,0 +1,542 @@
|
||||
import { reactive, ref, ComputedRef, watchEffect, watch, nextTick, computed, onBeforeUnmount } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { HistoryVideoType } from '@/enums/app';
|
||||
import indexDB from './indexDB';
|
||||
import { showToast } from 'vant';
|
||||
import AcgApi from '@/service/acg';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { freeSet } from './common';
|
||||
interface ReadData {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default ({
|
||||
mediaId = '',
|
||||
isScrollTop = true,
|
||||
isGetInfo = true,
|
||||
isUpdate = true,
|
||||
isNeedHistory = true,
|
||||
isGetHistoryInit = false,
|
||||
pageSize = 30,
|
||||
dbType = HistoryVideoType.AcgImage,
|
||||
}) => {
|
||||
const userStore = useUserStore();
|
||||
const { openLoading, closeLoading } = useLoading();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { id, episodeNumber, title } = route.query;
|
||||
|
||||
const state = reactive({
|
||||
list: [] as Types.Acg.MediaContentListItem[],
|
||||
info: {} as Types.Acg.MediaContentInfo,
|
||||
validateItem: {} as Types.Acg.MediaContentListItem,
|
||||
episodeNumber: Number(episodeNumber || '1'),
|
||||
id: (mediaId || id) as string,
|
||||
title,
|
||||
gradingGroupIndex: 0,
|
||||
});
|
||||
|
||||
const { historyData, historyEpisodeNumber, getHistoryData, setHistoryData } = indexDB(dbType, true);
|
||||
const addHistory = (num: number) => {
|
||||
if (num === -1) {
|
||||
num = state.episodeNumber;
|
||||
}
|
||||
const data = { ...historyData.value, historyEpisodeNumber: num, historyReadData };
|
||||
setHistoryData(data, dbType);
|
||||
};
|
||||
|
||||
const historyReadData = [] as Types.Acg.ReadData[];
|
||||
|
||||
const addHistoryReadData = (num: number, obj: ReadData, imgObj?: { url: string; width: number; height: number }) => {
|
||||
const listItem = state.list.find((item) => item.episodeNumber === num);
|
||||
if (!listItem) return;
|
||||
|
||||
const index = (historyReadData || []).findIndex((item) => item.episodeNumber === num);
|
||||
const isHas = index > -1;
|
||||
|
||||
const imgs = isHas ? historyReadData[index].imgs || {} : {};
|
||||
if (imgObj?.url) {
|
||||
imgs[imgObj?.url] = {
|
||||
width: imgObj?.width,
|
||||
height: imgObj?.height,
|
||||
};
|
||||
}
|
||||
let item = {};
|
||||
if (isHas) {
|
||||
item = historyReadData.splice(index, 1)[0];
|
||||
}
|
||||
|
||||
const newItem = Object.assign({}, { ...item }, { ...listItem, ...obj, imgs });
|
||||
historyReadData.push(newItem);
|
||||
};
|
||||
|
||||
if (isUpdate) {
|
||||
onBeforeUnmount(() => {
|
||||
nextTick(() => {
|
||||
addHistory(-1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const currentItem = computed(() => {
|
||||
if (state.list.length === 0) return {};
|
||||
return state.list.find((item) => item.episodeNumber === state.episodeNumber) || {};
|
||||
}) as ComputedRef<Types.Acg.MediaContentListItem>;
|
||||
|
||||
let initScrollLeft = true;
|
||||
watch(currentItem, () => {
|
||||
if (initScrollLeft) {
|
||||
goSectionScrollLeft();
|
||||
initScrollLeft = false;
|
||||
}
|
||||
if (['AcgTextDetail', 'AcgImageDetail'].includes((route.name as string) || '')) {
|
||||
return;
|
||||
}
|
||||
validateHasBuy(currentItem.value);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => state.episodeNumber,
|
||||
() => {
|
||||
addHistory(state.episodeNumber);
|
||||
},
|
||||
);
|
||||
|
||||
const getPageNumber = () => {
|
||||
return Math.ceil(state.episodeNumber / 30);
|
||||
};
|
||||
const pageNumber = getPageNumber();
|
||||
|
||||
const params = {
|
||||
prevPageNumber: pageNumber,
|
||||
nextPageNumber: pageNumber,
|
||||
pageSize,
|
||||
};
|
||||
|
||||
const moreParams = reactive({
|
||||
loading: true,
|
||||
refreshing: false,
|
||||
finished: false,
|
||||
isNoData: false,
|
||||
error: false,
|
||||
isHigehtMax: false,
|
||||
});
|
||||
|
||||
const moreParams_finished = {
|
||||
prev: false,
|
||||
next: false,
|
||||
};
|
||||
|
||||
const getList = async (type: 'prev' | 'next', pageNumber?: number) => {
|
||||
try {
|
||||
const tempParams = {
|
||||
mediaId: state.id,
|
||||
pageSize: params.pageSize,
|
||||
pageNumber: pageNumber ? pageNumber : type === 'prev' ? params.prevPageNumber : params.nextPageNumber,
|
||||
};
|
||||
let res: any = await AcgApi.getMediaContentList(tempParams);
|
||||
if ((res.list || []).length === 0 && state.list.length === 0 && tempParams.pageNumber !== 1) {
|
||||
params.prevPageNumber = 1;
|
||||
params.nextPageNumber = 1;
|
||||
tempParams.pageNumber = 1;
|
||||
res = await AcgApi.getMediaContentList(tempParams);
|
||||
}
|
||||
|
||||
historyData.value.currentEpisode = res.total;
|
||||
|
||||
moreParams.loading = false;
|
||||
moreParams.refreshing = false;
|
||||
|
||||
let list = res.list || [];
|
||||
if (list.length > 0) {
|
||||
const reList = (list || []).reduce(
|
||||
(acc: Types.Acg.MediaContentListItem[], current: Types.Acg.MediaContentListItem) => {
|
||||
const x = acc.find((item) => item.episodeNumber === current.episodeNumber);
|
||||
if (!x) {
|
||||
return acc.concat([current]);
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
if (isInitGetList && res.hasNext && list.length !== reList.length) {
|
||||
list = reList;
|
||||
onLoad();
|
||||
}
|
||||
}
|
||||
isInitGetList = false;
|
||||
|
||||
if (state.list.length === 0) {
|
||||
state.list = list;
|
||||
} else {
|
||||
state.list = state.list.concat(list);
|
||||
}
|
||||
if (tempParams.pageNumber == 1 && state.list.length == 0) {
|
||||
moreParams.isNoData = true;
|
||||
}
|
||||
if (!res.hasNext) {
|
||||
moreParams_finished[type] = true;
|
||||
moreParams.finished = type === 'next' && sort.value;
|
||||
}
|
||||
return list;
|
||||
} catch (e) {
|
||||
moreParams.loading = false;
|
||||
moreParams.error = true;
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const onLoad = () => {
|
||||
moreParams.loading = true;
|
||||
if (sort.value) {
|
||||
params.nextPageNumber += 1;
|
||||
getList('next');
|
||||
} else {
|
||||
if (params.prevPageNumber === 1) {
|
||||
moreParams.loading = false;
|
||||
return;
|
||||
}
|
||||
params.prevPageNumber -= 1;
|
||||
getList('prev');
|
||||
}
|
||||
};
|
||||
|
||||
const onRefresh = () => {
|
||||
if (sort.value) {
|
||||
if (params.prevPageNumber == 1) {
|
||||
moreParams.refreshing = false;
|
||||
return;
|
||||
}
|
||||
params.prevPageNumber -= 1;
|
||||
moreParams.refreshing = true;
|
||||
moreParams.finished = false;
|
||||
getList('prev');
|
||||
} else {
|
||||
if (moreParams_finished['next']) {
|
||||
moreParams.refreshing = false;
|
||||
return;
|
||||
}
|
||||
params.nextPageNumber += 1;
|
||||
moreParams.refreshing = true;
|
||||
getList('next');
|
||||
}
|
||||
};
|
||||
let isInitGetList = true;
|
||||
const init = () => {
|
||||
getList('next');
|
||||
};
|
||||
|
||||
if (isNeedHistory) {
|
||||
getHistoryData(state.id).then(() => {
|
||||
historyReadData.push(...JSON.parse(JSON.stringify(historyData.value.historyReadData || '[]')));
|
||||
if (isGetHistoryInit) {
|
||||
if (!episodeNumber) {
|
||||
state.episodeNumber = historyData.value.historyEpisodeNumber || state.episodeNumber;
|
||||
}
|
||||
|
||||
const pageNumber = getPageNumber();
|
||||
|
||||
params.prevPageNumber = pageNumber;
|
||||
params.nextPageNumber = pageNumber;
|
||||
}
|
||||
|
||||
init();
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
const getContent = async () => {
|
||||
if (state.info.id) {
|
||||
openLoading();
|
||||
}
|
||||
|
||||
await AcgApi.getMediaContentInfo({ id: currentItem.value.id }).then((res) => {
|
||||
state.info = res;
|
||||
});
|
||||
|
||||
closeLoading();
|
||||
};
|
||||
|
||||
if (isGetInfo) {
|
||||
watch(
|
||||
() => currentItem.value.id,
|
||||
(newId, oldId) => {
|
||||
if (!newId || newId === oldId) return;
|
||||
getContent();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const statusText = computed(() => {
|
||||
switch (historyData.value.updateStatus) {
|
||||
case 1:
|
||||
return '连载中';
|
||||
case 2:
|
||||
return '已完结';
|
||||
default:
|
||||
return '已完结';
|
||||
}
|
||||
});
|
||||
|
||||
//目录
|
||||
const visibleCatalogue = ref(false);
|
||||
const sort = ref(true);
|
||||
const autoList = computed(() => {
|
||||
return (state.list || []).sort(
|
||||
sort.value ? (a, b) => a.episodeNumber - b.episodeNumber : (a, b) => b.episodeNumber - a.episodeNumber,
|
||||
);
|
||||
});
|
||||
|
||||
const scrollToElement = (elementId: string) => {
|
||||
const element = document.getElementById(elementId) as HTMLElement;
|
||||
if (!element) return;
|
||||
const parent = element.closest('.list') as HTMLElement;
|
||||
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
|
||||
const offsetTop = elementRect.top - parentRect.top;
|
||||
const offsetBottom = elementRect.bottom - parentRect.bottom;
|
||||
|
||||
if (offsetTop < 0 || offsetBottom > 0) {
|
||||
// parent.scrollTop = element.offsetTop - (parent.offsetHeight - element.offsetHeight) / 2;
|
||||
parent.scrollTop = element.offsetTop - parent.offsetHeight / 2;
|
||||
}
|
||||
};
|
||||
const goSectionScrollTop = () => {
|
||||
if (!isScrollTop) return;
|
||||
nextTick(() => {
|
||||
scrollToElement(`section_${state.episodeNumber}`);
|
||||
});
|
||||
};
|
||||
|
||||
let isInit = false;
|
||||
|
||||
watchEffect(() => {
|
||||
if (!visibleCatalogue.value) return;
|
||||
|
||||
if (isInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
isInit = true;
|
||||
|
||||
goSectionScrollTop();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => sort.value,
|
||||
() => {
|
||||
moreParams.finished = moreParams_finished[sort.value ? 'next' : 'prev'];
|
||||
goSectionScrollTop();
|
||||
},
|
||||
);
|
||||
|
||||
//支付
|
||||
const dvPayForWatch = ref();
|
||||
const visiblePayForWatch = ref(false);
|
||||
|
||||
const validatePermission = () => {
|
||||
// 购买优先
|
||||
if (historyData.value.id && historyData.value.mediaStatus && historyData.value.mediaStatus.hasPaid) return true;
|
||||
// 会员
|
||||
if (historyData.value.permission === 0 && userStore.isVip) return true;
|
||||
// 付费
|
||||
if (historyData.value.permission === 1) {
|
||||
if (!historyData.value.price) {
|
||||
return true;
|
||||
}
|
||||
if (userStore.userInfo.vipLevel >= 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 免费 || 前几集免费
|
||||
if (historyData.value.permission === 2 || historyData.value.freeEpisode >= state.validateItem.episodeNumber)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const validateHasBuy = (item: Types.Acg.MediaContentListItem) => {
|
||||
state.validateItem = item;
|
||||
if (!validatePermission()) {
|
||||
visiblePayForWatch.value = true;
|
||||
// showToast('付费章节,购买后才能观看喔');
|
||||
return true;
|
||||
}
|
||||
visiblePayForWatch.value = false;
|
||||
return false;
|
||||
};
|
||||
|
||||
const buySuccess = (type = 'one') => {
|
||||
state.info.hasBuy = true;
|
||||
if (state.validateItem.id) {
|
||||
state.validateItem.hasBuy = true;
|
||||
state.episodeNumber = state.validateItem.episodeNumber;
|
||||
}
|
||||
visiblePayForWatch.value = false;
|
||||
visibleCatalogue.value = false;
|
||||
if (type === 'all') {
|
||||
state.list.forEach((item) => {
|
||||
item.hasBuy = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// watch(
|
||||
// dvPayForWatch,
|
||||
// (el) => {
|
||||
// debugger;
|
||||
// if (!el) return;
|
||||
// validateHasBuy(currentItem.value);
|
||||
// },
|
||||
// // {
|
||||
// // once: true,
|
||||
// // },
|
||||
// );
|
||||
|
||||
const closePayForWatch = () => {
|
||||
if (validatePermission()) {
|
||||
visiblePayForWatch.value = false;
|
||||
return;
|
||||
}
|
||||
router.back();
|
||||
};
|
||||
|
||||
const loadEpisodeList = async (num: number, type: 'prev' | 'next') => {
|
||||
const item = state.list.find((item) => item.episodeNumber === num);
|
||||
if (item) return item;
|
||||
|
||||
openLoading();
|
||||
await getList(type);
|
||||
closeLoading();
|
||||
|
||||
return state.list.find((item) => item.episodeNumber === num);
|
||||
};
|
||||
|
||||
const replaceRouter = (num: number) => {
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
episodeNumber: num,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const switchEpisodeNumber = async (num: number, isValidate = true) => {
|
||||
|
||||
if (num === -1) {
|
||||
if (state.episodeNumber !== 1) {
|
||||
const prevNum = state.episodeNumber - 1;
|
||||
const item = await loadEpisodeList(prevNum, 'prev');
|
||||
if (isValidate) {
|
||||
if (validateHasBuy(item as Types.Acg.MediaContentListItem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state.episodeNumber = prevNum;
|
||||
if (dbType === HistoryVideoType.AcgText) {
|
||||
addHistoryReadData(prevNum, { progress: 0, scrollTop: 0 });
|
||||
addHistory(prevNum);
|
||||
}
|
||||
replaceRouter(prevNum);
|
||||
return true;
|
||||
} else {
|
||||
showToast('这是第一章了喔~');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (state.episodeNumber < historyData.value.currentEpisode) {
|
||||
const nextNum = state.episodeNumber + 1;
|
||||
const item = await loadEpisodeList(nextNum, 'next');
|
||||
if (isValidate) {
|
||||
if (validateHasBuy(item as Types.Acg.MediaContentListItem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state.episodeNumber = nextNum;
|
||||
if (dbType === HistoryVideoType.AcgText) {
|
||||
addHistoryReadData(nextNum, { progress: 0, scrollTop: 0 });
|
||||
addHistory(nextNum);
|
||||
}
|
||||
replaceRouter(nextNum);
|
||||
return true;
|
||||
} else {
|
||||
showToast('已经到最后一章了喔~');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const switchEpisodeNumberCatalogue = (item: Types.Acg.MediaContentListItem) => {
|
||||
if (validateHasBuy(item)) return;
|
||||
|
||||
visibleCatalogue.value = false;
|
||||
if (dbType === HistoryVideoType.AcgText) {
|
||||
addHistoryReadData(item.episodeNumber, { progress: 0, scrollTop: 0 });
|
||||
addHistory(item.episodeNumber);
|
||||
}
|
||||
state.episodeNumber = item.episodeNumber;
|
||||
replaceRouter(item.episodeNumber);
|
||||
};
|
||||
|
||||
const scrollToElement2 = (elementId: string) => {
|
||||
const element = document.getElementById(elementId) as HTMLElement;
|
||||
if (!element) return;
|
||||
const parent = element.closest('.episode-box') as HTMLElement;
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
|
||||
const offsetLeft = elementRect.left - parentRect.left;
|
||||
const offsetRight = elementRect.right - parentRect.right;
|
||||
|
||||
if (offsetLeft < 0 || offsetRight > 0) {
|
||||
parent.scrollLeft = element.offsetLeft - parent.offsetWidth / 2;
|
||||
}
|
||||
};
|
||||
const goSectionScrollLeft = () => {
|
||||
const item = state.list.find((item) => item.episodeNumber === state.episodeNumber);
|
||||
if (!item) return;
|
||||
nextTick(() => {
|
||||
scrollToElement2(`episode-${item.id}`);
|
||||
});
|
||||
};
|
||||
const handlePopupSwitchEpisode = (num: number) => {
|
||||
state.episodeNumber = num;
|
||||
visibleCatalogue.value = false;
|
||||
|
||||
goSectionScrollLeft();
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
currentItem,
|
||||
statusText,
|
||||
moreParams,
|
||||
onLoad,
|
||||
onRefresh,
|
||||
getList,
|
||||
visibleCatalogue,
|
||||
handlePopupSwitchEpisode,
|
||||
sort,
|
||||
autoList,
|
||||
dvPayForWatch,
|
||||
visiblePayForWatch,
|
||||
validateHasBuy,
|
||||
buySuccess,
|
||||
closePayForWatch,
|
||||
switchEpisodeNumber,
|
||||
historyData,
|
||||
historyEpisodeNumber,
|
||||
addHistory,
|
||||
addHistoryReadData,
|
||||
switchEpisodeNumberCatalogue,
|
||||
validatePermission,
|
||||
goSectionScrollTop,
|
||||
replaceRouter,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user