mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 07:28:37 +08:00
refactor: standardize floating action buttons with a compact stack layout and migrate menu items to key-based i18n resolution
This commit is contained in:
@@ -322,15 +322,15 @@ function stopDrag() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Teleport to="body" v-if="!appMode && showFloatingNewFolderAction">
|
<Teleport to="body" v-if="!appMode && showFloatingNewFolderAction">
|
||||||
<VFab
|
<div class="compact-fab-stack">
|
||||||
icon="mdi-folder-plus-outline"
|
<VFab
|
||||||
location="bottom"
|
icon="mdi-folder-plus-outline"
|
||||||
size="x-large"
|
color="primary"
|
||||||
fixed
|
appear
|
||||||
app
|
class="compact-fab compact-fab--primary"
|
||||||
appear
|
@click="openNewFolderDialog"
|
||||||
@click="openNewFolderDialog"
|
/>
|
||||||
/>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ declare global {
|
|||||||
|
|
||||||
type MaybeRefValue<T> = T | Ref<T> | ComputedRef<T>
|
type MaybeRefValue<T> = T | Ref<T> | ComputedRef<T>
|
||||||
|
|
||||||
interface DynamicButtonMenuItem {
|
export interface DynamicButtonMenuItem {
|
||||||
title: string
|
title?: string
|
||||||
|
titleKey?: string
|
||||||
|
titleParams?: Record<string, unknown>
|
||||||
icon?: string
|
icon?: string
|
||||||
color?: string
|
color?: string
|
||||||
action: () => void
|
action: () => void
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { filterMenusByPermission } from '@/utils/permission'
|
import { filterMenusByPermission } from '@/utils/permission'
|
||||||
import { usePWA } from '@/composables/usePWA'
|
import { usePWA } from '@/composables/usePWA'
|
||||||
|
import type { DynamicButtonMenuItem } from '@/composables/useDynamicButton'
|
||||||
|
|
||||||
// 是否显示的输入参数
|
// 是否显示的输入参数
|
||||||
defineProps({
|
defineProps({
|
||||||
@@ -120,12 +121,7 @@ interface DynamicButton {
|
|||||||
action: () => void
|
action: () => void
|
||||||
show: boolean
|
show: boolean
|
||||||
routePath?: string // 添加路径属性,用于标识哪个路由注册的
|
routePath?: string // 添加路径属性,用于标识哪个路由注册的
|
||||||
menuItems?: {
|
menuItems?: DynamicButtonMenuItem[]
|
||||||
title: string
|
|
||||||
icon?: string
|
|
||||||
color?: string
|
|
||||||
action: () => void
|
|
||||||
}[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提供动态按钮注册和获取的方法
|
// 提供动态按钮注册和获取的方法
|
||||||
@@ -176,6 +172,28 @@ const showDynamicButton = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const hasDynamicButtonMenu = computed(() => Boolean(dynamicButton.value?.menuItems?.length))
|
const hasDynamicButtonMenu = computed(() => Boolean(dynamicButton.value?.menuItems?.length))
|
||||||
|
|
||||||
|
const legacyDynamicMenuTitleKeyMap: Record<string, string> = {
|
||||||
|
'components.subscribeHistory.title': 'dialog.subscribeHistory.title',
|
||||||
|
'components.subscribeEdit.titleDefault': 'dialog.subscribeEdit.titleDefault',
|
||||||
|
'components.transferQueue.title': 'dialog.transferQueue.title',
|
||||||
|
'components.pluginMarketSetting.title': 'dialog.pluginMarketSetting.title',
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDynamicMenuItemTitle(item: DynamicButtonMenuItem) {
|
||||||
|
if (item.titleKey) {
|
||||||
|
return t(item.titleKey, item.titleParams as any)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.title) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedTitleKey = legacyDynamicMenuTitleKeyMap[item.title] || item.title
|
||||||
|
const looksLikeI18nKey = /^[a-z0-9_-]+(?:\.[a-z0-9_-]+)+$/i.test(normalizedTitleKey)
|
||||||
|
|
||||||
|
return looksLikeI18nKey ? t(normalizedTitleKey, item.titleParams as any) : item.title
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -253,14 +271,14 @@ const hasDynamicButtonMenu = computed(() => Boolean(dynamicButton.value?.menuIte
|
|||||||
<VList>
|
<VList>
|
||||||
<VListItem
|
<VListItem
|
||||||
v-for="(item, index) in dynamicButton?.menuItems"
|
v-for="(item, index) in dynamicButton?.menuItems"
|
||||||
:key="index"
|
:key="item.titleKey || item.title || index"
|
||||||
:base-color="item.color"
|
:base-color="item.color"
|
||||||
@click="item.action()"
|
@click="item.action()"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VIcon v-if="item.icon" :icon="item.icon" />
|
<VIcon v-if="item.icon" :icon="item.icon" />
|
||||||
</template>
|
</template>
|
||||||
<VListItemTitle>{{ item.title }}</VListItemTitle>
|
<VListItemTitle>{{ resolveDynamicMenuItemTitle(item) }}</VListItemTitle>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
</VMenu>
|
</VMenu>
|
||||||
|
|||||||
+9
-10
@@ -376,16 +376,15 @@ onDeactivated(() => {
|
|||||||
|
|
||||||
<!-- 底部操作按钮(只在非移动设备上显示) -->
|
<!-- 底部操作按钮(只在非移动设备上显示) -->
|
||||||
<Teleport to="body" v-if="route.path === '/dashboard'">
|
<Teleport to="body" v-if="route.path === '/dashboard'">
|
||||||
<VFab
|
<div v-if="!appMode" class="compact-fab-stack">
|
||||||
v-if="!appMode"
|
<VFab
|
||||||
icon="mdi-view-dashboard-edit"
|
icon="mdi-view-dashboard-edit"
|
||||||
location="bottom"
|
color="primary"
|
||||||
size="x-large"
|
appear
|
||||||
fixed
|
class="compact-fab compact-fab--primary"
|
||||||
app
|
@click="dialog = true"
|
||||||
appear
|
/>
|
||||||
@click="dialog = true"
|
</div>
|
||||||
/>
|
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<!-- 弹窗,根据配置生成选项 -->
|
<!-- 弹窗,根据配置生成选项 -->
|
||||||
|
|||||||
+16
-21
@@ -14,7 +14,7 @@ import { useUserStore } from '@/stores'
|
|||||||
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
|
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t, locale } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@@ -141,23 +141,27 @@ onUnmounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const subscribeDynamicMenuItems = computed(() => {
|
const subscribeDynamicMenuItems = computed(() => {
|
||||||
locale.value
|
|
||||||
|
|
||||||
if (!appMode.value) return undefined
|
if (!appMode.value) return undefined
|
||||||
|
|
||||||
if (activeTab.value === 'mysub') {
|
if (activeTab.value === 'mysub') {
|
||||||
const items: Array<{ title: string; icon: string; action: () => void }> = []
|
const items: Array<{
|
||||||
|
titleKey: string
|
||||||
|
titleParams?: Record<string, unknown>
|
||||||
|
icon: string
|
||||||
|
action: () => void
|
||||||
|
}> = []
|
||||||
|
|
||||||
if (showSubscribeHistoryAction.value) {
|
if (showSubscribeHistoryAction.value) {
|
||||||
items.push({
|
items.push({
|
||||||
title: t('components.subscribeHistory.title', { type: subType }),
|
titleKey: 'dialog.subscribeHistory.title',
|
||||||
|
titleParams: { type: subType },
|
||||||
icon: 'mdi-history',
|
icon: 'mdi-history',
|
||||||
action: openSubscribeHistoryDialog,
|
action: openSubscribeHistoryDialog,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
title: t('components.subscribeEdit.titleDefault'),
|
titleKey: 'dialog.subscribeEdit.titleDefault',
|
||||||
icon: 'mdi-clipboard-edit-outline',
|
icon: 'mdi-clipboard-edit-outline',
|
||||||
action: openDefaultRuleDialog,
|
action: openDefaultRuleDialog,
|
||||||
})
|
})
|
||||||
@@ -353,39 +357,30 @@ onMounted(() => {
|
|||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<Teleport to="body" v-if="!appMode && route.path.startsWith(`/subscribe/${subType === '电影' ? 'movie' : 'tv'}`)">
|
<Teleport to="body" v-if="!appMode && route.path.startsWith(`/subscribe/${subType === '电影' ? 'movie' : 'tv'}`)">
|
||||||
<div>
|
<div class="compact-fab-stack">
|
||||||
<VFab
|
<VFab
|
||||||
v-if="showSubscribeHistoryAction"
|
v-if="showSubscribeHistoryAction"
|
||||||
icon="mdi-history"
|
icon="mdi-history"
|
||||||
color="info"
|
color="info"
|
||||||
location="bottom"
|
variant="tonal"
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
appear
|
||||||
|
class="compact-fab compact-fab--secondary"
|
||||||
@click="openSubscribeHistoryDialog"
|
@click="openSubscribeHistoryDialog"
|
||||||
/>
|
/>
|
||||||
<VFab
|
<VFab
|
||||||
v-if="showDefaultRuleAction"
|
v-if="showDefaultRuleAction"
|
||||||
icon="mdi-clipboard-edit-outline"
|
icon="mdi-clipboard-edit-outline"
|
||||||
color="primary"
|
color="primary"
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
appear
|
||||||
:class="{ 'mb-16': showSubscribeHistoryAction }"
|
class="compact-fab compact-fab--primary"
|
||||||
@click="openDefaultRuleDialog"
|
@click="openDefaultRuleDialog"
|
||||||
/>
|
/>
|
||||||
<VFab
|
<VFab
|
||||||
v-if="showShareStatisticsAction"
|
v-if="showShareStatisticsAction"
|
||||||
icon="mdi-chart-line"
|
icon="mdi-chart-line"
|
||||||
color="info"
|
color="primary"
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
appear
|
||||||
|
class="compact-fab compact-fab--primary"
|
||||||
@click="openShareStatisticsDialog"
|
@click="openShareStatisticsDialog"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -136,15 +136,15 @@ onMounted(() => {
|
|||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<Teleport to="body" v-if="!appMode && route.path === '/workflow' && activeTab === 'list'">
|
<Teleport to="body" v-if="!appMode && route.path === '/workflow' && activeTab === 'list'">
|
||||||
<VFab
|
<div class="compact-fab-stack">
|
||||||
icon="mdi-plus"
|
<VFab
|
||||||
location="bottom"
|
icon="mdi-plus"
|
||||||
size="x-large"
|
color="primary"
|
||||||
fixed
|
appear
|
||||||
app
|
class="compact-fab compact-fab--primary"
|
||||||
appear
|
@click="openAddWorkflowDialog"
|
||||||
@click="openAddWorkflowDialog"
|
/>
|
||||||
/>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+138
-1
@@ -238,6 +238,122 @@ html.v-overlay-scroll-blocked body {
|
|||||||
opacity:0.75;
|
opacity:0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 紧凑型悬浮操作按钮
|
||||||
|
.compact-fab-stack {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 0.75rem;
|
||||||
|
inset-block-end: max(1rem, calc(env(safe-area-inset-bottom) + 1rem));
|
||||||
|
inset-inline-end: max(1rem, calc(env(safe-area-inset-right) + 1rem));
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab-stack > * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab-stack--history {
|
||||||
|
inset-block-end: max(4.5rem, calc(env(safe-area-inset-bottom) + 4.5rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab.v-fab {
|
||||||
|
display: inline-flex;
|
||||||
|
overflow: visible;
|
||||||
|
flex: none;
|
||||||
|
min-inline-size: 0 !important;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab .v-fab__container {
|
||||||
|
position: static;
|
||||||
|
display: inline-flex;
|
||||||
|
overflow: visible;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab .v-btn {
|
||||||
|
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
box-shadow:
|
||||||
|
0 16px 34px rgb(15 23 42 / 16%),
|
||||||
|
0 6px 16px rgb(15 23 42 / 10%);
|
||||||
|
opacity: 0.98;
|
||||||
|
transition:
|
||||||
|
transform 0.18s ease,
|
||||||
|
box-shadow 0.18s ease,
|
||||||
|
filter 0.18s ease,
|
||||||
|
opacity 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--primary .v-btn {
|
||||||
|
block-size: 3.5rem !important;
|
||||||
|
box-shadow:
|
||||||
|
0 20px 40px rgb(15 23 42 / 20%),
|
||||||
|
0 8px 18px rgb(15 23 42 / 12%);
|
||||||
|
inline-size: 3.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--secondary .v-btn {
|
||||||
|
block-size: 3rem !important;
|
||||||
|
inline-size: 3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--primary .v-icon {
|
||||||
|
font-size: 1.75rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--secondary .v-icon {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.compact-fab .v-btn:hover {
|
||||||
|
box-shadow:
|
||||||
|
0 22px 42px rgb(15 23 42 / 22%),
|
||||||
|
0 8px 18px rgb(15 23 42 / 12%);
|
||||||
|
filter: saturate(1.03);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--primary .v-btn:hover {
|
||||||
|
box-shadow:
|
||||||
|
0 26px 46px rgb(15 23 42 / 24%),
|
||||||
|
0 10px 22px rgb(15 23 42 / 14%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab .v-btn:active {
|
||||||
|
box-shadow:
|
||||||
|
0 10px 22px rgb(15 23 42 / 16%),
|
||||||
|
0 3px 8px rgb(15 23 42 / 10%);
|
||||||
|
transform: translateY(0) scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
.compact-fab-stack {
|
||||||
|
gap: 0.625rem;
|
||||||
|
inset-block-end: max(0.875rem, calc(env(safe-area-inset-bottom) + 0.875rem));
|
||||||
|
inset-inline-end: max(0.875rem, calc(env(safe-area-inset-right) + 0.875rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab-stack--history {
|
||||||
|
inset-block-end: max(4rem, calc(env(safe-area-inset-bottom) + 4rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--primary .v-btn {
|
||||||
|
block-size: 3.5rem !important;
|
||||||
|
inline-size: 3.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-fab--secondary .v-btn {
|
||||||
|
block-size: 3rem !important;
|
||||||
|
inline-size: 3rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.apexcharts-title-text {
|
.apexcharts-title-text {
|
||||||
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity)) !important;
|
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity)) !important;
|
||||||
}
|
}
|
||||||
@@ -311,7 +427,28 @@ html.v-overlay-scroll-blocked body {
|
|||||||
|
|
||||||
.settings-icon-button {
|
.settings-icon-button {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
min-inline-size: auto;
|
border-radius: 0.95rem;
|
||||||
|
block-size: 2.75rem;
|
||||||
|
inline-size: 2.75rem;
|
||||||
|
margin-inline-start: 0.25rem;
|
||||||
|
min-inline-size: 2.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-icon-button .v-icon {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
.settings-icon-button {
|
||||||
|
border-radius: 0.825rem;
|
||||||
|
block-size: 2.5rem;
|
||||||
|
inline-size: 2.5rem;
|
||||||
|
min-inline-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-icon-button .v-icon {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-infinite-scroll__side {
|
.v-infinite-scroll__side {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { usePWA } from '@/composables/usePWA'
|
|||||||
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
|
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t, locale } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -908,14 +908,12 @@ const showNewFolderAction = computed(() => activeTab.value === 'installed' && !c
|
|||||||
const showMarketSettingAction = computed(() => activeTab.value === 'market')
|
const showMarketSettingAction = computed(() => activeTab.value === 'market')
|
||||||
|
|
||||||
const pluginDynamicMenuItems = computed(() => {
|
const pluginDynamicMenuItems = computed(() => {
|
||||||
locale.value
|
|
||||||
|
|
||||||
if (!appMode.value) return undefined
|
if (!appMode.value) return undefined
|
||||||
if (!showSearchAction.value) return undefined
|
if (!showSearchAction.value) return undefined
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
title: t('plugin.searchPlugins'),
|
titleKey: 'plugin.searchPlugins',
|
||||||
icon: 'mdi-magnify',
|
icon: 'mdi-magnify',
|
||||||
action: openPluginSearchDialog,
|
action: openPluginSearchDialog,
|
||||||
},
|
},
|
||||||
@@ -923,7 +921,7 @@ const pluginDynamicMenuItems = computed(() => {
|
|||||||
|
|
||||||
if (showNewFolderAction.value) {
|
if (showNewFolderAction.value) {
|
||||||
items.push({
|
items.push({
|
||||||
title: t('plugin.newFolder'),
|
titleKey: 'plugin.newFolder',
|
||||||
icon: 'mdi-folder-plus',
|
icon: 'mdi-folder-plus',
|
||||||
action: showNewFolderDialog,
|
action: showNewFolderDialog,
|
||||||
})
|
})
|
||||||
@@ -931,7 +929,7 @@ const pluginDynamicMenuItems = computed(() => {
|
|||||||
|
|
||||||
if (showMarketSettingAction.value) {
|
if (showMarketSettingAction.value) {
|
||||||
items.push({
|
items.push({
|
||||||
title: t('components.pluginMarketSetting.title'),
|
titleKey: 'dialog.pluginMarketSetting.title',
|
||||||
icon: 'mdi-store-cog',
|
icon: 'mdi-store-cog',
|
||||||
action: openMarketSettingDialog,
|
action: openMarketSettingDialog,
|
||||||
})
|
})
|
||||||
@@ -1609,41 +1607,32 @@ function onDragStartPlugin(evt: any) {
|
|||||||
|
|
||||||
<!-- 插件搜索图标 -->
|
<!-- 插件搜索图标 -->
|
||||||
<Teleport to="body" v-if="route.path === '/plugins'">
|
<Teleport to="body" v-if="route.path === '/plugins'">
|
||||||
<div v-if="isRefreshed && !appMode && showSearchAction">
|
<div v-if="isRefreshed && !appMode && showSearchAction" class="compact-fab-stack">
|
||||||
<VFab
|
|
||||||
icon="mdi-magnify"
|
|
||||||
color="info"
|
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
|
||||||
@click="openPluginSearchDialog"
|
|
||||||
/>
|
|
||||||
<VFab
|
|
||||||
v-if="showNewFolderAction"
|
|
||||||
icon="mdi-folder-plus"
|
|
||||||
color="primary"
|
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
|
||||||
class="mb-16"
|
|
||||||
@click="showNewFolderDialog"
|
|
||||||
/>
|
|
||||||
<VFab
|
<VFab
|
||||||
v-if="showMarketSettingAction"
|
v-if="showMarketSettingAction"
|
||||||
icon="mdi-store-cog"
|
icon="mdi-store-cog"
|
||||||
color="warning"
|
color="warning"
|
||||||
location="bottom"
|
variant="tonal"
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
appear
|
||||||
class="mb-16"
|
class="compact-fab compact-fab--secondary"
|
||||||
@click="openMarketSettingDialog"
|
@click="openMarketSettingDialog"
|
||||||
/>
|
/>
|
||||||
|
<VFab
|
||||||
|
v-if="showNewFolderAction"
|
||||||
|
icon="mdi-folder-plus"
|
||||||
|
color="success"
|
||||||
|
variant="tonal"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--secondary"
|
||||||
|
@click="showNewFolderDialog"
|
||||||
|
/>
|
||||||
|
<VFab
|
||||||
|
icon="mdi-magnify"
|
||||||
|
color="primary"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--primary"
|
||||||
|
@click="openPluginSearchDialog"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<!-- 插件市场设置窗口 -->
|
<!-- 插件市场设置窗口 -->
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { useBackgroundOptimization } from '@/composables/useBackgroundOptimizati
|
|||||||
import { useGlobalSettingsStore } from '@/stores'
|
import { useGlobalSettingsStore } from '@/stores'
|
||||||
|
|
||||||
// i18n
|
// i18n
|
||||||
const { t, locale } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 全局设置
|
// 全局设置
|
||||||
const globalSettingsStore = useGlobalSettingsStore()
|
const globalSettingsStore = useGlobalSettingsStore()
|
||||||
@@ -643,27 +643,25 @@ const toggleGroupSelection = (checked: boolean | null, items: readonly any[]) =>
|
|||||||
|
|
||||||
const historyDynamicIcon = computed(() => (selected.value.length > 0 ? 'mdi-chevron-up' : 'mdi-timer-sand-paused'))
|
const historyDynamicIcon = computed(() => (selected.value.length > 0 ? 'mdi-chevron-up' : 'mdi-timer-sand-paused'))
|
||||||
const historyDynamicMenuItems = computed(() => {
|
const historyDynamicMenuItems = computed(() => {
|
||||||
locale.value
|
|
||||||
|
|
||||||
if (selected.value.length === 0) return undefined
|
if (selected.value.length === 0) return undefined
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: t('components.transferQueue.title'),
|
titleKey: 'dialog.transferQueue.title',
|
||||||
icon: 'mdi-timer-sand-paused',
|
icon: 'mdi-timer-sand-paused',
|
||||||
action: () => {
|
action: () => {
|
||||||
transferQueueDialog.value = true
|
transferQueueDialog.value = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('transferHistory.actions.batchRedo'),
|
titleKey: 'transferHistory.actions.batchRedo',
|
||||||
icon: 'mdi-redo-variant',
|
icon: 'mdi-redo-variant',
|
||||||
action: () => {
|
action: () => {
|
||||||
retransferBatch()
|
retransferBatch()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('transferHistory.actions.batchDelete'),
|
titleKey: 'transferHistory.actions.batchDelete',
|
||||||
icon: 'mdi-trash-can-outline',
|
icon: 'mdi-trash-can-outline',
|
||||||
color: 'error',
|
color: 'error',
|
||||||
action: () => {
|
action: () => {
|
||||||
@@ -980,40 +978,31 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 非 app 模式下的 FAB 按钮 -->
|
<!-- 非 app 模式下的 FAB 按钮 -->
|
||||||
<Teleport to="body" v-if="!appMode && route.path === '/history'">
|
<Teleport to="body" v-if="!appMode && route.path === '/history'">
|
||||||
<div v-if="isRefreshed">
|
<div v-if="isRefreshed" class="compact-fab-stack compact-fab-stack--history">
|
||||||
<VFab
|
|
||||||
icon="mdi-timer-sand-paused"
|
|
||||||
color="info"
|
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
|
||||||
@click="transferQueueDialog = true"
|
|
||||||
/>
|
|
||||||
<VFab
|
|
||||||
v-if="selected.length > 0"
|
|
||||||
class="mb-16"
|
|
||||||
icon="mdi-redo-variant"
|
|
||||||
color="primary"
|
|
||||||
location="bottom"
|
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
|
||||||
@click="retransferBatch"
|
|
||||||
/>
|
|
||||||
<VFab
|
<VFab
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
icon="mdi-trash-can-outline"
|
icon="mdi-trash-can-outline"
|
||||||
color="error"
|
color="warning"
|
||||||
location="bottom"
|
variant="tonal"
|
||||||
size="x-large"
|
|
||||||
fixed
|
|
||||||
app
|
|
||||||
appear
|
appear
|
||||||
|
class="compact-fab compact-fab--secondary"
|
||||||
@click="removeHistoryBatch"
|
@click="removeHistoryBatch"
|
||||||
class="mb-32"
|
/>
|
||||||
|
<VFab
|
||||||
|
v-if="selected.length > 0"
|
||||||
|
icon="mdi-redo-variant"
|
||||||
|
color="success"
|
||||||
|
variant="tonal"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--secondary"
|
||||||
|
@click="retransferBatch"
|
||||||
|
/>
|
||||||
|
<VFab
|
||||||
|
icon="mdi-timer-sand-paused"
|
||||||
|
color="primary"
|
||||||
|
appear
|
||||||
|
class="compact-fab compact-fab--primary"
|
||||||
|
@click="transferQueueDialog = true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|||||||
@@ -393,17 +393,15 @@ useDynamicButton({
|
|||||||
/>
|
/>
|
||||||
<!-- 新增站点按钮 -->
|
<!-- 新增站点按钮 -->
|
||||||
<Teleport to="body" v-if="route.path === '/site'">
|
<Teleport to="body" v-if="route.path === '/site'">
|
||||||
<VFab
|
<div v-if="isRefreshed && !appMode" class="compact-fab-stack">
|
||||||
v-if="isRefreshed && !appMode"
|
<VFab
|
||||||
icon="mdi-web-plus"
|
icon="mdi-web-plus"
|
||||||
location="bottom"
|
color="primary"
|
||||||
size="x-large"
|
appear
|
||||||
fixed
|
class="compact-fab compact-fab--primary"
|
||||||
app
|
@click="siteAddDialog = true"
|
||||||
appear
|
/>
|
||||||
@click="siteAddDialog = true"
|
</div>
|
||||||
:class="{ 'mb-12': appMode }"
|
|
||||||
/>
|
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<!-- 新增站点弹窗 -->
|
<!-- 新增站点弹窗 -->
|
||||||
<SiteAddEditDialog
|
<SiteAddEditDialog
|
||||||
|
|||||||
@@ -99,17 +99,15 @@ useDynamicButton({
|
|||||||
|
|
||||||
<!-- 新增用户按钮 -->
|
<!-- 新增用户按钮 -->
|
||||||
<Teleport to="body" v-if="route.path === '/user'">
|
<Teleport to="body" v-if="route.path === '/user'">
|
||||||
<VFab
|
<div v-if="isRefreshed && !appMode" class="compact-fab-stack">
|
||||||
v-if="isRefreshed && !appMode"
|
<VFab
|
||||||
icon="mdi-account-plus"
|
icon="mdi-account-plus"
|
||||||
location="bottom"
|
color="primary"
|
||||||
size="x-large"
|
appear
|
||||||
fixed
|
class="compact-fab compact-fab--primary"
|
||||||
app
|
@click="openAddUserDialog"
|
||||||
appear
|
/>
|
||||||
@click="openAddUserDialog"
|
</div>
|
||||||
:class="{ 'mb-12': appMode }"
|
|
||||||
/>
|
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
<!-- 用户添加弹窗 -->
|
<!-- 用户添加弹窗 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user