Parcourir la source

文献详情页面

Kevin Jiang il y a 2 ans
Parent
commit
d8f98d68d4

+ 19 - 0
src/client/paperDetail.client.ts

@@ -0,0 +1,19 @@
+/**
+ * 文献客户端
+ */
+import httpClient from "@/services/httpClient";
+import * as urlHelper from "@/libs/url.lib"
+import type { Response } from "@/types/response.types"
+import type { PaperDetail } from "@/types/doc.types";
+
+const _url = urlHelper.withPrefix("/gw/user/detail")
+
+export async function get(paperId: number): Promise<PaperDetail> {
+  const resp = await httpClient.get<Response<PaperDetail>>(_url(paperId))
+  return resp.data.data
+}
+
+export function pdfUrl(pdfPath: string, token: string) {
+  return `${_url("/pdf")}?path=${pdfPath}&token=${token}`;
+}
+

+ 0 - 13
src/services/doc.service.ts

@@ -1,13 +0,0 @@
-import type { Doc } from "@/types/doc.types";
-import type { Response } from "@/types/response.types";
-import type { AxiosResponse } from "axios";
-import httpClient from "./httpClient";
-
-/**
- * 根据文档ID查询文档详情
- * @param id 文档ID
- * @returns axios响应
- */
-export function fetch(id: Object): Promise<AxiosResponse<Response<Doc>>> {
-  return httpClient.get<Response<Doc>>("/gw/search/document/CN/" + id);
-}

+ 9 - 0
src/services/paperDetail.service.ts

@@ -0,0 +1,9 @@
+import * as paperDetailClient from "@/client/paperDetail.client"
+
+export async function get(paperId: number) {
+  return paperDetailClient.get(paperId)
+}
+
+export function pdfUrl(pdfPath: string, token: string) {
+  return paperDetailClient.pdfUrl(pdfPath, token)
+}

+ 17 - 35
src/stores/doc-detail.ts

@@ -1,47 +1,29 @@
-import type { CatalogNode, Doc } from "@/types/doc.types";
-import type { Response } from "@/types/response.types";
+import type { PaperDetail } from "@/types/doc.types";
 import { defineStore } from "pinia";
 import { ref, type Ref } from "vue";
-import * as docService from "@/services/doc.service";
-import type { AxiosResponse } from "axios";
+import * as paperDetailService from "@/services/paperDetail.service"
+import { useAuthStore } from "./auth.store";
 
 export const useDocDetailStore = defineStore("docDetail", () => {
-  const doc: Ref<Doc | null> = ref(null);
+  const detail: Ref<PaperDetail | null> = ref(null);
 
-  const catalog: Ref<CatalogNode[]> = ref([
-    {
-      title: '第一章 前言',
-      children: [
-        {
-          title: '第一节 概述'
-        },
-        {
-          title: '第二节 现状'
-        }
-      ]
-    },
-    {
-      title: '2 工业与民用建筑工程施工管理的主要内容',
-      children: [
-        {
-          title: '2.1 工业与民用建筑工程质量管理'
-        },
-        {
-          title: '2.2 工业与民用建筑工程施工进度管理'
-        }
-      ]
-    }
-  ]);
+  const pdfUrl: Ref<string | null> = ref(null);
 
-  function fetch(id: Object) {
-    return docService.fetch(id).then((resp: AxiosResponse<Response<Doc>>) => {
-      doc.value = resp.data.data;
-    });
+  const authStore = useAuthStore();
+
+  function fetch(id: number) {
+    return paperDetailService.get(id).then((resp) => {
+      detail.value = resp;
+      const token = authStore.token;
+      if (token) {
+        pdfUrl.value = paperDetailService.pdfUrl(resp.filePath, token)
+      }
+    })
   }
 
   return {
-    catalog,
-    doc,
+    detail,
+    pdfUrl,
     fetch,
   }
 });

+ 22 - 4
src/types/doc.types.ts

@@ -1,12 +1,30 @@
-export interface Doc {
+export interface Section {
+  // 章节ID
   id: number;
+  // 章节标题
   title: string;
+  // 上级章节标题
+  parentTitle: string;
+  // 章节内容
+  content: string;
+  // 章节所在页码
+  page: number;
+  // 章节标题
+  orderNum: number;
+  // 子章节内容
+  children: Section[];
 }
 
 /**
- * 文档章节
+ * 文献详情
  */
-export interface CatalogNode {
+export interface PaperDetail {
+  id: number;
   title: string;
-  children?: CatalogNode[];
+  journalName: string;
+  year: string;
+  summary: string;
+  keywords: string[];
+  filePath: string;
+  sections: Section[];
 }

+ 41 - 11
src/views/detail/DetailView.vue

@@ -1,9 +1,9 @@
 <script setup lang="ts">
-import { onMounted, onUnmounted, computed } from 'vue';
+import { onMounted, onUnmounted, computed, ref } from 'vue';
 import { useDocDetailStore } from '@/stores/doc-detail';
 import { useSideBarStore } from '@/stores/side-bar';
 import { useRoute } from 'vue-router';
-import DocCatalog from './components/DocCatalog.vue';
+import PaperSection from './components/PaperSection.vue';
 
 // 当前路由
 const route = useRoute();
@@ -12,35 +12,48 @@ const sideBarStore = useSideBarStore();
 // 文档详情
 const docDetailStore = useDocDetailStore();
 
-const doc = computed(() => docDetailStore.doc);
+const detail = computed(() => docDetailStore.detail)
+const pdfPath = computed(() => docDetailStore.pdfUrl)
+const pdfPage = ref(1)
+
+function onSubSectionClick(page: number) {
+  console.log('page', page)
+  pdfPage.value = page
+}
 
 onMounted(() => {
   // 收起左侧边栏
   sideBarStore.setCollapse(true);
   // 查询文档详情内容
-  const id = route.params.id;
+  const id = parseInt(route.params.id as string);
   if (id) {
     docDetailStore.fetch(id);
   }
 });
 
-const catalog = computed(() => docDetailStore.catalog);
-
 onUnmounted(() => {
   sideBarStore.setCollapse(false);
 });
 </script>
 
 <template>
-  <a-row type="flex" :gutter="24">
+  <a-row type="flex" :gutter="24" class="detail-view">
     <a-col :span="5">
       <h2>目录</h2>
-      <DocCatalog :catalog="catalog"></DocCatalog>
+      <PaperSection :data="detail?.sections" @sub-section-click="onSubSectionClick" />
     </a-col>
     <a-col :span="19">
-      <div v-if="doc" class="doc-wrap">
-        <h2 class="title">{{ doc.title }}</h2>
-        <p>文档详情</p>
+      <div v-if="detail?.filePath" class="doc-wrap">
+        <!-- <h1 class="title">{{ detail.title }}</h1> -->
+        <iframe
+          v-if="pdfPath"
+          :src="pdfPath + '&page=' + pdfPage + '#page=' + pdfPage"
+          type="application/pdf"
+          frameborder="0"
+          width="100%"
+          height="100%"
+          class="pdf-viewer"
+        />
       </div>
       <div v-else>
         <a-empty />
@@ -48,3 +61,20 @@ onUnmounted(() => {
     </a-col>
   </a-row>
 </template>
+
+<style scoped lang="scss">
+@import "@/assets/base.scss";
+
+.detail-view {
+  margin-top: 24px;
+  min-height: 100vh;
+
+  .doc-wrap {
+    height: 100%;
+
+    .pdf-viewer {
+      border: 1px solid $border-color-secondary;
+    }
+  }
+}
+</style>

+ 0 - 51
src/views/detail/components/DocCatalog.vue

@@ -1,51 +0,0 @@
-<script setup lang="ts">
-import { ref, type Ref, type PropType } from "vue";
-import type { CatalogNode } from "@/types/doc.types";
-
-const props = defineProps({
-  catalog: {
-    type: Array as PropType<CatalogNode[]>,
-    default() {
-      return [];
-    }
-  }
-});
-
-const activeKey: Ref<string[]> = ref([]);
-
-function onChapterClick(e: MouseEvent, index: number, subIndex?: number) {
-  e.stopPropagation();
-  const catalogNode = props.catalog[index];
-  if (subIndex === undefined) {
-    console.log('点击了第一级章节标题', catalogNode.title);
-  } else {
-    const subCatalogNode = catalogNode.children![subIndex];
-    console.log('点击了第二级标题', subCatalogNode.title);
-  }
-}
-</script>
-
-<template>
-  <a-collapse v-model:activeKey="activeKey">
-    <a-collapse-panel
-      v-for="(item, index) in catalog"
-      :header="item.title"
-      :key="index"
-      @click="onChapterClick($event, index)"
-    >
-      <div v-for="(subItem, subIndex) in item.children" :key="index+'-'+subIndex" class="sub-catalog">
-        <a href="javascript:void(0)" @click="onChapterClick($event, index, subIndex)">{{ subItem.title }}</a>
-      </div>
-    </a-collapse-panel>
-  </a-collapse>
-</template>
-
-<style scoped lang="scss">
-.sub-catalog {
-  margin-top: 6px;
-
-  &:first-child {
-    margin-top: 0;
-  }
-}
-</style>

+ 57 - 0
src/views/detail/components/PaperSection.vue

@@ -0,0 +1,57 @@
+<script setup lang="ts">
+import { type PropType, computed } from "vue";
+import type { Section } from "@/types/doc.types";
+
+const props = defineProps({
+  data: {
+    type: Array as PropType<Section[]>,
+    default() {
+      return [];
+    }
+  }
+});
+
+const emits = defineEmits<{
+  (e: "subSectionClick", page: number): void
+}>();
+
+const activeKey = computed(() => props.data.map((_, index) => index))
+
+function onSectionClick(e: MouseEvent, index: number, subIndex?: number) {
+  e.stopPropagation();
+  const section = props.data[index];
+  if (subIndex === undefined) {
+    console.log('点击了第一级章节标题', section.title);
+  } else {
+    const subSection = section.children[subIndex];
+    console.log('点击了第二级标题', subSection.title);
+    emits("subSectionClick", subSection.page)
+  }
+}
+</script>
+
+<template>
+  <a-collapse v-model:activeKey="activeKey">
+    <a-collapse-panel
+      v-for="(item, index) in data"
+      :header="item.title"
+      :key="index"
+      @click="onSectionClick($event, index)"
+    >
+      <div v-for="(subSection, subIndex) in item.children" :key="index+'-'+subIndex" class="sub-section">
+        <a href="javascript:void(0)" @click="onSectionClick($event, index, subIndex)">{{ subSection.title }}</a>
+      </div>
+    </a-collapse-panel>
+  </a-collapse>
+</template>
+
+<style scoped lang="scss">
+.sub-section {
+  margin-top: 10px;
+  line-height: 24px;
+
+  &:first-child {
+    margin-top: 0;
+  }
+}
+</style>