mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
refactor: migrate page-specific action buttons to dynamic FABs for PWA mode compatibility
This commit is contained in:
@@ -1031,7 +1031,7 @@ export default {
|
|||||||
doubanGlobalTVRankings: 'Douban Global TV Rankings',
|
doubanGlobalTVRankings: 'Douban Global TV Rankings',
|
||||||
noCategoryContent: 'No content to display in current category',
|
noCategoryContent: 'No content to display in current category',
|
||||||
configureContent: 'Configure Display Content',
|
configureContent: 'Configure Display Content',
|
||||||
customizeContent: 'Customize Content',
|
customizeContent: 'Customize Recommendations',
|
||||||
selectContentToDisplay: 'Select content you want to display on the page',
|
selectContentToDisplay: 'Select content you want to display on the page',
|
||||||
selectAll: 'Select All',
|
selectAll: 'Select All',
|
||||||
selectNone: 'Select None',
|
selectNone: 'Select None',
|
||||||
@@ -2959,7 +2959,7 @@ export default {
|
|||||||
},
|
},
|
||||||
transferHistory: {
|
transferHistory: {
|
||||||
title: 'Transfer History',
|
title: 'Transfer History',
|
||||||
searchPlaceholder: 'Search transfer records',
|
searchPlaceholder: 'Search (supports * ? wildcards)',
|
||||||
titleColumn: 'Title',
|
titleColumn: 'Title',
|
||||||
pathColumn: 'Path',
|
pathColumn: 'Path',
|
||||||
modeColumn: 'Mode',
|
modeColumn: 'Mode',
|
||||||
|
|||||||
@@ -1025,7 +1025,7 @@ export default {
|
|||||||
doubanGlobalTVRankings: '豆瓣全球剧集榜',
|
doubanGlobalTVRankings: '豆瓣全球剧集榜',
|
||||||
noCategoryContent: '当前分类下没有可显示的内容',
|
noCategoryContent: '当前分类下没有可显示的内容',
|
||||||
configureContent: '设置显示内容',
|
configureContent: '设置显示内容',
|
||||||
customizeContent: '自定义内容',
|
customizeContent: '自定义推荐',
|
||||||
selectContentToDisplay: '选择您想在页面显示的内容',
|
selectContentToDisplay: '选择您想在页面显示的内容',
|
||||||
selectAll: '全选',
|
selectAll: '全选',
|
||||||
selectNone: '全不选',
|
selectNone: '全不选',
|
||||||
@@ -2904,7 +2904,7 @@ export default {
|
|||||||
},
|
},
|
||||||
transferHistory: {
|
transferHistory: {
|
||||||
title: '转移历史',
|
title: '转移历史',
|
||||||
searchPlaceholder: '搜索转移记录',
|
searchPlaceholder: '搜索(支持 * ? 通配符)',
|
||||||
titleColumn: '标题',
|
titleColumn: '标题',
|
||||||
pathColumn: '路径',
|
pathColumn: '路径',
|
||||||
modeColumn: '转移方式',
|
modeColumn: '转移方式',
|
||||||
|
|||||||
@@ -1026,7 +1026,7 @@ export default {
|
|||||||
doubanGlobalTVRankings: '豆瓣全球劇集榜',
|
doubanGlobalTVRankings: '豆瓣全球劇集榜',
|
||||||
noCategoryContent: '當前分類下沒有可顯示的內容',
|
noCategoryContent: '當前分類下沒有可顯示的內容',
|
||||||
configureContent: '設置顯示內容',
|
configureContent: '設置顯示內容',
|
||||||
customizeContent: '自定義內容',
|
customizeContent: '自定義推薦',
|
||||||
selectContentToDisplay: '選擇您想在頁面顯示的內容',
|
selectContentToDisplay: '選擇您想在頁面顯示的內容',
|
||||||
selectAll: '全選',
|
selectAll: '全選',
|
||||||
selectNone: '全不選',
|
selectNone: '全不選',
|
||||||
@@ -2906,7 +2906,7 @@ export default {
|
|||||||
},
|
},
|
||||||
transferHistory: {
|
transferHistory: {
|
||||||
title: '轉移歷史',
|
title: '轉移歷史',
|
||||||
searchPlaceholder: '搜索轉移記錄',
|
searchPlaceholder: '搜索(支援 * ? 萬用字元)',
|
||||||
titleColumn: '標題',
|
titleColumn: '標題',
|
||||||
pathColumn: '路徑',
|
pathColumn: '路徑',
|
||||||
modeColumn: '轉移方式',
|
modeColumn: '轉移方式',
|
||||||
|
|||||||
+26
-12
@@ -5,9 +5,12 @@ import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
|
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
|
||||||
|
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||||
|
import { usePWA } from '@/composables/usePWA'
|
||||||
import { getItemColor, initializeItemColors } from '@/utils/colorUtils'
|
import { getItemColor, initializeItemColors } from '@/utils/colorUtils'
|
||||||
|
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
|
const { appMode } = usePWA()
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -21,6 +24,10 @@ const currentCategory = ref(t('recommend.all'))
|
|||||||
// 使用动态标签页
|
// 使用动态标签页
|
||||||
const { registerHeaderTab } = useDynamicHeaderTab()
|
const { registerHeaderTab } = useDynamicHeaderTab()
|
||||||
|
|
||||||
|
function openRecommendSettings() {
|
||||||
|
dialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
const viewList = reactive<{ apipath: string; linkurl: string; title: string; type: string }[]>([
|
const viewList = reactive<{ apipath: string; linkurl: string; title: string; type: string }[]>([
|
||||||
{
|
{
|
||||||
apipath: 'recommend/tmdb_trending',
|
apipath: 'recommend/tmdb_trending',
|
||||||
@@ -218,17 +225,12 @@ const categoryItems = computed(() => [
|
|||||||
registerHeaderTab({
|
registerHeaderTab({
|
||||||
items: categoryItems,
|
items: categoryItems,
|
||||||
modelValue: currentCategory,
|
modelValue: currentCategory,
|
||||||
appendButtons: [
|
})
|
||||||
{
|
|
||||||
icon: 'mdi-tune',
|
useDynamicButton({
|
||||||
variant: 'text',
|
icon: 'mdi-tune',
|
||||||
color: 'grey',
|
onClick: openRecommendSettings,
|
||||||
class: 'settings-icon-button',
|
show: computed(() => appMode.value),
|
||||||
action: () => {
|
|
||||||
dialog.value = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面是否准备就绪
|
// 页面是否准备就绪
|
||||||
@@ -346,7 +348,19 @@ onActivated(async () => {
|
|||||||
|
|
||||||
<!-- 快速滚动到顶部按钮 -->
|
<!-- 快速滚动到顶部按钮 -->
|
||||||
<Teleport to="body" v-if="route.path === '/recommend'">
|
<Teleport to="body" v-if="route.path === '/recommend'">
|
||||||
<VScrollToTopBtn />
|
<div v-if="!appMode" class="compact-fab-stack">
|
||||||
|
<VFab
|
||||||
|
icon="mdi-tune"
|
||||||
|
color="primary"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--primary"
|
||||||
|
@click="openRecommendSettings"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
|
<Teleport to="body" v-if="route.path === '/recommend'">
|
||||||
|
<VScrollToTopBtn :offset-fab="!appMode" />
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+31
-89
@@ -11,11 +11,15 @@ import TorrentFilterBar from '@/components/filter/TorrentFilterBar.vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useGlobalSettingsStore } from '@/stores/global'
|
import { useGlobalSettingsStore } from '@/stores/global'
|
||||||
import { useTorrentFilter, type FilterState } from '@/composables/useTorrentFilter'
|
import { useTorrentFilter, type FilterState } from '@/composables/useTorrentFilter'
|
||||||
|
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||||
|
import { usePWA } from '@/composables/usePWA'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { appMode } = usePWA()
|
||||||
|
|
||||||
// 提示框
|
// 提示框
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
@@ -225,6 +229,19 @@ const filteredCardDataList = ref<Array<SearchTorrent>>([])
|
|||||||
// 是否刷新过
|
// 是否刷新过
|
||||||
const isRefreshed = ref(false)
|
const isRefreshed = ref(false)
|
||||||
|
|
||||||
|
const viewToggleIcon = computed(() => (viewType.value === 'card' ? 'mdi-view-list-outline' : 'mdi-view-grid-outline'))
|
||||||
|
|
||||||
|
// 搜索结果视图切换收纳到页面动态按钮中,和仪表盘的设置按钮保持一致。
|
||||||
|
function toggleViewType() {
|
||||||
|
changeViewType(viewType.value === 'card' ? 'row' : 'card')
|
||||||
|
}
|
||||||
|
|
||||||
|
useDynamicButton({
|
||||||
|
icon: viewToggleIcon,
|
||||||
|
onClick: toggleViewType,
|
||||||
|
show: computed(() => appMode.value && isRefreshed.value),
|
||||||
|
})
|
||||||
|
|
||||||
// 是否正在重新搜索
|
// 是否正在重新搜索
|
||||||
const isRefreshing = ref(false)
|
const isRefreshing = ref(false)
|
||||||
|
|
||||||
@@ -1211,18 +1228,6 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 重新设计的视图切换按钮 -->
|
|
||||||
<div class="view-toggle-container">
|
|
||||||
<div class="view-toggle-buttons">
|
|
||||||
<div class="active-indicator" :class="viewType"></div>
|
|
||||||
<button class="view-toggle-btn" :class="{ active: viewType === 'card' }" @click="changeViewType('card')">
|
|
||||||
<VIcon icon="mdi-view-grid-outline" :color="viewType === 'card' ? 'primary' : undefined" />
|
|
||||||
</button>
|
|
||||||
<button class="view-toggle-btn" :class="{ active: viewType === 'row' }" @click="changeViewType('row')">
|
|
||||||
<VIcon icon="mdi-view-list-outline" :color="viewType === 'row' ? 'primary' : undefined" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|
||||||
<!-- 搜索结果 -->
|
<!-- 搜索结果 -->
|
||||||
@@ -1328,9 +1333,22 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 初始加载状态 -->
|
<!-- 初始加载状态 -->
|
||||||
<LoadingBanner v-else-if="!isRefreshed && !isSearchLoading" />
|
<LoadingBanner v-else-if="!isRefreshed && !isSearchLoading" />
|
||||||
|
|
||||||
|
<Teleport to="body" v-if="route.path === '/resource'">
|
||||||
|
<div v-if="isRefreshed && !appMode" class="compact-fab-stack">
|
||||||
|
<VFab
|
||||||
|
:icon="viewToggleIcon"
|
||||||
|
color="primary"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--primary"
|
||||||
|
@click="toggleViewType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
<!-- 滚动到顶部按钮 -->
|
<!-- 滚动到顶部按钮 -->
|
||||||
<Teleport to="body" v-if="route.path === '/resource'">
|
<Teleport to="body" v-if="route.path === '/resource'">
|
||||||
<VScrollToTopBtn />
|
<VScrollToTopBtn :offset-fab="isRefreshed && !appMode" />
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -1465,58 +1483,6 @@ onUnmounted(() => {
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 重新设计的视图切换按钮 */
|
|
||||||
.view-toggle-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-toggle-buttons {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: rgba(var(--v-theme-surface-variant), 0.1);
|
|
||||||
isolation: isolate; /* Create new stacking context */
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-indicator {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: rgb(var(--v-theme-surface));
|
|
||||||
block-size: 36px;
|
|
||||||
box-shadow:
|
|
||||||
0 1px 3px rgba(0, 0, 0, 12%),
|
|
||||||
0 1px 2px rgba(0, 0, 0, 24%);
|
|
||||||
inline-size: 40px;
|
|
||||||
inset-block-start: 4px;
|
|
||||||
inset-inline-start: 4px;
|
|
||||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-indicator.row {
|
|
||||||
transform: translateX(40px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-toggle-btn {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2; /* Sit on top of indicator */
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
block-size: 36px;
|
|
||||||
cursor: pointer;
|
|
||||||
inline-size: 40px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-toggle-btn:hover:not(.active) {
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: rgba(var(--v-theme-primary), 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 重新搜索按钮 */
|
/* 重新搜索按钮 */
|
||||||
.refresh-search-btn {
|
.refresh-search-btn {
|
||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
@@ -1690,30 +1656,6 @@ onUnmounted(() => {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-toggle-container {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-toggle-buttons {
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-indicator {
|
|
||||||
block-size: 32px;
|
|
||||||
inline-size: 36px;
|
|
||||||
inset-block-start: 2px;
|
|
||||||
inset-inline-start: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-indicator.row {
|
|
||||||
transform: translateX(36px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-toggle-btn {
|
|
||||||
block-size: 32px;
|
|
||||||
inline-size: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.refresh-search-btn {
|
.refresh-search-btn {
|
||||||
block-size: 36px !important;
|
block-size: 36px !important;
|
||||||
inline-size: 36px !important;
|
inline-size: 36px !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user