提交 79d95f31 authored 作者: 龙菲's avatar 龙菲

增加pc端的抽屉

上级 016d0c67
......@@ -8,14 +8,17 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
BookMarks: typeof import('./components/BookMarks.vue')['default']
BookMarks: typeof import('./components/GuideMobile/bookMarks.vue')['default']
BookReader: typeof import('./components/BookReader/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElRow: typeof import('element-plus/es')['ElRow']
FileUpload: typeof import('./components/FileUpload.vue')['default']
Guide: typeof import('./components/BookReader/guide.vue')['default']
Guide: typeof import('./components/GuideMobile/guide.vue')['default']
GuideMobile: typeof import('./components/GuideMobile/index.vue')['default']
GuidePc: typeof import('./components/GuidePc/index.vue')['default']
IconCommunity: typeof import('./components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./components/icons/IconEcosystem.vue')['default']
......@@ -29,4 +32,7 @@ declare module 'vue' {
VanNavBar: typeof import('vant/es')['NavBar']
VanPopup: typeof import('vant/es')['Popup']
}
export interface GlobalDirectives {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
......@@ -3,6 +3,7 @@
class="book-reader"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
v-loading="loading"
>
<div class="magazine-viewport">
<div ref="magazine" class="magazine">
......@@ -69,7 +70,16 @@
<!-- <div class="mobile-gesture-hint" v-if="isMobile">左右滑动切换页面</div> -->
<!-- Replace directory modal with Element Plus dialog -->
<Guide ref="guideRef" :bookmarks="bookmarks" @goToPage="goToPage"></Guide>
<GuideMobile
ref="guideMobileRef"
:bookmarks="bookmarks"
@goToPage="goToPage"
></GuideMobile>
<GuidePc
ref="guidePcRef"
:bookmarks="bookmarks"
@goToPage="goToPage"
></GuidePc>
</div>
<div class="footer">
<el-row :gutter="10" class="tab-bar">
......@@ -91,7 +101,7 @@
<svg-icon name="to-left" size="22" @click="previous"></svg-icon>
<div class="page-count">{{ currentPage }}</div>
<svg-icon name="to-right" size="22" @click="next"></svg-icon>
<svg-icon name="to-end" size="30" @clicl="toEnd"></svg-icon>
<svg-icon name="to-end" size="30" @click="toEnd"></svg-icon>
</el-col>
<el-col class="right" :span="8">
<!-- <svg-icon name="download" size="20"></svg-icon> -->
......@@ -107,8 +117,7 @@ import "turn.js";
// import * as 'turn' from '/public/js/turn'
import { ref, onMounted, onUnmounted, watch, nextTick, computed } from "vue";
import VueEasyLightbox from "vue-easy-lightbox";
import Guide from "./guide.vue";
import { showImagePreview } from "vant";
import GuideMobile from "../GuideMobile/index.vue";
import audioFlip from "@/assets/audio/flip.mp3";
const props = defineProps({
pages: {
......@@ -148,7 +157,8 @@ const processedPages = ref([]);
const pageTurnSound = ref(null);
const audioInitialized = ref(false);
const directoryImages = ref([]);
const guideRef = ref(null);
const guideMobileRef = ref(null);
const guidePcRef = ref(null);
const currentPage = ref(0);
const isMuted = ref(false); // 默认不静音
......@@ -198,7 +208,6 @@ const processPages = (pages) => {
}));
};
// 修改图片加载函数
const loadImage = async (pageIndex) => {
const page = processedPages.value[pageIndex];
if (!page || page.isLoaded) return true;
......@@ -224,24 +233,35 @@ const loadImage = async (pageIndex) => {
try {
const img = new Image();
await new Promise((resolve, reject) => {
img.onload = () => {
clearTimeout(timeoutId);
loadingTimeouts.value.delete(pageIndex);
imageCache.value.set(src, true);
page.isLoaded = true;
page.src = src;
resolve();
};
img.onerror = () => {
clearTimeout(timeoutId);
loadingTimeouts.value.delete(pageIndex);
reject(new Error("Image load failed"));
};
img.src = src;
});
await Promise.race([
new Promise((resolve, reject) => {
img.onload = () => {
clearTimeout(timeoutId);
loadingTimeouts.value.delete(pageIndex);
imageCache.value.set(src, true);
page.isLoaded = true;
page.src = src;
resolve();
};
img.onerror = () => {
clearTimeout(timeoutId);
loadingTimeouts.value.delete(pageIndex);
reject(new Error("Image load failed"));
};
img.src = src;
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), 3000)
),
]);
return true;
} catch (error) {
// 如果加载失败,直接显示占位图
const $img = $(magazine.value).find(`.p${pageIndex + 1} img`);
if ($img.length) {
$img[0].src =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI2YwZjBmMCIvPjx0ZXh0IHg9IjUwJSIgeT0iNTAlIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTYiIGZpbGw9IiM5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj7lvIDlp4vmlbDmja7lupM8L3RleHQ+PC9zdmc+";
}
return false;
} finally {
loadingQueue.value.delete(pageIndex);
......@@ -255,9 +275,9 @@ const loadVisiblePages = async (currentPage) => {
const $magazine = $(magazine.value);
const totalPages = $magazine.turn("pages");
// 只加载当前页和下一
const startPage = currentPage;
const endPage = Math.min(totalPages, currentPage + 1);
// 加载当前页、上一页、下一页和下下
const startPage = Math.max(1, currentPage - 1);
const endPage = Math.min(totalPages, currentPage + 2);
const loadTasks = [];
for (let i = startPage; i <= endPage; i++) {
......@@ -272,9 +292,12 @@ const loadVisiblePages = async (currentPage) => {
}
try {
loading.value = true;
await Promise.all(loadTasks);
} catch (error) {
console.error("加载页面失败:", error);
} finally {
loading.value = false;
}
};
......@@ -356,6 +379,11 @@ const initBook = async () => {
turning: async (event, page) => {
loading.value = true;
await loadVisiblePages(page);
// 确保加载完成后才继续翻页
setTimeout(() => {
event.preventDefault();
$magazine.turn("page", page);
}, 100);
},
turned: async (event, page) => {
currentPage.value = page; // 更新当前页码
......@@ -375,13 +403,13 @@ const initBook = async () => {
};
// 修改加载图片函数
const loadImages = async () => {
loading.value = true;
// loading.value = true;
try {
await initBook();
} catch (error) {
console.error("加载图片失败:", error);
} finally {
loading.value = false;
// loading.value = false;
}
};
......@@ -478,13 +506,13 @@ const previous = () => {
};
const toFirst = () => {
const firstPage = props.pages[0].page;
$magazine.turn("page", firstPage);
const firstPage = props.pages[0].page_num;
goToPage(firstPage);
};
const toEnd = () => {
const endPage = props.pages[props.pages.length - 1].page;
$magazine.turn("page", endPage);
const endPage = props.pages[props.pages.length - 1].page_num;
goToPage(endPage);
};
const zoomIn = () => {
......@@ -772,7 +800,11 @@ const goToPage = (pageNum) => {
};
const showGuide = () => {
guideRef.value.show();
if (isMobile.value) {
guideMobileRef.value.show();
} else {
guidePcRef.value.show();
}
};
</script>
......@@ -843,22 +875,22 @@ const showGuide = () => {
transform-style: preserve-3d;
}
.magazine::after {
content: "";
position: absolute;
top: 0;
left: 50%;
width: 1px;
height: 100%;
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.1) 0%,
rgba(0, 0, 0, 0.2) 50%,
rgba(0, 0, 0, 0.1) 100%
);
z-index: 10;
pointer-events: none;
}
// .magazine::after {
// content: "";
// position: absolute;
// top: 0;
// left: 50%;
// width: 1px;
// height: 100%;
// background: linear-gradient(
// to bottom,
// rgba(0, 0, 0, 0.1) 0%,
// rgba(0, 0, 0, 0.2) 50%,
// rgba(0, 0, 0, 0.1) 100%
// );
// z-index: 10;
// pointer-events: none;
// }
.page {
background-color: white;
......@@ -1034,6 +1066,9 @@ const showGuide = () => {
color: #ccc;
width: 100%;
height: 100%;
.svg-icon {
cursor: pointer;
}
.left,
.right,
.center {
......
<template>
<!-- 圆角弹窗(底部) -->
<van-popup v-model:show="showDirectory" round position="bottom" :style="{ height: '80%' }">
<van-nav-bar title="目录"/>
<van-popup
v-model:show="showDirectory"
round
position="bottom"
:style="{ height: '80%' }"
>
<van-nav-bar title="目录" />
<van-list :finished="true" finished-text="没有更多了">
<BookMarks :bookmarks="bookmarks" @goToPage="goToPage"/>
<BookMarks :bookmarks="bookmarks" @goToPage="goToPage" />
</van-list>
</van-popup>
</template>
<script setup>
import BookMarks from "./bookMarks.vue";
const $emit = defineEmits(["goToPage"]);
const props = defineProps({
bookmarks: {
......
<template>
<template v-for="item in bookmarks" :key="item">
<van-cell
:title="item.title"
:style="{ paddingLeft: `${level * 20 + 30}px` }"
@click.prevent="clickCell(item.page)"
/>
<template v-if="item.children">
<BookMarks :bookmarks="bookmarks" @goToPage="goToPage" />
</template>
</template>
</template>
<script setup name="BookMarksPc">
const $emit = defineEmits(["goToPage"]);
const props = defineProps({
bookmarks: {
type: Array,
default: () => [],
},
level: {
type: Number,
default: 0, // 默认层级是 0(第一层)
},
});
const clickCell = (page) => {
$emit("goToPage", page);
};
</script>
<style lang="scss" scoped></style>
<template>
<el-drawer
title="目录"
v-model="visible"
direction="ltr"
size="30%"
:before-close="close"
:destroy-on-close="true"
:show-close="true"
:wrapperClosable="true"
>
<BookMarks :bookmarks="bookmarks" @goToPage="goToPage" />
</el-drawer>
</template>
<script lang="ts" setup>
/* props */
const props = defineProps({
bookmarks: {
type: Array,
default: () => [],
},
currentPage: {
type: Number,
default: 0,
},
});
/* emit */
const $emit = defineEmits(["goToPage"]);
/* data */
const visible = ref(false);
/* computed */
/* methods */
function close() {
visible.value = false;
}
function goToPage(pageNum) {
$emit("goToPage", pageNum);
close();
}
function show() {
visible.value = true;
}
defineExpose({
show,
});
</script>
<style lang="scss" scoped></style>
<template>
<div>
<div class="header">
<el-button type="primary" link class="back">返回</el-button>
<el-button type="primary" link class="back" @click="goBack"
>返回</el-button
>
微方志
</div>
<BookReader :pages="pages" :bookmarks="bookmarks" />
......@@ -36,7 +38,7 @@ async function loadData(id) {
});
}
function onClickLeft() {
function goBack() {
router.go(-1);
}
onMounted(() => {
......
......@@ -61,7 +61,12 @@ export default defineConfig(({ mode, command }) => {
'/api': {
target: 'http://222.85.214.245:9666',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_API_BASE_URL), '/api'),
bypass(req, res, options) {
const realUrl = options.target + (options.rewrite ? options.rewrite(req.url) : '');
console.log(realUrl); // 在终端显示
res.setHeader('A-Real-Url', realUrl); // 添加响应标头(A-Real-Url为自定义命名),在浏览器中显示
},
}
},
host: '0.0.0.0',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论