提交 fbb94355 authored 作者: 龙菲's avatar 龙菲

优化目录

上级 cf12f22a
差异被折叠。
......@@ -13,15 +13,17 @@
"axios": "^1.8.4",
"element-plus": "^2.9.8",
"turn.js": "^1.0.5",
"vant": "^4.9.19",
"vue": "^3.5.13",
"vue-easy-lightbox": "^1.19.0",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^5.2.3",
"sass-embedded": "^1.89.1",
"unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.5.0",
"unplugin-auto-import": "^19.3.0",
"unplugin-vue-components": "^28.7.0",
"vite": "^6.2.4",
"vite-plugin-vue-devtools": "^7.7.2"
}
......
......@@ -8,21 +8,14 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
BookMarks: typeof import('./components/BookMarks.vue')['default']
BookReader: typeof import('./components/BookReader.vue')['default']
copy: typeof import('./components/Guide copy.vue')['default']
ElBacktop: typeof import('element-plus/es')['ElBacktop']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTree: typeof import('element-plus/es')['ElTree']
FileUpload: typeof import('./components/FileUpload.vue')['default']
Guide: typeof import('./components/Guide.vue')['default']
......@@ -32,7 +25,17 @@ declare module 'vue' {
IconEcosystem: typeof import('./components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./components/icons/IconTooling.vue')['default']
List: typeof import('./components/List.vue')['default']
WelcomeItem: typeof import('./components/WelcomeItem.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanButton: typeof import('vant/es')['Button']
VanCell: typeof import('vant/es')['Cell']
VanCol: typeof import('vant/es')['Col']
VanIcon: typeof import('vant/es')['Icon']
VanList: typeof import('vant/es')['List']
VanNavBar: typeof import('vant/es')['NavBar']
VanPopup: typeof import('vant/es')['Popup']
VanRow: typeof import('vant/es')['Row']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']
}
}
<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 @goToPage="$emit('goToPage', $event)" :level="level + 1" :bookmarks="item.children" />
</template>
</template>
</template>
<script setup name="BookMarks">
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>
\ No newline at end of file
<template>
<div
class="book-reader"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
>
<div class="book-reader" @touchstart="handleTouchStart" @touchend="handleTouchEnd">
<!-- <div v-if="loading" class="loading-overlay">
<div class="loader"></div>
<p>正在加载图片...</p>
......@@ -11,10 +7,7 @@
<div class="magazine-viewport">
<div ref="magazine" class="magazine">
<div
v-for="(page, index) in processedPages"
:key="index"
:class="[
<div v-for="(page, index) in processedPages" :key="index" :class="[
'page',
`p${index + 1}`,
{
......@@ -23,44 +16,34 @@
even: !page.isWide && index % 2 === 1,
mobile: isMobile,
},
]"
>
<img
:data-page-index="index"
:src="page.src"
:alt="`第 ${page.page_num} 页`"
@error="handleImageError(index)"
:class="['page-image', { 'wide-image': page.isWide }]"
/>
]">
<img :data-page-index="index" :src="page.src" :alt="`第 ${page.page_num} 页`" @error="handleImageError(index)"
:class="['page-image', { 'wide-image': page.isWide }]" />
<!-- 添加小图叠加层 -->
<div
v-if="page.images && page.images.length > 0"
class="small-images-overlay"
>
<div
v-for="(smallImage, imgIndex) in page.images"
:key="imgIndex"
class="small-image-container"
:style="{
<div v-if="page.images && page.images.length > 0" class="small-images-overlay">
<div v-for="(smallImage, imgIndex) in page.images" :key="imgIndex" class="small-image-container" :style="{
left: `${smallImage.position.x1 * 100}%`,
top: `${smallImage.position.y1 * 100}%`,
width: `${
(smallImage.position.x2 - smallImage.position.x1) * 100
width: `${(smallImage.position.x2 - smallImage.position.x1) * 100
}%`,
height: `${
(smallImage.position.y2 - smallImage.position.y1) * 100
height: `${(smallImage.position.y2 - smallImage.position.y1) * 100
}%`,
}"
@click="handleSmallImageClick(smallImage, page.page_num)"
>
}" @click="handleSmallImageClick(smallImage, page.page_num)">
<!-- <img :src="smallImage.url" :alt="`小图 ${imgIndex + 1}`" class="small-image" /> -->
</div>
</div>
</div>
</div>
</div>
<van-tabbar class="tab-bar">
<div class="tab-bar-item" v-for="(item, index) in tabbars" :key="index" @click="clickTabBar(item.code)">
<!-- 目录 -->
<van-icon :name="item.icon" size="20" />
</div>
</van-tabbar>
<!--
<div class="controls" :class="{ 'mobile-controls': isMobile }">
<el-button class="control-button" @click="previous" circle>
<el-icon>
......@@ -87,17 +70,13 @@
<Menu />
</el-icon>
</el-button>
</div>
</div> -->
<div v-if="showExitMessage" class="exit-message">按 ESC 键退出阅读</div>
<!-- 替换为 vue-easy-lightbox 预览组件 -->
<vue-easy-lightbox
:visible="showViewer"
:imgs="previewImages"
:index="currentImageIndex"
@hide="showViewer = false"
/>
<vue-easy-lightbox :visible="showViewer" :imgs="previewImages" :index="currentImageIndex"
@hide="showViewer = false" />
<div class="mobile-gesture-hint" v-if="isMobile">左右滑动切换页面</div>
......@@ -119,7 +98,7 @@ import {
} from "@element-plus/icons-vue";
import VueEasyLightbox from "vue-easy-lightbox";
import Guide from "./Guide.vue";
import { showImagePreview } from 'vant';
const props = defineProps({
pages: {
type: Array,
......@@ -131,7 +110,33 @@ const props = defineProps({
default: () => [],
},
});
const tabbars = ref([
{
label: '目录',
icon: 'apps-o',
code: 'menu'
},
{
label: '上一页',
icon: 'arrow-left',
code: 'previous'
},
{
label: '下一页',
icon: 'arrow',
code: 'next'
},
{
label: '放大',
icon: 'plus',
code: 'plus'
},
{
label: '缩小',
icon: 'minus',
code: 'minus'
}
])
const magazine = ref(null);
const loading = ref(true);
const showExitMessage = ref(false);
......@@ -493,6 +498,26 @@ const handleImageError = async (index) => {
}
};
const clickTabBar = (code) => {
switch (code) {
case 'menu':
showGuide()
break;
case 'previous':
previous()
break;
case 'next':
next()
break;
case 'minus':
zoomIn()
break;
case 'menu':
zoomOut()
break;
}
}
const next = () => {
if (magazine.value && isInitialized.value) {
const $magazine = $(magazine.value);
......@@ -729,18 +754,27 @@ const handleSmallImageClick = (smallImage, pageNum) => {
// 获取当前页面所有小图的URL
const currentPage = props.pages.find((page) => page.page_num === pageNum);
if (currentPage && currentPage.images) {
previewImages.value = currentPage.images.map((img) => ({
src: img.url,
title: `第 ${pageNum} 页`,
}));
// 设置当前点击的图片索引
// // 设置当前点击的图片索引
currentImageIndex.value = currentPage.images.findIndex(
(img) => img.url === smallImage.url
);
const images = currentPage.images.map((img) => img.url)
console.log(12,images);
// 显示预览组件
showViewer.value = true;
showImagePreview({
images,
closeable: true,
startPosition: currentImageIndex.value,
});
// previewImages.value = currentPage.images.map((img) => ({
// src: img.url,
// title: `第 ${pageNum} 页`,
// }));
// // 显示预览组件
// showViewer.value = true;
}
};
......@@ -777,7 +811,7 @@ const showGuide = () => {
};
</script>
<style scoped>
<style scoped lang="scss">
.book-reader {
width: 100vw;
height: 100vh;
......@@ -820,12 +854,10 @@ const showGuide = () => {
left: 50%;
width: 1px;
height: 100%;
background: linear-gradient(
to bottom,
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%
);
rgba(0, 0, 0, 0.1) 100%);
z-index: 10;
pointer-events: none;
}
......@@ -1258,4 +1290,16 @@ const showGuide = () => {
grid-template-columns: repeat(auto-fill, minmax(20vw, 1fr)) !important;
}
}
.tab-bar {
display: flex;
.tab-bar-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>
<template>
<el-drawer v-model="showDirectory" direction="ltr" style="wdith: 40%">
<template #header>
<h4>目录</h4>
</template>
<template #default>
<el-tree
:data="bookmarks"
:props="defaultProps"
@node-click="handleNodeClick"
/>
</template>
<template #footer>
<div style="flex: auto">
<el-button type="primary" @click="confirmClick">关闭</el-button>
</div>
</template>
</el-drawer>
<!-- 圆角弹窗(底部) -->
<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"/>
</van-list>
</van-popup>
</template>
<script setup>
import { ArrowUpBold } from "@element-plus/icons-vue";
const $emit = defineEmits(["goToPage"]);
const props = defineProps({
bookmarks: {
......@@ -121,9 +110,11 @@ defineExpose({
overflow-y: auto;
display: flex;
flex-wrap: wrap;
gap: 10px; /* 添加行间距和列间距 */
gap: 10px;
/* 添加行间距和列间距 */
position: relative;
}
.content {
.back-top {
position: absolute;
......@@ -137,19 +128,23 @@ defineExpose({
cursor: pointer;
transition: transform 0.2s;
overflow: hidden;
width: calc(33.333% - 10px); /* 调整宽度,减去间距 */
width: calc(33.333% - 10px);
/* 调整宽度,减去间距 */
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid transparent;
&:hover {
border: 1px solid #000;
}
img {
width: 100%;
height: 200px;
object-fit: contain;
}
.page-number {
font-size: 20px;
position: absolute;
......@@ -161,8 +156,10 @@ defineExpose({
color: #fff;
}
}
.active {
border: 1px solid #000;
.page-number {
color: var(--el-color-success);
background-color: #fff;
......@@ -171,10 +168,13 @@ defineExpose({
@media (max-width: 768px) {
.thumbnail-grid {
gap: 10px; /* 保持间距 */
gap: 10px;
/* 保持间距 */
}
.thumbnail-item {
width: calc(33.333% - 10px); /* 保持宽度调整 */
width: calc(33.333% - 10px);
/* 保持宽度调整 */
}
}
</style>
<template>
<div>
<div class="nav"></div>
<van-nav-bar :title="title" left-text="返回" left-arrow @click-left="onClickLeft" />
<BookReader :pages="pages" :bookmarks="bookmarks" />
</div>
</template>
......@@ -8,9 +8,11 @@
import { getDocumentDetail } from "@/api";
import BookReader from "@/components/BookReader.vue";
const route = useRoute();
const router = useRouter();
/* props */
/* emit */
/* data */
const title = ref('')
const pages = ref([]);
const bookmarks = ref([]);
/* computed */
......@@ -30,9 +32,14 @@ async function loadData(id) {
};
});
}
function onClickLeft(){
router.go(-1)
}
onMounted(() => {
const { id } = route.params;
bookmarks.value = JSON.parse(route.query.bookmarks);
bookmarks.value = JSON.parse(localStorage.getItem(`bookmarks-${id}`));
title.value = route.query.title
loadData(id);
});
</script>
......
<template>
<div class="book-list-container">
<el-row :gutter="16" class="book-card-list">
<van-nav-bar title="微方志">
<template #right>
<van-icon name="search" size="20" />
</template>
</van-nav-bar>
<van-row class="book-card-list">
<van-col span="8" v-for="(book, index) in books" :key="book.book_id + index">
<div class="book-card" shadow="hover" @click="openBook(book)">
<div class="book-cover">{{ book.name }}</div>
<div class="book-name">{{ book.name }}</div>
</div>
</van-col>
</van-row>
<!-- <el-row :gutter="16" class="book-card-list">
<el-col
v-for="(book, index) in books"
:key="book.book_id + index"
:span="8"
>
<div class="book-card" shadow="hover" @click="openBook(book)">
<!-- 这里可根据实际书籍封面逻辑替换,示例用纯色背景占位 -->
<div class="book-cover">{{ book.name }}</div>
<div class="book-name">{{ book.name }}</div>
</div>
</el-col>
</el-row>
</el-row> -->
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { getDocList } from "@/api/index";
import {
ref,
computed
} from "vue";
import {
getDocList
} from "@/api/index";
import router from "@/router";
// 模拟接口返回数据类型
interface Book {
......@@ -35,12 +53,14 @@ async function loadData() {
const res = await getDocList();
books.value = res;
}
function openBook(book) {
console.log(book);
const query = {
bookmarks: book.bookmarks,
title:book.name
};
const path = `/detail/${book.book_id}`;
localStorage.setItem(`bookmarks-${book.book_id}`, book.bookmarks)
router.push({
query,
path,
......@@ -51,12 +71,15 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
<style lang="scss" scoped>
.book-list-container {
height: 100vh;
.book-card-list{
padding: 30px;
}
.book-card {
border-radius: 8px;
.book-cover {
background-color: rgba(65, 184, 131, 0.4);
padding: 50px 10px;
......
......@@ -6,7 +6,7 @@ import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { ElementPlusResolver,VantResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
......@@ -17,13 +17,13 @@ export default defineConfig({
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue', 'vue-router', 'pinia'],
// 自动导入 Element Plus 相关函数
resolvers: [ElementPlusResolver()],
resolvers: [ElementPlusResolver(),VantResolver()],
// 生成自动导入的TS声明文件
dts: 'src/auto-imports.d.ts',
}),
Components({
// 自动导入组件
resolvers: [ElementPlusResolver()],
resolvers: [ElementPlusResolver(),VantResolver()],
// 生成自动导入的TS声明文件
dts: 'src/components.d.ts',
}),
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论