feat: 首页文献列表接入显示设置 — 新增共享齿轮面板组件并复用
CI / backend (push) Waiting to run
CI / frontend (push) Waiting to run

首页卡片原未传 displaySettings 导致除标题日期外全部字段隐藏;
新建 DisplaySettingsPopover 共享组件,首页列表顶部加「最新文献」行+齿轮按钮,
搜索页改用共享组件,两页显示设置全局同步(localStorage 持久化)。
This commit is contained in:
34047007@qq.com
2026-08-10 23:36:53 +08:00
parent cb4cb0d178
commit dba5aebd87
4 changed files with 43 additions and 21 deletions
+1
View File
@@ -8,6 +8,7 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AdvancedSearchPanel: typeof import('./components/search/AdvancedSearchPanel.vue')['default'] AdvancedSearchPanel: typeof import('./components/search/AdvancedSearchPanel.vue')['default']
DisplaySettingsPopover: typeof import('./components/literature/DisplaySettingsPopover.vue')['default']
LiteratureCard: typeof import('./components/literature/LiteratureCard.vue')['default'] LiteratureCard: typeof import('./components/literature/LiteratureCard.vue')['default']
LiteraturePreviewDrawer: typeof import('./components/literature/LiteraturePreviewDrawer.vue')['default'] LiteraturePreviewDrawer: typeof import('./components/literature/LiteraturePreviewDrawer.vue')['default']
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem'] NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { NButton, NIcon, NPopover, NCheckbox } from 'naive-ui'
import { SettingsOutline } from '@vicons/ionicons5'
import { useDisplaySettings } from '../../composables/useDisplaySettings'
const displaySettings = useDisplaySettings()
</script>
<template>
<NPopover trigger="click" placement="bottom-end" :width="200">
<template #trigger>
<NButton size="tiny" quaternary title="显示设置"><template #icon><NIcon size="14"><SettingsOutline /></NIcon></template></NButton>
</template>
<div style="font-size:13px;font-weight:600;margin-bottom:8px">显示设置</div>
<div style="display:flex;flex-direction:column;gap:4px;font-size:13px">
<NCheckbox v-model:checked="displaySettings.showAuthors">作者</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showAffiliation">机构</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showJournal">期刊+分区</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showPmid">PMID</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showDoi">DOI</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showStudyTypes">研究类型+OA+NCT</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showTags">MeSH 标签</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showCitedBy">被引次数</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showAiSummary">AI 摘要</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showBadges">徽章</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showActions">操作按钮</NCheckbox>
</div>
</NPopover>
</template>
+4 -21
View File
@@ -2,8 +2,8 @@
import { ref, computed, onMounted, onUnmounted, watch } from 'vue' import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router' import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router'
import { useAuthStore } from '../../stores/auth' import { useAuthStore } from '../../stores/auth'
import { NInput, NButton, NEmpty, NSelect, NCheckboxGroup, NCheckbox, NPagination, NIcon, NRadioGroup, NRadio, NSlider, NModal, NPopover } from 'naive-ui' import { NInput, NButton, NEmpty, NSelect, NCheckboxGroup, NCheckbox, NPagination, NIcon, NRadioGroup, NRadio, NSlider, NModal } from 'naive-ui'
import { SearchOutline, FilterOutline, EyeOffOutline, SettingsOutline } from '@vicons/ionicons5' import { SearchOutline, FilterOutline, EyeOffOutline } from '@vicons/ionicons5'
import { api } from '../../api/client' import { api } from '../../api/client'
import { useToast } from '../../composables/useToast' import { useToast } from '../../composables/useToast'
import { useLiteraturePreview } from '../../composables/useLiteraturePreview' import { useLiteraturePreview } from '../../composables/useLiteraturePreview'
@@ -13,6 +13,7 @@ import { trackAction } from '../../composables/useAnalytics'
import PageSkeleton from '../../components/common/PageSkeleton.vue' import PageSkeleton from '../../components/common/PageSkeleton.vue'
import LiteratureCard from '../../components/literature/LiteratureCard.vue' import LiteratureCard from '../../components/literature/LiteratureCard.vue'
import LiteraturePreviewDrawer from '../../components/literature/LiteraturePreviewDrawer.vue' import LiteraturePreviewDrawer from '../../components/literature/LiteraturePreviewDrawer.vue'
import DisplaySettingsPopover from '../../components/literature/DisplaySettingsPopover.vue'
import type { LiteratureItem, TagOption, SearchRequestBody, SavedFilter } from '../../types' import type { LiteratureItem, TagOption, SearchRequestBody, SavedFilter } from '../../types'
const router = useRouter(); const route = useRoute() const router = useRouter(); const route = useRoute()
@@ -903,25 +904,7 @@ const specialTags = computed(() => filterOptions.value?.special_tags || {})
{label:'20条/页',value:20}, {label:'20条/页',value:20},
{label:'50条/页',value:50}, {label:'50条/页',value:50},
]" size="tiny" style="width:100px" /> ]" size="tiny" style="width:100px" />
<NPopover trigger="click" placement="bottom-end" :width="200"> <DisplaySettingsPopover />
<template #trigger>
<NButton size="tiny" quaternary title="显示设置"><template #icon><NIcon size="14"><SettingsOutline /></NIcon></template></NButton>
</template>
<div style="font-size:13px;font-weight:600;margin-bottom:8px">显示设置</div>
<div style="display:flex;flex-direction:column;gap:4px;font-size:13px">
<NCheckbox v-model:checked="displaySettings.showAuthors">作者</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showAffiliation">机构</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showJournal">期刊+分区</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showPmid">PMID</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showDoi">DOI</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showStudyTypes">研究类型+OA+NCT</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showTags">MeSH 标签</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showCitedBy">被引次数</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showAiSummary">AI 摘要</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showBadges">徽章</NCheckbox>
<NCheckbox v-model:checked="displaySettings.showActions">操作按钮</NCheckbox>
</div>
</NPopover>
</div> </div>
</div> </div>
+9
View File
@@ -8,13 +8,16 @@ import { useAuthStore } from '../../stores/auth'
import { useToast } from '../../composables/useToast' import { useToast } from '../../composables/useToast'
import { trackAction } from '../../composables/useAnalytics' import { trackAction } from '../../composables/useAnalytics'
import { useLiteraturePreview } from '../../composables/useLiteraturePreview' import { useLiteraturePreview } from '../../composables/useLiteraturePreview'
import { useDisplaySettings } from '../../composables/useDisplaySettings'
import PageSkeleton from '../../components/common/PageSkeleton.vue' import PageSkeleton from '../../components/common/PageSkeleton.vue'
import DisplaySettingsPopover from '../../components/literature/DisplaySettingsPopover.vue'
import type { LiteratureItem, TagOption } from '../../types' import type { LiteratureItem, TagOption } from '../../types'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const auth = useAuthStore() const auth = useAuthStore()
const toast = useToast() const toast = useToast()
const displaySettings = useDisplaySettings()
// ── 搜索状态(统一数据源) ── // ── 搜索状态(统一数据源) ──
const searchParams = ref({ const searchParams = ref({
@@ -502,6 +505,10 @@ onBeforeUnmount(() => {
<!-- 文献列表 --> <!-- 文献列表 -->
<section class="section section-list"> <section class="section section-list">
<div class="list-header">
<span>最新文献</span>
<DisplaySettingsPopover />
</div>
<PageSkeleton :loading="loading && !feedItems.length"> <PageSkeleton :loading="loading && !feedItems.length">
<NEmpty v-if="!loading && !feedItems.length" description="暂无文献" /> <NEmpty v-if="!loading && !feedItems.length" description="暂无文献" />
<div v-if="!loading && !feedItems.length && searched && searchParams.query" class="search-result-count" style="text-align:center;margin-top:8px"> 0 条结果</div> <div v-if="!loading && !feedItems.length && searched && searchParams.query" class="search-result-count" style="text-align:center;margin-top:8px"> 0 条结果</div>
@@ -518,6 +525,7 @@ onBeforeUnmount(() => {
:showSave="auth.isAuthenticated" :showSave="auth.isAuthenticated"
:savedPmids="savedPmids" :savedPmids="savedPmids"
:searchQuery="searchParams.query" :searchQuery="searchParams.query"
:displaySettings="displaySettings"
@detail="goDetail" @detail="goDetail"
@preview="openPreview" @preview="openPreview"
@save="saveLiterature" @save="saveLiterature"
@@ -620,6 +628,7 @@ onBeforeUnmount(() => {
.home { max-width: 1100px; margin: 0 auto; padding: 0 24px; color: var(--text-primary); } .home { max-width: 1100px; margin: 0 auto; padding: 0 24px; color: var(--text-primary); }
.section { padding: 3px 0; } .section { padding: 3px 0; }
.section-list { padding-bottom: 16px; } .section-list { padding-bottom: 16px; }
.list-header { display: flex; align-items: center; justify-content: space-between; font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px; }
.section-cta { padding: 12px 0 24px; } .section-cta { padding: 12px 0 24px; }
/* 内容网格:文献列表 + 热榜侧栏 */ /* 内容网格:文献列表 + 热榜侧栏 */