refactor(modules/web): Mirgrat to typescript; fix bugs
This commit is contained in:
@@ -106,25 +106,18 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import jump from "@/plugins/jump";
|
||||
import settings from "@/config/themeConfig";
|
||||
import API from "@api";
|
||||
import { useLoading } from "@/hooks/loading";
|
||||
import { useThrottleFn } from "@vueuse/shared";
|
||||
<script setup lang="ts">
|
||||
import jump from '@/plugins/jump'
|
||||
import settings from '@/config/themeConfig'
|
||||
import API from '@api'
|
||||
import { useLoading } from '@/hooks/loading'
|
||||
import { useThrottleFn } from '@vueuse/shared'
|
||||
import { isNullOrBlank } from '@/utils/utils'
|
||||
|
||||
const content = ref();
|
||||
const content = ref()
|
||||
// loading spinner
|
||||
const { isLoading, loadingWrapper } = useLoading(content, "正在获取信息");
|
||||
const store = useBookStore();
|
||||
|
||||
// 读取阅读配置
|
||||
try {
|
||||
const browerConfig = JSON.parse(localStorage.getItem("config"));
|
||||
if (browerConfig != null) store.setConfig(browerConfig);
|
||||
} catch {
|
||||
localStorage.removeItem("config");
|
||||
}
|
||||
const { isLoading, loadingWrapper } = useLoading(content, '正在获取信息')
|
||||
const store = useBookStore()
|
||||
|
||||
const {
|
||||
catalog,
|
||||
@@ -135,232 +128,238 @@ const {
|
||||
bookProgress,
|
||||
theme,
|
||||
isNight,
|
||||
} = storeToRefs(store);
|
||||
} = storeToRefs(store)
|
||||
|
||||
const chapterPos = computed({
|
||||
get: () => store.readingBook.chapterPos,
|
||||
set: (value) => (store.readingBook.chapterPos = value),
|
||||
});
|
||||
set: value => (store.readingBook.chapterPos = value),
|
||||
})
|
||||
const chapterIndex = computed({
|
||||
get: () => store.readingBook.index,
|
||||
set: (value) => (store.readingBook.index = value),
|
||||
});
|
||||
get: () => store.readingBook.chapterIndex,
|
||||
set: value => (store.readingBook.chapterIndex = value),
|
||||
})
|
||||
const isSeachBook = computed({
|
||||
get: () => store.readingBook.isSeachBook,
|
||||
set: value => (store.readingBook.isSeachBook = value),
|
||||
})
|
||||
|
||||
// 当前阅读书籍readingBook持久化
|
||||
watch(
|
||||
() => store.readingBook,
|
||||
book => {
|
||||
// 保存localStorage
|
||||
// localStorage.setItem(book.bookUrl, JSON.stringify(book));
|
||||
// 最近阅读
|
||||
localStorage.setItem('readingRecent', JSON.stringify(book))
|
||||
//保存 sessionStorage
|
||||
sessionStorage.setItem('chapterIndex', book.chapterIndex.toString())
|
||||
sessionStorage.setItem('chapterPos', book.chapterPos.toString())
|
||||
},
|
||||
)
|
||||
|
||||
// 无限滚动
|
||||
const infiniteLoading = computed(() => store.config.infiniteLoading);
|
||||
let scrollObserver;
|
||||
const loading = ref();
|
||||
const infiniteLoading = computed(() => store.config.infiniteLoading)
|
||||
let scrollObserver: IntersectionObserver | null
|
||||
const loading = ref()
|
||||
watchEffect(() => {
|
||||
if (!infiniteLoading.value) {
|
||||
scrollObserver?.disconnect();
|
||||
scrollObserver?.disconnect()
|
||||
} else {
|
||||
scrollObserver?.observe(loading.value);
|
||||
scrollObserver?.observe(loading.value)
|
||||
}
|
||||
});
|
||||
})
|
||||
const loadMore = () => {
|
||||
let index = chapterData.value.slice(-1)[0].index;
|
||||
const index = chapterData.value.slice(-1)[0].index
|
||||
if (catalog.value.length - 1 > index) {
|
||||
getContent(index + 1, false);
|
||||
store.saveBookProgress(); // 保存的是上一章的进度,不是预载的本章进度
|
||||
getContent(index + 1, false)
|
||||
store.saveBookProgress() // 保存的是上一章的进度,不是预载的本章进度
|
||||
}
|
||||
};
|
||||
}
|
||||
// IntersectionObserver回调 底部加载
|
||||
const onReachBottom = (entries) => {
|
||||
if (isLoading.value) return;
|
||||
for (let { isIntersecting } of entries) {
|
||||
if (!isIntersecting) return;
|
||||
loadMore();
|
||||
const onReachBottom = (entries: IntersectionObserverEntry[]) => {
|
||||
if (isLoading.value) return
|
||||
for (const { isIntersecting } of entries) {
|
||||
if (!isIntersecting) return
|
||||
loadMore()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 字体
|
||||
const fontFamily = computed(() => {
|
||||
if (store.config.font >= 0) {
|
||||
return settings.fonts[store.config.font];
|
||||
return settings.fonts[store.config.font]
|
||||
}
|
||||
return store.config.customFontName;
|
||||
});
|
||||
return store.config.customFontName
|
||||
})
|
||||
const fontSize = computed(() => {
|
||||
return store.config.fontSize + "px";
|
||||
});
|
||||
return store.config.fontSize + 'px'
|
||||
})
|
||||
|
||||
// 主题部分
|
||||
const bodyColor = computed(() => settings.themes[theme.value].body);
|
||||
const chapterColor = computed(() => settings.themes[theme.value].content);
|
||||
const popupColor = computed(() => settings.themes[theme.value].popup);
|
||||
const bodyColor = computed(() => settings.themes[theme.value].body)
|
||||
const chapterColor = computed(() => settings.themes[theme.value].content)
|
||||
const popupColor = computed(() => settings.themes[theme.value].popup)
|
||||
|
||||
const readWidth = computed(() => {
|
||||
if (!miniInterface.value) {
|
||||
return store.config.readWidth - 130 + "px";
|
||||
return store.config.readWidth - 130 + 'px'
|
||||
} else {
|
||||
return window.innerWidth + "px";
|
||||
return window.innerWidth + 'px'
|
||||
}
|
||||
});
|
||||
})
|
||||
const popupWidth = computed(() => {
|
||||
if (!miniInterface.value) {
|
||||
return store.config.readWidth - 33;
|
||||
return store.config.readWidth - 33
|
||||
} else {
|
||||
return window.innerWidth - 33;
|
||||
return window.innerWidth - 33
|
||||
}
|
||||
});
|
||||
})
|
||||
const bodyTheme = computed(() => {
|
||||
return {
|
||||
background: bodyColor.value,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
const chapterTheme = computed(() => {
|
||||
return {
|
||||
background: chapterColor.value,
|
||||
width: readWidth.value,
|
||||
};
|
||||
});
|
||||
const showToolBar = ref(false);
|
||||
}
|
||||
})
|
||||
const showToolBar = ref(false)
|
||||
const leftBarTheme = computed(() => {
|
||||
return {
|
||||
background: popupColor.value,
|
||||
marginLeft: miniInterface.value
|
||||
? 0
|
||||
: -(store.config.readWidth / 2 + 68) + "px",
|
||||
display: miniInterface.value && !showToolBar.value ? "none" : "block",
|
||||
};
|
||||
});
|
||||
: -(store.config.readWidth / 2 + 68) + 'px',
|
||||
display: miniInterface.value && !showToolBar.value ? 'none' : 'block',
|
||||
}
|
||||
})
|
||||
const rightBarTheme = computed(() => {
|
||||
return {
|
||||
background: popupColor.value,
|
||||
marginRight: miniInterface.value
|
||||
? 0
|
||||
: -(store.config.readWidth / 2 + 52) + "px",
|
||||
display: miniInterface.value && !showToolBar.value ? "none" : "block",
|
||||
};
|
||||
});
|
||||
: -(store.config.readWidth / 2 + 52) + 'px',
|
||||
display: miniInterface.value && !showToolBar.value ? 'none' : 'block',
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* pc移动端判断 最大阅读宽度修正
|
||||
* 阅读宽度最小为640px 加上工具栏 68px 52px 取较大值 为 776px
|
||||
*/
|
||||
const onResize = () => {
|
||||
store.setMiniInterface(window.innerWidth < 776);
|
||||
const width = store.config.readWidth; /**包含padding */
|
||||
checkPageWidth(width);
|
||||
};
|
||||
store.setMiniInterface(window.innerWidth < 776)
|
||||
const width = store.config.readWidth /**包含padding */
|
||||
checkPageWidth(width)
|
||||
}
|
||||
/** 判断阅读宽度是否超出页面 */
|
||||
const checkPageWidth = (readWidth) => {
|
||||
if (store.miniInterface) return;
|
||||
if (readWidth + 2 * 68 > window.innerWidth) store.config.readWidth -= 160;
|
||||
};
|
||||
const checkPageWidth = (readWidth: number) => {
|
||||
if (store.miniInterface) return
|
||||
if (readWidth + 2 * 68 > window.innerWidth) store.config.readWidth -= 160
|
||||
}
|
||||
watch(
|
||||
() => store.config.readWidth,
|
||||
(width) => checkPageWidth(width),
|
||||
);
|
||||
width => checkPageWidth(width),
|
||||
)
|
||||
// 顶部底部跳转
|
||||
const top = ref();
|
||||
const bottom = ref();
|
||||
const top = ref()
|
||||
const bottom = ref()
|
||||
const toTop = () => {
|
||||
jump(top.value);
|
||||
};
|
||||
jump(top.value)
|
||||
}
|
||||
const toBottom = () => {
|
||||
jump(bottom.value);
|
||||
};
|
||||
jump(bottom.value)
|
||||
}
|
||||
|
||||
// 书架路由切换
|
||||
const router = useRouter();
|
||||
const router = useRouter()
|
||||
const toShelf = () => {
|
||||
router.push("/");
|
||||
};
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
// 获取章节内容
|
||||
const chapterData = ref([]);
|
||||
const noPoint = ref(true);
|
||||
const getContent = (index, reloadChapter = true, chapterPos = 0) => {
|
||||
const chapterData = ref<{ index: number; content: string[]; title: string }[]>(
|
||||
[],
|
||||
)
|
||||
const noPoint = ref(true)
|
||||
const getContent = (index: number, reloadChapter = true, chapterPos = 0) => {
|
||||
if (reloadChapter) {
|
||||
//展示进度条
|
||||
store.setShowContent(false);
|
||||
store.setShowContent(false)
|
||||
//强制滚回顶层
|
||||
jump(top.value, { duration: 0 });
|
||||
jump(top.value, { duration: 0 })
|
||||
//从目录,按钮切换章节时保存进度 预加载时不保存
|
||||
saveReadingBookProgressToBrowser(index, chapterPos);
|
||||
chapterData.value = [];
|
||||
saveReadingBookProgressToBrowser(index, chapterPos)
|
||||
chapterData.value = []
|
||||
}
|
||||
let bookUrl = sessionStorage.getItem("bookUrl");
|
||||
let { title, index: chapterIndex } = catalog.value[index];
|
||||
const bookUrl = store.readingBook.bookUrl
|
||||
const { title, index: chapterIndex } = catalog.value[index]
|
||||
|
||||
loadingWrapper(
|
||||
API.getBookContent(bookUrl, chapterIndex).then(
|
||||
(res) => {
|
||||
res => {
|
||||
if (res.data.isSuccess) {
|
||||
let data = res.data.data;
|
||||
let content = data.split(/\n+/);
|
||||
chapterData.value.push({ index, content, title });
|
||||
if (reloadChapter) toChapterPos(chapterPos);
|
||||
const data = res.data.data
|
||||
const content = data.split(/\n+/)
|
||||
chapterData.value.push({ index, content, title })
|
||||
if (reloadChapter) toChapterPos(chapterPos)
|
||||
} else {
|
||||
ElMessage({ message: res.data.errorMsg, type: "error" });
|
||||
let content = [res.data.errorMsg];
|
||||
chapterData.value.push({ index, content, title });
|
||||
ElMessage({ message: res.data.errorMsg, type: 'error' })
|
||||
const content = [res.data.errorMsg]
|
||||
chapterData.value.push({ index, content, title })
|
||||
}
|
||||
store.setContentLoading(true);
|
||||
noPoint.value = false;
|
||||
store.setShowContent(true);
|
||||
store.setContentLoading(true)
|
||||
noPoint.value = false
|
||||
store.setShowContent(true)
|
||||
if (!res.data.isSuccess) {
|
||||
throw res.data;
|
||||
throw res.data
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
ElMessage({ message: "获取章节内容失败", type: "error" });
|
||||
let content = ["获取章节内容失败!"];
|
||||
chapterData.value.push({ index, content, title });
|
||||
store.setShowContent(true);
|
||||
throw err;
|
||||
err => {
|
||||
ElMessage({ message: '获取章节内容失败', type: 'error' })
|
||||
const content = ['获取章节内容失败!']
|
||||
chapterData.value.push({ index, content, title })
|
||||
store.setShowContent(true)
|
||||
throw err
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
// 章节进度跳转和计算
|
||||
const chapter = ref();
|
||||
const chapterRef = ref();
|
||||
const toChapterPos = (pos) => {
|
||||
const chapter = ref()
|
||||
const chapterRef = ref()
|
||||
const toChapterPos = (pos: number) => {
|
||||
nextTick(() => {
|
||||
if (chapterRef.value.length === 1)
|
||||
chapterRef.value[0].scrollToReadedLength(pos);
|
||||
});
|
||||
};
|
||||
chapterRef.value[0].scrollToReadedLength(pos)
|
||||
})
|
||||
}
|
||||
|
||||
// 60秒保存一次进度
|
||||
const saveBookProgressThrottle = useThrottleFn(
|
||||
() => store.saveBookProgress(),
|
||||
60000,
|
||||
);
|
||||
)
|
||||
|
||||
const onReadedLengthChange = (index, pos) => {
|
||||
saveReadingBookProgressToBrowser(index, pos);
|
||||
saveBookProgressThrottle();
|
||||
};
|
||||
const onReadedLengthChange = (index: number, pos: number) => {
|
||||
saveReadingBookProgressToBrowser(index, pos)
|
||||
saveBookProgressThrottle()
|
||||
}
|
||||
|
||||
// 文档标题
|
||||
watchEffect(() => {
|
||||
document.title = catalog.value[chapterIndex.value]?.title || document.title;
|
||||
});
|
||||
document.title = catalog.value[chapterIndex.value]?.title || document.title
|
||||
})
|
||||
|
||||
// 阅读记录保存浏览器
|
||||
const saveReadingBookProgressToBrowser = (index, pos) => {
|
||||
//保存localStorage
|
||||
let bookUrl = sessionStorage.getItem("bookUrl");
|
||||
var book = JSON.parse(localStorage.getItem(bookUrl));
|
||||
book.index = index;
|
||||
book.chapterPos = pos;
|
||||
localStorage.setItem(bookUrl, JSON.stringify(book));
|
||||
//最近阅读
|
||||
book = JSON.parse(localStorage.getItem("readingRecent"));
|
||||
book.chapterIndex = index;
|
||||
book.chapterPos = pos;
|
||||
localStorage.setItem("readingRecent", JSON.stringify(book));
|
||||
//保存vuex
|
||||
chapterIndex.value = index;
|
||||
chapterPos.value = pos;
|
||||
//保存sessionStorage
|
||||
sessionStorage.setItem("chapterIndex", index);
|
||||
sessionStorage.setItem("chapterPos", String(pos));
|
||||
};
|
||||
const saveReadingBookProgressToBrowser = (index: number, pos: number) => {
|
||||
// 保存pinia
|
||||
chapterIndex.value = index
|
||||
chapterPos.value = pos
|
||||
}
|
||||
|
||||
// 进度同步
|
||||
// 返回导航变化 同步请求会在获取书架前完成
|
||||
@@ -371,186 +370,210 @@ const saveReadingBookProgressToBrowser = (index, pos) => {
|
||||
* 注意不用监听点击链接导航变化 不对Safari<14.5兼容处理
|
||||
**/
|
||||
const onVisibilityChange = () => {
|
||||
if (document.visibilityState == "hidden") {
|
||||
API.saveBookProgressWithBeacon(bookProgress.value);
|
||||
const _bookProgress = bookProgress.value
|
||||
if (document.visibilityState == 'hidden' && _bookProgress) {
|
||||
API.saveBookProgressWithBeacon(_bookProgress)
|
||||
}
|
||||
};
|
||||
}
|
||||
// 定时同步
|
||||
|
||||
// 章节切换
|
||||
const toNextChapter = () => {
|
||||
store.setContentLoading(true);
|
||||
let index = chapterIndex.value + 1;
|
||||
if (typeof catalog.value[index] !== "undefined") {
|
||||
store.setContentLoading(true)
|
||||
const index = chapterIndex.value + 1
|
||||
if (typeof catalog.value[index] !== 'undefined') {
|
||||
ElMessage({
|
||||
message: "下一章",
|
||||
type: "info",
|
||||
});
|
||||
getContent(index);
|
||||
store.saveBookProgress();
|
||||
message: '下一章',
|
||||
type: 'info',
|
||||
})
|
||||
getContent(index)
|
||||
store.saveBookProgress()
|
||||
} else {
|
||||
ElMessage({
|
||||
message: "本章是最后一章",
|
||||
type: "error",
|
||||
});
|
||||
message: '本章是最后一章',
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
const toPreChapter = () => {
|
||||
store.setContentLoading(true);
|
||||
let index = chapterIndex.value - 1;
|
||||
if (typeof catalog.value[index] !== "undefined") {
|
||||
store.setContentLoading(true)
|
||||
const index = chapterIndex.value - 1
|
||||
if (typeof catalog.value[index] !== 'undefined') {
|
||||
ElMessage({
|
||||
message: "上一章",
|
||||
type: "info",
|
||||
});
|
||||
getContent(index);
|
||||
store.saveBookProgress();
|
||||
message: '上一章',
|
||||
type: 'info',
|
||||
})
|
||||
getContent(index)
|
||||
store.saveBookProgress()
|
||||
} else {
|
||||
ElMessage({
|
||||
message: "本章是第一章",
|
||||
type: "error",
|
||||
});
|
||||
message: '本章是第一章',
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let canJump = true;
|
||||
let canJump = true
|
||||
// 监听方向键
|
||||
const handleKeyPress = (event) => {
|
||||
if (!canJump) return;
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (!canJump) return
|
||||
switch (event.key) {
|
||||
case "ArrowLeft":
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
toPreChapter();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
toNextChapter();
|
||||
break;
|
||||
case "ArrowUp":
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
case 'ArrowLeft':
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
toPreChapter()
|
||||
break
|
||||
case 'ArrowRight':
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
toNextChapter()
|
||||
break
|
||||
case 'ArrowUp':
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
if (document.documentElement.scrollTop === 0) {
|
||||
ElMessage.warning("已到达页面顶部");
|
||||
ElMessage.warning('已到达页面顶部')
|
||||
} else {
|
||||
canJump = false;
|
||||
canJump = false
|
||||
jump(0 - document.documentElement.clientHeight + 100, {
|
||||
duration: store.config.jumpDuration,
|
||||
callback: () => (canJump = true),
|
||||
});
|
||||
})
|
||||
}
|
||||
break;
|
||||
case "ArrowDown":
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break
|
||||
case 'ArrowDown':
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
if (
|
||||
document.documentElement.clientHeight +
|
||||
document.documentElement.scrollTop ===
|
||||
document.documentElement.scrollHeight
|
||||
) {
|
||||
ElMessage.warning("已到达页面底部");
|
||||
ElMessage.warning('已到达页面底部')
|
||||
} else {
|
||||
canJump = false;
|
||||
canJump = false
|
||||
jump(document.documentElement.clientHeight - 100, {
|
||||
duration: store.config.jumpDuration,
|
||||
callback: () => (canJump = true),
|
||||
});
|
||||
})
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 阻止默认滚动事件
|
||||
const ignoreKeyPress = (event) => {
|
||||
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const ignoreKeyPress = (event: {
|
||||
key: string
|
||||
preventDefault: () => void
|
||||
stopPropagation: () => void
|
||||
}) => {
|
||||
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
//获取书籍数据
|
||||
let bookUrl = sessionStorage.getItem("bookUrl");
|
||||
let bookName = sessionStorage.getItem("bookName");
|
||||
let bookAuthor = sessionStorage.getItem("bookAuthor");
|
||||
let chapterIndex = Number(sessionStorage.getItem("chapterIndex") || 0);
|
||||
let chapterPos = Number(sessionStorage.getItem("chapterPos") || 0);
|
||||
var book = JSON.parse(localStorage.getItem(bookUrl));
|
||||
const bookUrl = sessionStorage.getItem('bookUrl')
|
||||
const name = sessionStorage.getItem('bookName')
|
||||
const author = sessionStorage.getItem('bookAuthor')
|
||||
const chapterIndex = Number(sessionStorage.getItem('chapterIndex') || 0)
|
||||
const chapterPos = Number(sessionStorage.getItem('chapterPos') || 0)
|
||||
const isSeachBook = sessionStorage.getItem('isSeachBook') === 'true'
|
||||
if (isNullOrBlank(bookUrl) || isNullOrBlank(name) || isNullOrBlank(author)) {
|
||||
ElMessage.warning('书籍信息为空,即将自动返回书架页面...')
|
||||
return setTimeout(toShelf, 500)
|
||||
}
|
||||
const book: typeof store.readingBook = {
|
||||
// @ts-expect-error: bookUrl name author is NON_Blank string here
|
||||
bookUrl,
|
||||
// @ts-expect-error: bookUrl name author is NON_Blank string here
|
||||
name,
|
||||
// @ts-expect-error: bookUrl name author is NON_Blank string here
|
||||
author,
|
||||
chapterIndex,
|
||||
chapterPos,
|
||||
isSeachBook,
|
||||
}
|
||||
/* const bookStr = localStorage.getItem(bookUrl);
|
||||
if (isNullOrBlank(bookStr)) {
|
||||
return setTimeout(toShelf, 500);
|
||||
}
|
||||
book = JSON.parse(bookStr as string);
|
||||
if (
|
||||
book == null ||
|
||||
chapterIndex != book.index ||
|
||||
chapterIndex != book.chapterIndex ||
|
||||
chapterPos != book.chapterPos
|
||||
) {
|
||||
book = {
|
||||
bookName: bookName,
|
||||
bookAuthor: bookAuthor,
|
||||
bookUrl: bookUrl,
|
||||
index: chapterIndex,
|
||||
chapterPos: chapterPos,
|
||||
name: bookName!!,
|
||||
author: bookAuthor!!,
|
||||
bookUrl,
|
||||
chapterIndex,
|
||||
chapterPos,
|
||||
isSeachBook
|
||||
};
|
||||
localStorage.setItem(bookUrl, JSON.stringify(book));
|
||||
}
|
||||
onResize();
|
||||
window.addEventListener("resize", onResize);
|
||||
} */
|
||||
onResize()
|
||||
window.addEventListener('resize', onResize)
|
||||
loadingWrapper(
|
||||
API.getChapterList(bookUrl).then(
|
||||
(res) => {
|
||||
API.getChapterList(bookUrl as string).then(
|
||||
res => {
|
||||
if (!res.data.isSuccess) {
|
||||
ElMessage({ message: res.data.errorMsg, type: "error" });
|
||||
setTimeout(toShelf, 500);
|
||||
return;
|
||||
ElMessage({ message: res.data.errorMsg, type: 'error' })
|
||||
setTimeout(toShelf, 500)
|
||||
return
|
||||
}
|
||||
let data = res.data.data;
|
||||
store.setCatalog(data);
|
||||
store.setReadingBook(book);
|
||||
const data = res.data.data
|
||||
store.setCatalog(data)
|
||||
store.setReadingBook(book)
|
||||
|
||||
getContent(chapterIndex, true, chapterPos);
|
||||
window.addEventListener("keyup", handleKeyPress);
|
||||
window.addEventListener("keydown", ignoreKeyPress);
|
||||
getContent(chapterIndex, true, chapterPos)
|
||||
window.addEventListener('keyup', handleKeyPress)
|
||||
window.addEventListener('keydown', ignoreKeyPress)
|
||||
// 兼容Safari < 14
|
||||
document.addEventListener("visibilitychange", onVisibilityChange);
|
||||
document.addEventListener('visibilitychange', onVisibilityChange)
|
||||
//监听底部加载
|
||||
scrollObserver = new IntersectionObserver(onReachBottom, {
|
||||
rootMargin: "-100% 0% 20% 0%",
|
||||
});
|
||||
infiniteLoading.value && scrollObserver.observe(loading.value);
|
||||
rootMargin: '-100% 0% 20% 0%',
|
||||
})
|
||||
if (infiniteLoading.value === true)
|
||||
scrollObserver.observe(loading.value)
|
||||
//第二次点击同一本书 页面标题不会变化
|
||||
document.title = null;
|
||||
document.title = bookName + " | " + catalog.value[chapterIndex].title;
|
||||
document.title = '...'
|
||||
document.title =
|
||||
(name as string) + ' | ' + catalog.value[chapterIndex].title
|
||||
},
|
||||
(err) => {
|
||||
ElMessage({ message: "获取书籍目录失败", type: "error" });
|
||||
throw err;
|
||||
err => {
|
||||
ElMessage({ message: '获取书籍目录失败', type: 'error' })
|
||||
throw err
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("keyup", handleKeyPress);
|
||||
window.removeEventListener("keydown", ignoreKeyPress);
|
||||
window.removeEventListener("resize", onResize);
|
||||
window.removeEventListener('keyup', handleKeyPress)
|
||||
window.removeEventListener('keydown', ignoreKeyPress)
|
||||
window.removeEventListener('resize', onResize)
|
||||
// 兼容Safari < 14
|
||||
document.removeEventListener("visibilitychange", onVisibilityChange);
|
||||
readSettingsVisible.value = false;
|
||||
popCataVisible.value = false;
|
||||
scrollObserver?.disconnect();
|
||||
scrollObserver = null;
|
||||
});
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange)
|
||||
readSettingsVisible.value = false
|
||||
popCataVisible.value = false
|
||||
scrollObserver?.disconnect()
|
||||
scrollObserver = null
|
||||
})
|
||||
|
||||
const addToBookShelfConfirm = async () => {
|
||||
const bookUrl = sessionStorage.getItem("bookUrl");
|
||||
const bookName = sessionStorage.getItem("bookName");
|
||||
const isSeachBook = sessionStorage.getItem("isSeachBook");
|
||||
const book = JSON.parse(localStorage.getItem(bookUrl));
|
||||
sessionStorage.removeItem("isSeachBook");
|
||||
const book = store.readingBook
|
||||
// 阅读的是搜索的书籍 并未在书架
|
||||
if (isSeachBook === "true") {
|
||||
await ElMessageBox.confirm(`是否将《${bookName}》放入书架?`, "放入书架", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "否",
|
||||
type: "info",
|
||||
if (book.isSeachBook === true) {
|
||||
await ElMessageBox.confirm(`是否将《${book.name}》放入书架?`, '放入书架', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '否',
|
||||
type: 'info',
|
||||
/*
|
||||
ElMessageBox.confirm默认在触发hashChange事件时自动关闭
|
||||
按下物理返回键时触发hashChange事件
|
||||
@@ -560,20 +583,22 @@ const addToBookShelfConfirm = async () => {
|
||||
})
|
||||
.then(() => {
|
||||
//选择是,无动作
|
||||
isSeachBook.value = false
|
||||
})
|
||||
.catch(async () => {
|
||||
//选择否,删除书籍
|
||||
await API.deleteBook(book);
|
||||
});
|
||||
await API.deleteBook(book)
|
||||
})
|
||||
.finally(() => sessionStorage.removeItem('isSeachBook'))
|
||||
}
|
||||
};
|
||||
}
|
||||
onBeforeRouteLeave(async (to, from, next) => {
|
||||
console.log("onBeforeRouteLeave");
|
||||
console.log('onBeforeRouteLeave')
|
||||
// 弹窗时停止响应按键翻页
|
||||
window.removeEventListener("keyup", handleKeyPress);
|
||||
await addToBookShelfConfirm();
|
||||
next();
|
||||
});
|
||||
window.removeEventListener('keyup', handleKeyPress)
|
||||
await addToBookShelfConfirm()
|
||||
next()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -662,8 +687,8 @@ onBeforeRouteLeave(async (to, from, next) => {
|
||||
}
|
||||
|
||||
.chapter {
|
||||
font-family: "Microsoft YaHei", PingFangSC-Regular, HelveticaNeue-Light,
|
||||
"Helvetica Neue Light", sans-serif;
|
||||
font-family: 'Microsoft YaHei', PingFangSC-Regular, HelveticaNeue-Light,
|
||||
'Helvetica Neue Light', sans-serif;
|
||||
text-align: left;
|
||||
padding: 0 65px;
|
||||
min-height: 100vh;
|
||||
@@ -673,8 +698,8 @@ onBeforeRouteLeave(async (to, from, next) => {
|
||||
.content {
|
||||
font-size: 18px;
|
||||
line-height: 1.8;
|
||||
font-family: "Microsoft YaHei", PingFangSC-Regular, HelveticaNeue-Light,
|
||||
"Helvetica Neue Light", sans-serif;
|
||||
font-family: 'Microsoft YaHei', PingFangSC-Regular, HelveticaNeue-Light,
|
||||
'Helvetica Neue Light', sans-serif;
|
||||
|
||||
.bottom-bar,
|
||||
.top-bar {
|
||||
|
||||
+264
-296
@@ -27,7 +27,7 @@
|
||||
size="large"
|
||||
@click="
|
||||
toDetail(
|
||||
readingRecent.url,
|
||||
readingRecent.bookUrl,
|
||||
readingRecent.name,
|
||||
readingRecent.author,
|
||||
readingRecent.chapterIndex,
|
||||
@@ -36,7 +36,7 @@
|
||||
true,
|
||||
)
|
||||
"
|
||||
:class="{ 'no-point': readingRecent.url == '' }"
|
||||
:class="{ 'no-point': readingRecent.bookUrl == '' }"
|
||||
>
|
||||
{{ readingRecent.name }}
|
||||
</el-tag>
|
||||
@@ -78,309 +78,277 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "@/assets/bookshelf.css";
|
||||
import "@/assets/fonts/shelffont.css";
|
||||
import { useBookStore } from "@/store";
|
||||
import githubUrl from "@/assets/imgs/github.png";
|
||||
import { useLoading } from "@/hooks/loading";
|
||||
import { Search as SearchIcon } from "@element-plus/icons-vue";
|
||||
import {baseURL_localStorage_key} from "@/api/axios"
|
||||
<script setup lang="ts">
|
||||
import '@/assets/bookshelf.css'
|
||||
import '@/assets/fonts/shelffont.css'
|
||||
import { useBookStore } from '@/store'
|
||||
import githubUrl from '@/assets/imgs/github.png'
|
||||
import { useLoading } from '@/hooks/loading'
|
||||
import { Search as SearchIcon } from '@element-plus/icons-vue'
|
||||
import { baseURL_localStorage_key } from '@/api/axios'
|
||||
import API, {
|
||||
legado_http_entry_point,
|
||||
validatorHttpUrl,
|
||||
setLeagdoHttpUrl,
|
||||
} from "@api";
|
||||
parseLeagdoHttpUrlWithDefault,
|
||||
setApiEntryPoint,
|
||||
} from '@api'
|
||||
import { validatorHttpUrl } from '@/utils/utils'
|
||||
import type { Book, SeachBook } from '@/book'
|
||||
import type { webReadConfig } from '@/web'
|
||||
|
||||
export default defineComponent({
|
||||
beforeRouteEnter: (to, from, next) => {
|
||||
API.getReadConfig()
|
||||
.then((response) => response.data)
|
||||
.then(({ isSuccess, data }) => {
|
||||
if (isSuccess) {
|
||||
next((vm) => {
|
||||
console.log("初始化加载阅读界面配置成功");
|
||||
// @ts-ignore
|
||||
vm.saveReadConfig(data);
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch(() => next());
|
||||
},
|
||||
setup: () => {
|
||||
const store = useBookStore();
|
||||
const isNight = computed(() => store.isNight);
|
||||
const store = useBookStore()
|
||||
const isNight = computed(() => store.isNight)
|
||||
|
||||
const readingRecent = ref({
|
||||
name: "尚无阅读记录",
|
||||
author: "",
|
||||
url: "",
|
||||
chapterIndex: 0,
|
||||
chapterPos: 0,
|
||||
isSeachBook: false,
|
||||
});
|
||||
const shelfWrapper = ref(null);
|
||||
const { showLoading, closeLoading, loadingWrapper, isLoading } = useLoading(
|
||||
shelfWrapper,
|
||||
"正在获取书籍信息",
|
||||
);
|
||||
/** shortcuts of `store.setConfig` */
|
||||
const applyReadConfig = (config?: webReadConfig) => {
|
||||
try {
|
||||
if (config !== undefined) store.setConfig(config)
|
||||
} catch {
|
||||
ElMessage.info('阅读界面配置解析错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 书架书籍和在线书籍搜索
|
||||
const books = shallowRef([]);
|
||||
const shelf = computed(() => store.shelf);
|
||||
const searchWord = ref("");
|
||||
const isSearching = ref(false);
|
||||
watchEffect(() => {
|
||||
if (isSearching.value && searchWord.value != "") return;
|
||||
isSearching.value = false;
|
||||
books.value = [];
|
||||
if (searchWord.value == "") {
|
||||
books.value = shelf.value;
|
||||
return;
|
||||
const readingRecent = ref<typeof store.readingBook>({
|
||||
name: '尚无阅读记录',
|
||||
author: '',
|
||||
bookUrl: '',
|
||||
chapterIndex: 0,
|
||||
chapterPos: 0,
|
||||
isSeachBook: false,
|
||||
})
|
||||
|
||||
const shelfWrapper = ref<HTMLElement>()
|
||||
//const shelfWrapper = useTemplateRef<HTMLElement>("shelfWrapper")
|
||||
const { showLoading, closeLoading, loadingWrapper, isLoading } = useLoading(
|
||||
shelfWrapper,
|
||||
'正在获取书籍信息',
|
||||
)
|
||||
|
||||
// 书架书籍和在线书籍搜索
|
||||
const books = shallowRef<Book[] | SeachBook[]>([])
|
||||
const shelf = computed(() => store.shelf)
|
||||
const searchWord = ref('')
|
||||
const isSearching = ref(false)
|
||||
watchEffect(() => {
|
||||
if (isSearching.value && searchWord.value != '') return
|
||||
isSearching.value = false
|
||||
books.value = []
|
||||
if (searchWord.value == '') {
|
||||
books.value = shelf.value
|
||||
return
|
||||
}
|
||||
books.value = shelf.value.filter(book => {
|
||||
return (
|
||||
book.name.includes(searchWord.value) ||
|
||||
book.author.includes(searchWord.value)
|
||||
)
|
||||
})
|
||||
})
|
||||
//搜索在线书籍
|
||||
const searchBook = () => {
|
||||
if (searchWord.value == '') return
|
||||
books.value = []
|
||||
store.clearSearchBooks()
|
||||
showLoading()
|
||||
isSearching.value = true
|
||||
API.search(
|
||||
searchWord.value,
|
||||
searcBooks => {
|
||||
if (isLoading) {
|
||||
closeLoading()
|
||||
}
|
||||
books.value = shelf.value.filter((book) => {
|
||||
return (
|
||||
book.name.includes(searchWord.value) ||
|
||||
book.author.includes(searchWord.value)
|
||||
);
|
||||
});
|
||||
});
|
||||
//搜索在线书籍
|
||||
const searchBook = () => {
|
||||
if (searchWord.value == "") return;
|
||||
books.value = [];
|
||||
store.clearSearchBooks();
|
||||
showLoading();
|
||||
isSearching.value = true;
|
||||
API.search(
|
||||
searchWord.value,
|
||||
(data) => {
|
||||
if (isLoading) {
|
||||
closeLoading();
|
||||
}
|
||||
try {
|
||||
store.setSearchBooks(JSON.parse(data));
|
||||
books.value = store.searchBooks;
|
||||
//store.searchBooks.forEach((item) => books.value.push(item));
|
||||
} catch (e) {
|
||||
ElMessage.error("后端数据错误");
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
closeLoading();
|
||||
if (books.value.length == 0) {
|
||||
ElMessage.info("搜索结果为空");
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
//连接状态
|
||||
const connectStatus = computed(() => store.connectStatus);
|
||||
const connectType = computed(() => store.connectType);
|
||||
const newConnect = computed(() => store.newConnect);
|
||||
const setLegadoRetmoteUrl = () => {
|
||||
ElMessageBox.prompt(
|
||||
"请输入 后端地址 ( 如:http://127.0.0.1:9527 或者通过内网穿透的地址)",
|
||||
"提示",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
inputPlaceholder: legado_http_entry_point,
|
||||
inputValidator: (value) => {
|
||||
try {
|
||||
validatorHttpUrl(value);
|
||||
} catch (e) {
|
||||
return e?.cause?.message ?? e.message;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === "confirm") {
|
||||
store.setNewConnect(true);
|
||||
instance.confirmButtonLoading = true;
|
||||
instance.confirmButtonText = "校验中……";
|
||||
// instance.inputValue
|
||||
const url = new URL(instance.inputValue).toString();
|
||||
API.testLeagdoHttpUrlConnection(url)
|
||||
//API.getBookShelf()
|
||||
.then(function (configStr) {
|
||||
saveReadConfig(configStr);
|
||||
instance.confirmButtonLoading = false;
|
||||
store.setConnectType("success");
|
||||
store.clearSearchBooks();
|
||||
store.setNewConnect(false);
|
||||
setLeagdoHttpUrl(url);
|
||||
if (url === location.origin) {
|
||||
localStorage.removeItem(baseURL_localStorage_key);
|
||||
} else {
|
||||
localStorage.setItem(baseURL_localStorage_key, url);
|
||||
}
|
||||
store.setConnectStatus("已连接 " + url.toString());
|
||||
fetchBookShelfData();
|
||||
done();
|
||||
})
|
||||
.catch(function (error) {
|
||||
instance.confirmButtonLoading = false;
|
||||
instance.confirmButtonText = "确定";
|
||||
ElMessage.error("访问失败,请检查您输入的 url");
|
||||
store.setNewConnect(false);
|
||||
throw error;
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const handleBookClick = async (book) => {
|
||||
const {
|
||||
bookUrl,
|
||||
name,
|
||||
author,
|
||||
durChapterIndex = 0,
|
||||
durChapterPos = 0,
|
||||
} = book;
|
||||
// 判断是否为 searchBook
|
||||
const isSeachBook = "respondTime" in book;
|
||||
if (isSeachBook) {
|
||||
await API.saveBook(book);
|
||||
}
|
||||
toDetail(
|
||||
bookUrl,
|
||||
name,
|
||||
author,
|
||||
durChapterIndex,
|
||||
durChapterPos,
|
||||
isSeachBook,
|
||||
);
|
||||
};
|
||||
const toDetail = (
|
||||
bookUrl,
|
||||
bookName,
|
||||
bookAuthor,
|
||||
chapterIndex,
|
||||
chapterPos,
|
||||
isSeachBook,
|
||||
fromReadRecentClick = false,
|
||||
) => {
|
||||
if (bookName === "尚无阅读记录") return;
|
||||
// 最近书籍不再书架上 自动搜索
|
||||
if (isSeachBook === true && fromReadRecentClick) {
|
||||
searchWord.value = bookName;
|
||||
searchBook();
|
||||
return;
|
||||
}
|
||||
sessionStorage.setItem("bookUrl", bookUrl);
|
||||
sessionStorage.setItem("bookName", bookName);
|
||||
sessionStorage.setItem("bookAuthor", bookAuthor);
|
||||
sessionStorage.setItem("chapterIndex", chapterIndex);
|
||||
sessionStorage.setItem("chapterPos", chapterPos);
|
||||
sessionStorage.setItem("isSeachBook", String(isSeachBook));
|
||||
readingRecent.value = {
|
||||
name: bookName,
|
||||
author: bookAuthor,
|
||||
url: bookUrl,
|
||||
chapterIndex: chapterIndex,
|
||||
chapterPos: chapterPos,
|
||||
isSeachBook,
|
||||
};
|
||||
localStorage.setItem(
|
||||
"readingRecent",
|
||||
JSON.stringify(readingRecent.value),
|
||||
);
|
||||
router.push({
|
||||
path: "/chapter",
|
||||
});
|
||||
};
|
||||
|
||||
const loadShelf = () => {
|
||||
loadingWrapper(
|
||||
store
|
||||
.saveBookProgress()
|
||||
//确保各种网络情况下同步请求先完成
|
||||
.finally(fetchBookShelfData),
|
||||
);
|
||||
};
|
||||
|
||||
const saveReadConfig = (configStr) => {
|
||||
try {
|
||||
store.setConfig(JSON.parse(configStr));
|
||||
} catch {
|
||||
ElMessage.info("阅读界面配置解析错误");
|
||||
store.setSearchBooks(searcBooks)
|
||||
books.value = store.searchBooks
|
||||
//store.searchBooks.forEach((item) => books.value.push(item));
|
||||
} catch (e) {
|
||||
ElMessage.error('后端数据错误')
|
||||
throw e
|
||||
}
|
||||
};
|
||||
},
|
||||
() => {
|
||||
closeLoading()
|
||||
if (books.value.length == 0) {
|
||||
ElMessage.info('搜索结果为空')
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const fetchBookShelfData = () => {
|
||||
return API.getBookShelf().then((response) => {
|
||||
store.setConnectType("success");
|
||||
if (response.data.isSuccess) {
|
||||
//store.increaseBookNum(response.data.data.length);
|
||||
store.addBooks(
|
||||
response.data.data.sort(function (a, b) {
|
||||
var x = a["durChapterTime"] || 0;
|
||||
var y = b["durChapterTime"] || 0;
|
||||
return y - x;
|
||||
}),
|
||||
);
|
||||
//连接状态
|
||||
const connectStatus = computed(() => store.connectStatus)
|
||||
const connectType = computed(() => store.connectType)
|
||||
const newConnect = computed(() => store.newConnect)
|
||||
const setLegadoRetmoteUrl = () => {
|
||||
ElMessageBox.prompt(
|
||||
'请输入 后端地址 ( 如:http://127.0.0.1:9527 或者通过内网穿透的地址)',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPlaceholder: legado_http_entry_point,
|
||||
inputValidator: value => validatorHttpUrl(value),
|
||||
inputErrorMessage: '输入的格式不对',
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'confirm') {
|
||||
store.setNewConnect(true)
|
||||
instance.confirmButtonLoading = true
|
||||
instance.confirmButtonText = '校验中……'
|
||||
// instance.inputValue
|
||||
const url = new URL(instance.inputValue).toString()
|
||||
API.getReadConfig(url)
|
||||
//API.getBookShelf()
|
||||
.then(function (config) {
|
||||
applyReadConfig(config)
|
||||
instance.confirmButtonLoading = false
|
||||
store.setConnectType('success')
|
||||
store.clearSearchBooks()
|
||||
store.setNewConnect(false)
|
||||
setApiEntryPoint(...parseLeagdoHttpUrlWithDefault(url))
|
||||
if (url === location.origin) {
|
||||
localStorage.removeItem(baseURL_localStorage_key)
|
||||
} else {
|
||||
localStorage.setItem(baseURL_localStorage_key, url)
|
||||
}
|
||||
store.setConnectStatus('已连接 ' + url.toString())
|
||||
fetchBookShelfData()
|
||||
done()
|
||||
})
|
||||
.catch(function (error) {
|
||||
instance.confirmButtonLoading = false
|
||||
instance.confirmButtonText = '确定'
|
||||
ElMessage.error('访问失败,请检查您输入的 url')
|
||||
store.setNewConnect(false)
|
||||
throw error
|
||||
})
|
||||
} else {
|
||||
ElMessage.error(response.data.errorMsg ?? "后端返回格式错误!");
|
||||
}
|
||||
store.setConnectStatus("已连接 " + legado_http_entry_point);
|
||||
store.setNewConnect(false);
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
//获取最近阅读书籍
|
||||
let readingRecentStr = localStorage.getItem("readingRecent");
|
||||
if (readingRecentStr != null) {
|
||||
readingRecent.value = JSON.parse(readingRecentStr);
|
||||
if (typeof readingRecent.value.chapterIndex == "undefined") {
|
||||
readingRecent.value.chapterIndex = 0;
|
||||
done()
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
const handleBookClick = async (book: SeachBook | Book) => {
|
||||
// 判断是否为 searchBook
|
||||
const isSeachBook = 'respondTime' in book
|
||||
if (isSeachBook) {
|
||||
await API.saveBook(book)
|
||||
}
|
||||
const {
|
||||
bookUrl,
|
||||
name,
|
||||
author,
|
||||
// @ts-expect-error: descruct with default value
|
||||
durChapterIndex = 0,
|
||||
// @ts-expect-error: descruct with default value
|
||||
durChapterPos = 0,
|
||||
} = book
|
||||
|
||||
toDetail(bookUrl, name, author, durChapterIndex, durChapterPos, isSeachBook)
|
||||
}
|
||||
const toDetail = (
|
||||
bookUrl: string,
|
||||
bookName: string,
|
||||
bookAuthor: string,
|
||||
chapterIndex: number,
|
||||
chapterPos: number,
|
||||
isSeachBook: boolean | undefined = false,
|
||||
fromReadRecentClick = false,
|
||||
) => {
|
||||
if (bookName === '尚无阅读记录') return
|
||||
// 最近书籍不再书架上 自动搜索
|
||||
if (
|
||||
fromReadRecentClick &&
|
||||
shelf.value.every(book => book.bookUrl !== bookUrl)
|
||||
) {
|
||||
searchWord.value = bookName
|
||||
searchBook()
|
||||
return
|
||||
}
|
||||
sessionStorage.setItem('bookUrl', bookUrl)
|
||||
sessionStorage.setItem('bookName', bookName)
|
||||
sessionStorage.setItem('bookAuthor', bookAuthor)
|
||||
sessionStorage.setItem('chapterIndex', String(chapterIndex))
|
||||
sessionStorage.setItem('chapterPos', String(chapterPos))
|
||||
sessionStorage.setItem('isSeachBook', String(isSeachBook))
|
||||
readingRecent.value = {
|
||||
name: bookName,
|
||||
author: bookAuthor,
|
||||
bookUrl,
|
||||
chapterIndex,
|
||||
chapterPos,
|
||||
isSeachBook,
|
||||
}
|
||||
localStorage.setItem('readingRecent', JSON.stringify(readingRecent.value))
|
||||
router.push({
|
||||
path: '/chapter',
|
||||
})
|
||||
}
|
||||
|
||||
const loadShelf = async () => {
|
||||
try {
|
||||
if (store.configInited === false) {
|
||||
const config = await API.getReadConfig()
|
||||
applyReadConfig(config)
|
||||
} else {
|
||||
}
|
||||
await store.saveBookProgress()
|
||||
//确保各种网络情况下同步请求先完成
|
||||
await fetchBookShelfData()
|
||||
} catch (error: unknown) {
|
||||
store.setConnectType('danger')
|
||||
store.setConnectStatus('连接异常')
|
||||
store.setNewConnect(false)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const fetchBookShelfData = () => {
|
||||
return API.getBookShelf().then(response => {
|
||||
store.setConnectType('success')
|
||||
if (response.data.isSuccess) {
|
||||
//store.increaseBookNum(response.data.data.length);
|
||||
store.addBooks(
|
||||
response.data.data.sort(function (a, b) {
|
||||
const x = a['durChapterTime'] || 0
|
||||
const y = b['durChapterTime'] || 0
|
||||
return y - x
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
if (
|
||||
response.data.errorMsg.includes('还没有添加小说') &&
|
||||
shelf.value.length > 0
|
||||
) {
|
||||
ElNotification.warning({
|
||||
title: '提示',
|
||||
message: '当前书架上的书籍已经被删除',
|
||||
position: 'bottom-right',
|
||||
})
|
||||
return store.clearBooks()
|
||||
}
|
||||
console.log("bookshelf mounted");
|
||||
API.testLeagdoHttpUrlConnection()
|
||||
//.then(saveReadConfig) 应该在组件挂载前读取阅读配置
|
||||
.then(loadShelf)
|
||||
.catch(function (error) {
|
||||
store.setConnectType("danger");
|
||||
store.setConnectStatus("连接异常");
|
||||
ElMessage.error(
|
||||
"后端连接失败异常,请检查阅读WEB服务或者设置其它可用链接",
|
||||
);
|
||||
store.setNewConnect(false);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
return {
|
||||
setLegadoRetmoteUrl,
|
||||
isNight,
|
||||
connectStatus,
|
||||
connectType,
|
||||
newConnect,
|
||||
saveReadConfig, //expose it so beforeRouteEnter next can access it
|
||||
readingRecent,
|
||||
searchBook,
|
||||
books,
|
||||
handleBookClick,
|
||||
toDetail,
|
||||
isSearching,
|
||||
SearchIcon,
|
||||
githubUrl,
|
||||
searchWord,
|
||||
};
|
||||
},
|
||||
});
|
||||
ElMessage.error(response.data.errorMsg ?? '后端返回格式错误!')
|
||||
}
|
||||
store.setConnectStatus('已连接 ' + legado_http_entry_point)
|
||||
store.setNewConnect(false)
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
//获取最近阅读书籍
|
||||
const readingRecentStr = localStorage.getItem('readingRecent')
|
||||
if (readingRecentStr != null) {
|
||||
readingRecent.value = JSON.parse(readingRecentStr)
|
||||
if (typeof readingRecent.value.chapterIndex == 'undefined') {
|
||||
readingRecent.value.chapterIndex = 0
|
||||
}
|
||||
}
|
||||
console.log('bookshelf mounted')
|
||||
loadingWrapper(loadShelf())
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped>
|
||||
.index-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -438,12 +406,12 @@ export default defineComponent({
|
||||
|
||||
.recent-book {
|
||||
font-size: 10px;
|
||||
// font-weight: 400;
|
||||
/* // font-weight: 400;
|
||||
// margin: 12px 0;
|
||||
// font-weight: 500;
|
||||
// color: #6B7C87;
|
||||
// color: #6B7C87; */
|
||||
cursor: pointer;
|
||||
// padding: 6px 18px;
|
||||
/* // padding: 6px 18px; */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,7 +432,7 @@ export default defineComponent({
|
||||
.setting-connect {
|
||||
font-size: 8px;
|
||||
margin-top: 16px;
|
||||
// color: #6B7C87;
|
||||
/* // color: #6B7C87; */
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@@ -538,7 +506,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.night {
|
||||
:deep(.navigation-wrapper) {
|
||||
.navigation-wrapper {
|
||||
background-color: #454545;
|
||||
|
||||
.navigation-title {
|
||||
|
||||
@@ -5,22 +5,23 @@
|
||||
<source-tab-tools class="right" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import bookSourceConfig from "@/config/bookSourceEditConfig";
|
||||
import rssSourceConfig from "@/config/rssSourceEditConfig";
|
||||
import "@/assets/sourceeditor.css";
|
||||
import { useDark } from "@vueuse/core";
|
||||
<script setup lang="ts">
|
||||
import bookSourceConfig from '@/config/bookSourceEditConfig'
|
||||
import rssSourceConfig from '@/config/rssSourceEditConfig'
|
||||
import '@/assets/sourceeditor.css'
|
||||
import { useDark } from '@vueuse/core'
|
||||
import type { SourceConfig } from '@/config/sourceConfig'
|
||||
|
||||
useDark();
|
||||
useDark()
|
||||
|
||||
let config;
|
||||
let config: SourceConfig
|
||||
|
||||
if (/bookSource/i.test(location.href)) {
|
||||
config = bookSourceConfig;
|
||||
document.title = "书源管理";
|
||||
config = bookSourceConfig as SourceConfig
|
||||
document.title = '书源管理'
|
||||
} else {
|
||||
config = rssSourceConfig;
|
||||
document.title = "订阅源管理";
|
||||
config = rssSourceConfig as SourceConfig
|
||||
document.title = '订阅源管理'
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user