886 lines
24 KiB
TypeScript
886 lines
24 KiB
TypeScript
|
import request from '../../utils/request';
|
||
|
|
||
|
// 定义组件方法接口
|
||
|
interface IComponentMethods {
|
||
|
loadChapter: (chapter: number) => Promise<void>;
|
||
|
splitPages: () => Promise<void>;
|
||
|
prevPage: () => void;
|
||
|
nextPage: () => void;
|
||
|
touchStart: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
touchEnd: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
toggleToolbar: () => void;
|
||
|
toggleMenu: () => void;
|
||
|
prevChapter: () => void;
|
||
|
nextChapter: () => void;
|
||
|
jumpToChapter: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
goBack: () => void;
|
||
|
onScroll: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
toggleSettings: () => void;
|
||
|
decreaseFontSize: () => void;
|
||
|
increaseFontSize: () => void;
|
||
|
startTimeUpdate: () => void;
|
||
|
calculateProgress: () => void;
|
||
|
closeAllPanels: () => void;
|
||
|
switchTheme: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
adjustBrightness: (e: WechatMiniprogram.SliderChange) => void;
|
||
|
toggleAutoReading: (e: WechatMiniprogram.SwitchChange) => void;
|
||
|
setAutoReadingInterval: (e: WechatMiniprogram.PickerChange) => void;
|
||
|
startAutoReading: () => void;
|
||
|
stopAutoReading: () => void;
|
||
|
toggleBookmarks: () => void;
|
||
|
addBookmark: () => void;
|
||
|
loadBookmarks: () => void;
|
||
|
jumpToBookmark: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
toggleNotes: () => void;
|
||
|
showTextMenu: (e: WechatMiniprogram.TouchEvent) => void;
|
||
|
addNote: () => void;
|
||
|
loadNotes: () => void;
|
||
|
copyText: () => void;
|
||
|
startReadingTimer: () => void;
|
||
|
updateReadingTime: () => void;
|
||
|
loadTodayReadingTime: () => void;
|
||
|
saveReadingProgress: () => void;
|
||
|
restoreSettings: () => void;
|
||
|
toggleMore: () => void;
|
||
|
onSelectText: (e: any) => void;
|
||
|
addBookmarkWithSelection: () => void;
|
||
|
addNoteWithSelection: () => void;
|
||
|
copySelectedText: () => void;
|
||
|
hideTextMenu: () => void;
|
||
|
startReadingTimer: () => void;
|
||
|
stopReadingTimer: () => void;
|
||
|
saveReadingRecord: (duration: number) => void;
|
||
|
}
|
||
|
|
||
|
// 定义组件数据接口
|
||
|
interface IComponentData {
|
||
|
bookTitle: string;
|
||
|
content: string;
|
||
|
currentChapter: number;
|
||
|
totalChapters: number;
|
||
|
showToolbar: boolean;
|
||
|
showMenu: boolean;
|
||
|
scrollTop: number;
|
||
|
chapters: number[];
|
||
|
lastTapTime: number;
|
||
|
pages: string[];
|
||
|
currentPage: number;
|
||
|
pageHeight: number;
|
||
|
lineHeight: number;
|
||
|
fontSize: number;
|
||
|
showSettings: boolean;
|
||
|
minFontSize: number;
|
||
|
maxFontSize: number;
|
||
|
currentTime: string;
|
||
|
readingProgress: number;
|
||
|
themes: Array<{id: string; name: string; bg: string; color: string}>;
|
||
|
currentTheme: string;
|
||
|
brightness: number;
|
||
|
autoReading: boolean;
|
||
|
autoReadingInterval: number;
|
||
|
showBookmarks: boolean;
|
||
|
showNotes: boolean;
|
||
|
showTextMenu: boolean;
|
||
|
textMenuTop: number;
|
||
|
textMenuLeft: number;
|
||
|
selectedText: string;
|
||
|
bookmarks: any[];
|
||
|
notes: any[];
|
||
|
readingStartTime: number;
|
||
|
todayReadingTime: number;
|
||
|
showMore: boolean;
|
||
|
selectionStart: number;
|
||
|
selectionEnd: number;
|
||
|
readingDuration: number;
|
||
|
isReading: boolean;
|
||
|
readingTimer: any;
|
||
|
}
|
||
|
|
||
|
// 定义组件属性接口
|
||
|
interface IComponentProperties {
|
||
|
bookId: string;
|
||
|
bookUrl: string;
|
||
|
title: string;
|
||
|
}
|
||
|
|
||
|
Component<IComponentData, IComponentProperties, IComponentMethods>({
|
||
|
data: {
|
||
|
bookTitle: '',
|
||
|
content: '',
|
||
|
currentChapter: 1,
|
||
|
totalChapters: 2541,
|
||
|
showToolbar: true,
|
||
|
showMenu: false,
|
||
|
scrollTop: 0,
|
||
|
chapters: [] as number[],
|
||
|
lastTapTime: 0,
|
||
|
pages: [] as string[],
|
||
|
currentPage: 0,
|
||
|
pageHeight: 0,
|
||
|
lineHeight: 36,
|
||
|
fontSize: 32,
|
||
|
showSettings: false,
|
||
|
minFontSize: 24,
|
||
|
maxFontSize: 48,
|
||
|
currentTime: '',
|
||
|
readingProgress: 0,
|
||
|
themes: [
|
||
|
{ id: 'default', name: '默认', bg: '#f4ecd8', color: '#333' },
|
||
|
{ id: 'night', name: '夜间', bg: '#222', color: '#999' },
|
||
|
{ id: 'green', name: '护眼', bg: '#cce8cf', color: '#333' },
|
||
|
{ id: 'paper', name: '纸张', bg: '#e8e2d3', color: '#333' }
|
||
|
],
|
||
|
currentTheme: 'default',
|
||
|
brightness: 100,
|
||
|
autoReading: false,
|
||
|
autoReadingInterval: 5000,
|
||
|
showBookmarks: false,
|
||
|
showNotes: false,
|
||
|
showTextMenu: false,
|
||
|
textMenuTop: 0,
|
||
|
textMenuLeft: 0,
|
||
|
selectedText: '',
|
||
|
bookmarks: [] as any[],
|
||
|
notes: [] as any[],
|
||
|
readingStartTime: 0,
|
||
|
todayReadingTime: 0,
|
||
|
showMore: false,
|
||
|
selectionStart: -1,
|
||
|
selectionEnd: -1,
|
||
|
readingDuration: 0,
|
||
|
isReading: false,
|
||
|
readingTimer: null as any,
|
||
|
},
|
||
|
|
||
|
properties: {
|
||
|
bookId: String,
|
||
|
bookUrl: String,
|
||
|
title: String
|
||
|
},
|
||
|
|
||
|
lifetimes: {
|
||
|
attached() {
|
||
|
this.setData({
|
||
|
bookTitle: this.properties.title || '大爱仙尊',
|
||
|
chapters: Array.from({length: 2541}, (_, i) => i + 1)
|
||
|
});
|
||
|
this.loadChapter(1).then(() => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
const savedFontSize = wx.getStorageSync('reader_font_size');
|
||
|
if (savedFontSize) {
|
||
|
this.setData({
|
||
|
fontSize: savedFontSize
|
||
|
});
|
||
|
}
|
||
|
this.startTimeUpdate();
|
||
|
this.startReadingTimer();
|
||
|
this.loadBookmarks();
|
||
|
this.loadNotes();
|
||
|
this.restoreSettings();
|
||
|
|
||
|
// 获取当前屏幕亮度
|
||
|
wx.getScreenBrightness({
|
||
|
success: (res) => {
|
||
|
this.setData({
|
||
|
brightness: Math.round(res.value * 100)
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
detached() {
|
||
|
if (this.data.readingTimer) {
|
||
|
clearInterval(this.data.readingTimer);
|
||
|
}
|
||
|
this.updateReadingTime();
|
||
|
this.saveReadingProgress();
|
||
|
if (this.autoReadingTimer) {
|
||
|
clearInterval(this.autoReadingTimer);
|
||
|
}
|
||
|
this.stopReadingTimer();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
pageLifetimes: {
|
||
|
hide() {
|
||
|
this.stopReadingTimer();
|
||
|
},
|
||
|
|
||
|
show() {
|
||
|
if (!this.data.readingDuration) {
|
||
|
this.startReadingTimer();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
// 加载章节内容
|
||
|
async loadChapter(chapter: number) {
|
||
|
try {
|
||
|
wx.showLoading({ title: '加载中...' });
|
||
|
const res = await request.get('/common/getBookContent', {
|
||
|
bookName: this.data.bookTitle,
|
||
|
id: chapter
|
||
|
});
|
||
|
|
||
|
if (res.code === 200) {
|
||
|
// 处理换行符
|
||
|
const content = res.data.replace(/\\r\\n/g, '\n');
|
||
|
this.setData({
|
||
|
content,
|
||
|
currentChapter: chapter // 更新当前章节号
|
||
|
});
|
||
|
// 分页处理
|
||
|
await this.splitPages();
|
||
|
this.calculateProgress();
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('加载章节失败:', error);
|
||
|
wx.showToast({
|
||
|
title: '加载失败',
|
||
|
icon: 'none'
|
||
|
});
|
||
|
} finally {
|
||
|
wx.hideLoading();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 分页处理
|
||
|
async splitPages() {
|
||
|
// 获取容器尺寸
|
||
|
const query = this.createSelectorQuery();
|
||
|
query.select('.content').boundingClientRect();
|
||
|
const rect = await new Promise(resolve => query.exec(resolve));
|
||
|
const containerHeight = rect[0].height;
|
||
|
|
||
|
// 计算每页能显示的行数
|
||
|
const lineHeight = this.data.lineHeight;
|
||
|
const linesPerPage = Math.floor((containerHeight - 160) / (lineHeight * 2 / 750 * wx.getSystemInfoSync().windowWidth));
|
||
|
|
||
|
// 按自然段落分割内容
|
||
|
const paragraphs = this.data.content.split('\n').filter(p => p.trim());
|
||
|
|
||
|
// 分页
|
||
|
const pages = [];
|
||
|
let currentPage = [];
|
||
|
let currentLines = 0;
|
||
|
|
||
|
for (const paragraph of paragraphs) {
|
||
|
// 计算段落需要的行数
|
||
|
const paragraphLines = Math.ceil(paragraph.length * (this.data.fontSize / 32) / 20);
|
||
|
|
||
|
if (currentLines + paragraphLines > linesPerPage) {
|
||
|
// 当前页放不下这段,新建一页
|
||
|
if (currentPage.length > 0) {
|
||
|
pages.push(currentPage.join('\n'));
|
||
|
currentPage = [];
|
||
|
currentLines = 0;
|
||
|
}
|
||
|
|
||
|
// 如果单个段落超过一页
|
||
|
if (paragraphLines > linesPerPage) {
|
||
|
const chars = Math.floor(20 * linesPerPage);
|
||
|
let p = paragraph;
|
||
|
while (p.length > 0) {
|
||
|
const pageContent = p.slice(0, chars);
|
||
|
pages.push(pageContent);
|
||
|
p = p.slice(chars);
|
||
|
}
|
||
|
} else {
|
||
|
currentPage.push(paragraph);
|
||
|
currentLines = paragraphLines;
|
||
|
}
|
||
|
} else {
|
||
|
// 当前页能放下这段
|
||
|
currentPage.push(paragraph);
|
||
|
currentLines += paragraphLines;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 保存最后一页
|
||
|
if (currentPage.length > 0) {
|
||
|
pages.push(currentPage.join('\n'));
|
||
|
}
|
||
|
|
||
|
this.setData({
|
||
|
pages,
|
||
|
currentPage: 0,
|
||
|
pageHeight: containerHeight
|
||
|
}, () => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 上一页
|
||
|
prevPage() {
|
||
|
if (this.data.currentPage > 0) {
|
||
|
this.setData({
|
||
|
currentPage: this.data.currentPage - 1
|
||
|
}, () => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
} else if (this.data.currentChapter > 1) {
|
||
|
// 上一章最后一页
|
||
|
const prevChapter = this.data.currentChapter - 1;
|
||
|
this.loadChapter(prevChapter).then(() => {
|
||
|
this.setData({
|
||
|
currentPage: this.data.pages.length - 1
|
||
|
}, () => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 下一页
|
||
|
nextPage() {
|
||
|
if (this.data.currentPage < this.data.pages.length - 1) {
|
||
|
this.setData({
|
||
|
currentPage: this.data.currentPage + 1
|
||
|
}, () => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
} else if (this.data.currentChapter < this.data.totalChapters) {
|
||
|
// 下一章第一页
|
||
|
const nextChapter = this.data.currentChapter + 1;
|
||
|
this.loadChapter(nextChapter).then(() => {
|
||
|
this.calculateProgress();
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 触摸开始
|
||
|
touchStart(e: any) {
|
||
|
this.touchStartX = e.touches[0].pageX;
|
||
|
},
|
||
|
|
||
|
// 触摸结束
|
||
|
touchEnd(e: any) {
|
||
|
const touchEndX = e.changedTouches[0].pageX;
|
||
|
const diff = touchEndX - this.touchStartX;
|
||
|
|
||
|
if (Math.abs(diff) > 50) { // 滑动距离大于50px才触发翻页
|
||
|
if (diff > 0) {
|
||
|
this.prevPage();
|
||
|
} else {
|
||
|
this.nextPage();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 切换工具栏显示
|
||
|
toggleToolbar() {
|
||
|
const now = Date.now();
|
||
|
if (now - this.data.lastTapTime < 300) {
|
||
|
// 双击
|
||
|
this.setData({
|
||
|
showToolbar: !this.data.showToolbar
|
||
|
});
|
||
|
}
|
||
|
this.setData({ lastTapTime: now });
|
||
|
},
|
||
|
|
||
|
// 切换目录显示
|
||
|
toggleMenu() {
|
||
|
this.setData({
|
||
|
showMenu: !this.data.showMenu
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 上一章
|
||
|
prevChapter() {
|
||
|
if (this.data.currentChapter > 1) {
|
||
|
this.loadChapter(this.data.currentChapter - 1);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 下一章
|
||
|
nextChapter() {
|
||
|
if (this.data.currentChapter < this.data.totalChapters) {
|
||
|
this.loadChapter(this.data.currentChapter + 1);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 跳转到指定章节
|
||
|
jumpToChapter(e: any) {
|
||
|
const chapter = e.currentTarget.dataset.chapter;
|
||
|
this.loadChapter(Number(chapter)); // 确保 chapter 是数字
|
||
|
this.toggleMenu();
|
||
|
},
|
||
|
|
||
|
// 返回
|
||
|
goBack() {
|
||
|
this.stopReadingTimer(); // 停止计时
|
||
|
|
||
|
// 打印阅读时间
|
||
|
const minutes = Math.floor(this.data.readingDuration / 60);
|
||
|
const seconds = this.data.readingDuration % 60;
|
||
|
console.log(`本次阅读时长: ${minutes}分${seconds}秒`);
|
||
|
console.log('详细信息:', {
|
||
|
开始时间: new Date(this.data.readingStartTime).toLocaleString(),
|
||
|
结束时间: new Date().toLocaleString(),
|
||
|
总秒数: this.data.readingDuration,
|
||
|
格式化时长: `${minutes}分${seconds}秒`
|
||
|
});
|
||
|
|
||
|
wx.navigateBack();
|
||
|
},
|
||
|
|
||
|
// 滚动处理
|
||
|
onScroll(e: any) {
|
||
|
// 可以在这里处理阅读进度保存等逻辑
|
||
|
},
|
||
|
|
||
|
// 切换设置面板
|
||
|
toggleSettings() {
|
||
|
console.log('切换设置面板', this.data.showSettings);
|
||
|
this.setData({
|
||
|
showSettings: !this.data.showSettings,
|
||
|
showMenu: false // 确保目录菜单关闭
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 减小字体
|
||
|
decreaseFontSize() {
|
||
|
if (this.data.fontSize > this.data.minFontSize) {
|
||
|
const newSize = this.data.fontSize - 2;
|
||
|
this.setData({
|
||
|
fontSize: newSize
|
||
|
}, () => {
|
||
|
// 重新计算分页
|
||
|
this.splitPages();
|
||
|
// 保存设置到本地
|
||
|
wx.setStorageSync('reader_font_size', newSize);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 增大字体
|
||
|
increaseFontSize() {
|
||
|
if (this.data.fontSize < this.data.maxFontSize) {
|
||
|
const newSize = this.data.fontSize + 2;
|
||
|
this.setData({
|
||
|
fontSize: newSize
|
||
|
}, () => {
|
||
|
// 重新计算分页
|
||
|
this.splitPages();
|
||
|
// 保存设置到本地
|
||
|
wx.setStorageSync('reader_font_size', newSize);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 更新时间
|
||
|
startTimeUpdate() {
|
||
|
const updateTime = () => {
|
||
|
const now = new Date();
|
||
|
const hours = now.getHours().toString().padStart(2, '0');
|
||
|
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||
|
this.setData({
|
||
|
currentTime: `${hours}:${minutes}`
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// 立即更新一次
|
||
|
updateTime();
|
||
|
// 每分钟更新一次
|
||
|
this.timeInterval = setInterval(updateTime, 60000);
|
||
|
},
|
||
|
|
||
|
// 计算阅读进度
|
||
|
calculateProgress() {
|
||
|
// 当前章节进度
|
||
|
const currentChapterProgress = this.data.pages.length > 0
|
||
|
? (this.data.currentPage + 1) / this.data.pages.length
|
||
|
: 0;
|
||
|
|
||
|
// 总进度计算:(当前章节 - 1 + 当前章节阅读进度) / 总章节数
|
||
|
const totalProgress = Math.floor(
|
||
|
((this.data.currentChapter - 1 + currentChapterProgress) / this.data.totalChapters) * 100
|
||
|
);
|
||
|
|
||
|
console.log('Progress calculation:', {
|
||
|
currentChapter: this.data.currentChapter,
|
||
|
totalChapters: this.data.totalChapters,
|
||
|
currentPage: this.data.currentPage,
|
||
|
totalPages: this.data.pages.length,
|
||
|
chapterProgress: currentChapterProgress,
|
||
|
totalProgress
|
||
|
});
|
||
|
|
||
|
this.setData({
|
||
|
readingProgress: totalProgress
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 关闭所有面板
|
||
|
closeAllPanels() {
|
||
|
this.setData({
|
||
|
showSettings: false,
|
||
|
showMenu: false
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 切换主题
|
||
|
switchTheme(e: any) {
|
||
|
const themeId = e.currentTarget.dataset.theme;
|
||
|
this.setData({
|
||
|
currentTheme: themeId,
|
||
|
showMore: false // 选择后关闭面板
|
||
|
});
|
||
|
wx.setStorageSync('reader_theme', themeId);
|
||
|
},
|
||
|
|
||
|
// 调整屏幕亮度
|
||
|
adjustBrightness(e: WechatMiniprogram.SliderChange) {
|
||
|
const value = e.detail.value;
|
||
|
console.log('调整亮度:', value); // 添加调试日志
|
||
|
wx.setScreenBrightness({
|
||
|
value: value / 100,
|
||
|
success: () => {
|
||
|
this.setData({ brightness: value });
|
||
|
wx.setStorageSync('reader_brightness', value);
|
||
|
console.log('亮度设置成功:', value);
|
||
|
},
|
||
|
fail: (error) => {
|
||
|
console.error('设置亮度失败:', error);
|
||
|
wx.showToast({
|
||
|
title: '设置亮度失败',
|
||
|
icon: 'none'
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 自动阅读相关
|
||
|
toggleAutoReading(e: any) {
|
||
|
const isChecked = e.detail.value;
|
||
|
this.setData({ autoReading: isChecked });
|
||
|
if (isChecked) {
|
||
|
this.startAutoReading();
|
||
|
} else {
|
||
|
this.stopAutoReading();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
setAutoReadingInterval(e: any) {
|
||
|
const interval = [3000, 5000, 8000, 10000][e.detail.value];
|
||
|
this.setData({ autoReadingInterval: interval });
|
||
|
if (this.data.autoReading) {
|
||
|
this.stopAutoReading();
|
||
|
this.startAutoReading();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
startAutoReading() {
|
||
|
this.autoReadingTimer = setInterval(() => {
|
||
|
this.nextPage();
|
||
|
}, this.data.autoReadingInterval);
|
||
|
},
|
||
|
|
||
|
stopAutoReading() {
|
||
|
if (this.autoReadingTimer) {
|
||
|
clearInterval(this.autoReadingTimer);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 书签相关
|
||
|
toggleBookmarks() {
|
||
|
this.setData({
|
||
|
showBookmarks: !this.data.showBookmarks,
|
||
|
showSettings: false,
|
||
|
showNotes: false
|
||
|
});
|
||
|
},
|
||
|
|
||
|
addBookmark() {
|
||
|
const bookmark = {
|
||
|
chapter: this.data.currentChapter,
|
||
|
page: this.data.currentPage,
|
||
|
content: this.data.pages[this.data.currentPage].slice(0, 50),
|
||
|
time: new Date().toLocaleString()
|
||
|
};
|
||
|
const bookmarks = [...this.data.bookmarks, bookmark];
|
||
|
this.setData({ bookmarks });
|
||
|
wx.setStorageSync(`bookmarks_${this.data.bookTitle}`, bookmarks);
|
||
|
wx.showToast({ title: '添加书签成功', icon: 'success' });
|
||
|
},
|
||
|
|
||
|
loadBookmarks() {
|
||
|
const bookmarks = wx.getStorageSync(`bookmarks_${this.data.bookTitle}`) || [];
|
||
|
this.setData({ bookmarks });
|
||
|
},
|
||
|
|
||
|
jumpToBookmark(e: any) {
|
||
|
const bookmark = e.currentTarget.dataset.bookmark;
|
||
|
this.loadChapter(bookmark.chapter).then(() => {
|
||
|
this.setData({
|
||
|
currentPage: bookmark.page,
|
||
|
showBookmarks: false
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 笔记相关
|
||
|
toggleNotes() {
|
||
|
this.setData({
|
||
|
showNotes: !this.data.showNotes,
|
||
|
showSettings: false,
|
||
|
showBookmarks: false
|
||
|
});
|
||
|
},
|
||
|
|
||
|
showTextMenu(e: any) {
|
||
|
const text = this.data.pages[this.data.currentPage];
|
||
|
const start = 0;
|
||
|
const end = text.length;
|
||
|
|
||
|
this.setData({
|
||
|
selectionStart: start,
|
||
|
selectionEnd: end,
|
||
|
selectedText: text.substring(start, end),
|
||
|
showTextMenu: true,
|
||
|
textMenuTop: e.touches[0].clientY,
|
||
|
textMenuLeft: e.touches[0].clientX
|
||
|
});
|
||
|
},
|
||
|
|
||
|
addNote() {
|
||
|
wx.showModal({
|
||
|
title: '添加笔记',
|
||
|
editable: true,
|
||
|
placeholderText: '请输入笔记内容',
|
||
|
success: (res) => {
|
||
|
if (res.confirm && res.content) {
|
||
|
const note = {
|
||
|
chapter: this.data.currentChapter,
|
||
|
page: this.data.currentPage,
|
||
|
content: res.content,
|
||
|
selectedText: this.data.selectedText,
|
||
|
time: new Date().toLocaleString()
|
||
|
};
|
||
|
const notes = [...this.data.notes, note];
|
||
|
this.setData({
|
||
|
notes,
|
||
|
showTextMenu: false
|
||
|
});
|
||
|
wx.setStorageSync(`notes_${this.data.bookTitle}`, notes);
|
||
|
wx.showToast({ title: '添加笔记成功', icon: 'success' });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
loadNotes() {
|
||
|
const notes = wx.getStorageSync(`notes_${this.data.bookTitle}`) || [];
|
||
|
this.setData({ notes });
|
||
|
},
|
||
|
|
||
|
copyText() {
|
||
|
wx.setClipboardData({
|
||
|
data: this.data.selectedText,
|
||
|
success: () => {
|
||
|
this.setData({ showTextMenu: false });
|
||
|
wx.showToast({ title: '复制成功', icon: 'success' });
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 阅读统计相关
|
||
|
startReadingTimer() {
|
||
|
this.setData({
|
||
|
readingStartTime: Date.now(),
|
||
|
isReading: true,
|
||
|
readingDuration: 0
|
||
|
});
|
||
|
|
||
|
// 每秒更新阅读时长
|
||
|
this.data.readingTimer = setInterval(() => {
|
||
|
const duration = Math.floor((Date.now() - this.data.readingStartTime) / 1000);
|
||
|
this.setData({ readingDuration: duration });
|
||
|
}, 1000);
|
||
|
},
|
||
|
|
||
|
updateReadingTime() {
|
||
|
const duration = Math.floor((Date.now() - this.data.readingStartTime) / 1000);
|
||
|
const today = new Date().toDateString();
|
||
|
const newTodayTime = this.data.todayReadingTime + duration;
|
||
|
this.setData({ todayReadingTime: newTodayTime });
|
||
|
|
||
|
let statistics = wx.getStorageSync('reading_statistics') || {};
|
||
|
statistics[today] = newTodayTime;
|
||
|
wx.setStorageSync('reading_statistics', statistics);
|
||
|
},
|
||
|
|
||
|
loadTodayReadingTime() {
|
||
|
const today = new Date().toDateString();
|
||
|
const statistics = wx.getStorageSync('reading_statistics') || {};
|
||
|
this.setData({ todayReadingTime: statistics[today] || 0 });
|
||
|
},
|
||
|
|
||
|
// 保存和恢复设置
|
||
|
saveReadingProgress() {
|
||
|
const progress = {
|
||
|
chapter: this.data.currentChapter,
|
||
|
page: this.data.currentPage,
|
||
|
timestamp: Date.now()
|
||
|
};
|
||
|
wx.setStorageSync(`reading_progress_${this.data.bookTitle}`, progress);
|
||
|
},
|
||
|
|
||
|
restoreSettings() {
|
||
|
// 恢复主题
|
||
|
const theme = wx.getStorageSync('reader_theme');
|
||
|
if (theme) {
|
||
|
this.setData({ currentTheme: theme });
|
||
|
}
|
||
|
|
||
|
// 恢复亮度
|
||
|
const brightness = wx.getStorageSync('reader_brightness');
|
||
|
if (brightness !== undefined) {
|
||
|
this.setData({ brightness });
|
||
|
wx.setScreenBrightness({ value: brightness / 100 });
|
||
|
}
|
||
|
|
||
|
// 恢复阅读进度
|
||
|
const progress = wx.getStorageSync(`reading_progress_${this.data.bookTitle}`);
|
||
|
if (progress) {
|
||
|
this.loadChapter(progress.chapter).then(() => {
|
||
|
this.setData({ currentPage: progress.page });
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
toggleMore() {
|
||
|
this.setData({
|
||
|
showMore: !this.data.showMore,
|
||
|
showSettings: false,
|
||
|
showMenu: false
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 处理文本选择事件
|
||
|
onSelectText(e: any) {
|
||
|
console.log('Selected text:', e.detail);
|
||
|
const selectedText = e.detail.text;
|
||
|
if (selectedText && selectedText.trim()) {
|
||
|
this.setData({ selectedText });
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 隐藏文本菜单
|
||
|
hideTextMenu() {
|
||
|
this.setData({
|
||
|
showTextMenu: false,
|
||
|
selectionStart: -1,
|
||
|
selectionEnd: -1
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 添加书签
|
||
|
addBookmarkWithSelection() {
|
||
|
const bookmark = {
|
||
|
chapter: this.data.currentChapter,
|
||
|
page: this.data.currentPage,
|
||
|
content: this.data.selectedText,
|
||
|
time: new Date().toLocaleString()
|
||
|
};
|
||
|
const bookmarks = [...this.data.bookmarks, bookmark];
|
||
|
this.setData({
|
||
|
bookmarks,
|
||
|
showTextMenu: false
|
||
|
});
|
||
|
wx.setStorageSync(`bookmarks_${this.data.bookTitle}`, bookmarks);
|
||
|
wx.showToast({
|
||
|
title: '添加书签成功',
|
||
|
icon: 'success'
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 添加笔记
|
||
|
addNoteWithSelection() {
|
||
|
wx.showModal({
|
||
|
title: '添加笔记',
|
||
|
editable: true,
|
||
|
placeholderText: '请输入笔记内容',
|
||
|
success: (res) => {
|
||
|
if (res.confirm && res.content) {
|
||
|
const note = {
|
||
|
chapter: this.data.currentChapter,
|
||
|
page: this.data.currentPage,
|
||
|
selectedText: this.data.selectedText,
|
||
|
content: res.content,
|
||
|
time: new Date().toLocaleString()
|
||
|
};
|
||
|
const notes = [...this.data.notes, note];
|
||
|
this.setData({
|
||
|
notes,
|
||
|
showTextMenu: false
|
||
|
});
|
||
|
wx.setStorageSync(`notes_${this.data.bookTitle}`, notes);
|
||
|
wx.showToast({
|
||
|
title: '添加笔记成功',
|
||
|
icon: 'success'
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// 复制选中文本
|
||
|
copySelectedText() {
|
||
|
if (this.data.selectedText) {
|
||
|
wx.setClipboardData({
|
||
|
data: this.data.selectedText,
|
||
|
success: () => {
|
||
|
this.setData({ showTextMenu: false });
|
||
|
wx.showToast({
|
||
|
title: '复制成功',
|
||
|
icon: 'success'
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 结束计时并保存
|
||
|
stopReadingTimer() {
|
||
|
if (!this.data.isReading) return;
|
||
|
|
||
|
if (this.data.readingTimer) {
|
||
|
clearInterval(this.data.readingTimer);
|
||
|
}
|
||
|
|
||
|
const endTime = Date.now();
|
||
|
const duration = Math.floor((endTime - this.data.readingStartTime) / 1000);
|
||
|
|
||
|
this.setData({
|
||
|
readingDuration: duration,
|
||
|
isReading: false
|
||
|
});
|
||
|
|
||
|
// 保存阅读记录
|
||
|
this.saveReadingRecord(duration);
|
||
|
},
|
||
|
|
||
|
// 保存阅读记录
|
||
|
saveReadingRecord(duration: number) {
|
||
|
const readingRecord = {
|
||
|
bookId: this.data.bookId,
|
||
|
startTime: this.data.readingStartTime,
|
||
|
duration: duration,
|
||
|
endTime: Date.now()
|
||
|
};
|
||
|
|
||
|
// 可以选择保存到本地或发送到服务器
|
||
|
wx.setStorageSync('lastReadingRecord', readingRecord);
|
||
|
|
||
|
wx.showToast({
|
||
|
title: `本次阅读 ${Math.floor(duration / 60)} 分钟`,
|
||
|
icon: 'none',
|
||
|
duration: 2000
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|