mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-11 10:00:08 +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>
|
||||
|
||||
<Teleport to="body" v-if="!appMode && showFloatingNewFolderAction">
|
||||
<VFab
|
||||
icon="mdi-folder-plus-outline"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
@click="openNewFolderDialog"
|
||||
/>
|
||||
<div class="compact-fab-stack">
|
||||
<VFab
|
||||
icon="mdi-folder-plus-outline"
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="openNewFolderDialog"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@ declare global {
|
||||
|
||||
type MaybeRefValue<T> = T | Ref<T> | ComputedRef<T>
|
||||
|
||||
interface DynamicButtonMenuItem {
|
||||
title: string
|
||||
export interface DynamicButtonMenuItem {
|
||||
title?: string
|
||||
titleKey?: string
|
||||
titleParams?: Record<string, unknown>
|
||||
icon?: string
|
||||
color?: string
|
||||
action: () => void
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { filterMenusByPermission } from '@/utils/permission'
|
||||
import { usePWA } from '@/composables/usePWA'
|
||||
import type { DynamicButtonMenuItem } from '@/composables/useDynamicButton'
|
||||
|
||||
// 是否显示的输入参数
|
||||
defineProps({
|
||||
@@ -120,12 +121,7 @@ interface DynamicButton {
|
||||
action: () => void
|
||||
show: boolean
|
||||
routePath?: string // 添加路径属性,用于标识哪个路由注册的
|
||||
menuItems?: {
|
||||
title: string
|
||||
icon?: string
|
||||
color?: string
|
||||
action: () => void
|
||||
}[]
|
||||
menuItems?: DynamicButtonMenuItem[]
|
||||
}
|
||||
|
||||
// 提供动态按钮注册和获取的方法
|
||||
@@ -176,6 +172,28 @@ const showDynamicButton = computed(() => {
|
||||
})
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -253,14 +271,14 @@ const hasDynamicButtonMenu = computed(() => Boolean(dynamicButton.value?.menuIte
|
||||
<VList>
|
||||
<VListItem
|
||||
v-for="(item, index) in dynamicButton?.menuItems"
|
||||
:key="index"
|
||||
:key="item.titleKey || item.title || index"
|
||||
:base-color="item.color"
|
||||
@click="item.action()"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon v-if="item.icon" :icon="item.icon" />
|
||||
</template>
|
||||
<VListItemTitle>{{ item.title }}</VListItemTitle>
|
||||
<VListItemTitle>{{ resolveDynamicMenuItemTitle(item) }}</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
|
||||
@@ -376,16 +376,15 @@ onDeactivated(() => {
|
||||
|
||||
<!-- 底部操作按钮(只在非移动设备上显示) -->
|
||||
<Teleport to="body" v-if="route.path === '/dashboard'">
|
||||
<VFab
|
||||
v-if="!appMode"
|
||||
icon="mdi-view-dashboard-edit"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
@click="dialog = true"
|
||||
/>
|
||||
<div v-if="!appMode" class="compact-fab-stack">
|
||||
<VFab
|
||||
icon="mdi-view-dashboard-edit"
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="dialog = true"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<!-- 弹窗,根据配置生成选项 -->
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useUserStore } from '@/stores'
|
||||
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
|
||||
|
||||
// 国际化
|
||||
const { t, locale } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
@@ -141,23 +141,27 @@ onUnmounted(() => {
|
||||
})
|
||||
|
||||
const subscribeDynamicMenuItems = computed(() => {
|
||||
locale.value
|
||||
|
||||
if (!appMode.value) return undefined
|
||||
|
||||
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) {
|
||||
items.push({
|
||||
title: t('components.subscribeHistory.title', { type: subType }),
|
||||
titleKey: 'dialog.subscribeHistory.title',
|
||||
titleParams: { type: subType },
|
||||
icon: 'mdi-history',
|
||||
action: openSubscribeHistoryDialog,
|
||||
})
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: t('components.subscribeEdit.titleDefault'),
|
||||
titleKey: 'dialog.subscribeEdit.titleDefault',
|
||||
icon: 'mdi-clipboard-edit-outline',
|
||||
action: openDefaultRuleDialog,
|
||||
})
|
||||
@@ -353,39 +357,30 @@ onMounted(() => {
|
||||
</Teleport>
|
||||
|
||||
<Teleport to="body" v-if="!appMode && route.path.startsWith(`/subscribe/${subType === '电影' ? 'movie' : 'tv'}`)">
|
||||
<div>
|
||||
<div class="compact-fab-stack">
|
||||
<VFab
|
||||
v-if="showSubscribeHistoryAction"
|
||||
icon="mdi-history"
|
||||
color="info"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
variant="tonal"
|
||||
appear
|
||||
class="compact-fab compact-fab--secondary"
|
||||
@click="openSubscribeHistoryDialog"
|
||||
/>
|
||||
<VFab
|
||||
v-if="showDefaultRuleAction"
|
||||
icon="mdi-clipboard-edit-outline"
|
||||
color="primary"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
:class="{ 'mb-16': showSubscribeHistoryAction }"
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="openDefaultRuleDialog"
|
||||
/>
|
||||
<VFab
|
||||
v-if="showShareStatisticsAction"
|
||||
icon="mdi-chart-line"
|
||||
color="info"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="openShareStatisticsDialog"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -136,15 +136,15 @@ onMounted(() => {
|
||||
</Teleport>
|
||||
|
||||
<Teleport to="body" v-if="!appMode && route.path === '/workflow' && activeTab === 'list'">
|
||||
<VFab
|
||||
icon="mdi-plus"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
@click="openAddWorkflowDialog"
|
||||
/>
|
||||
<div class="compact-fab-stack">
|
||||
<VFab
|
||||
icon="mdi-plus"
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="openAddWorkflowDialog"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -238,6 +238,122 @@ html.v-overlay-scroll-blocked body {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { usePWA } from '@/composables/usePWA'
|
||||
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
|
||||
|
||||
// 国际化
|
||||
const { t, locale } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -908,14 +908,12 @@ const showNewFolderAction = computed(() => activeTab.value === 'installed' && !c
|
||||
const showMarketSettingAction = computed(() => activeTab.value === 'market')
|
||||
|
||||
const pluginDynamicMenuItems = computed(() => {
|
||||
locale.value
|
||||
|
||||
if (!appMode.value) return undefined
|
||||
if (!showSearchAction.value) return undefined
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: t('plugin.searchPlugins'),
|
||||
titleKey: 'plugin.searchPlugins',
|
||||
icon: 'mdi-magnify',
|
||||
action: openPluginSearchDialog,
|
||||
},
|
||||
@@ -923,7 +921,7 @@ const pluginDynamicMenuItems = computed(() => {
|
||||
|
||||
if (showNewFolderAction.value) {
|
||||
items.push({
|
||||
title: t('plugin.newFolder'),
|
||||
titleKey: 'plugin.newFolder',
|
||||
icon: 'mdi-folder-plus',
|
||||
action: showNewFolderDialog,
|
||||
})
|
||||
@@ -931,7 +929,7 @@ const pluginDynamicMenuItems = computed(() => {
|
||||
|
||||
if (showMarketSettingAction.value) {
|
||||
items.push({
|
||||
title: t('components.pluginMarketSetting.title'),
|
||||
titleKey: 'dialog.pluginMarketSetting.title',
|
||||
icon: 'mdi-store-cog',
|
||||
action: openMarketSettingDialog,
|
||||
})
|
||||
@@ -1609,41 +1607,32 @@ function onDragStartPlugin(evt: any) {
|
||||
|
||||
<!-- 插件搜索图标 -->
|
||||
<Teleport to="body" v-if="route.path === '/plugins'">
|
||||
<div v-if="isRefreshed && !appMode && showSearchAction">
|
||||
<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"
|
||||
/>
|
||||
<div v-if="isRefreshed && !appMode && showSearchAction" class="compact-fab-stack">
|
||||
<VFab
|
||||
v-if="showMarketSettingAction"
|
||||
icon="mdi-store-cog"
|
||||
color="warning"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
variant="tonal"
|
||||
appear
|
||||
class="mb-16"
|
||||
class="compact-fab compact-fab--secondary"
|
||||
@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>
|
||||
</Teleport>
|
||||
<!-- 插件市场设置窗口 -->
|
||||
|
||||
@@ -18,7 +18,7 @@ import { useBackgroundOptimization } from '@/composables/useBackgroundOptimizati
|
||||
import { useGlobalSettingsStore } from '@/stores'
|
||||
|
||||
// i18n
|
||||
const { t, locale } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 全局设置
|
||||
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 historyDynamicMenuItems = computed(() => {
|
||||
locale.value
|
||||
|
||||
if (selected.value.length === 0) return undefined
|
||||
|
||||
return [
|
||||
{
|
||||
title: t('components.transferQueue.title'),
|
||||
titleKey: 'dialog.transferQueue.title',
|
||||
icon: 'mdi-timer-sand-paused',
|
||||
action: () => {
|
||||
transferQueueDialog.value = true
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('transferHistory.actions.batchRedo'),
|
||||
titleKey: 'transferHistory.actions.batchRedo',
|
||||
icon: 'mdi-redo-variant',
|
||||
action: () => {
|
||||
retransferBatch()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('transferHistory.actions.batchDelete'),
|
||||
titleKey: 'transferHistory.actions.batchDelete',
|
||||
icon: 'mdi-trash-can-outline',
|
||||
color: 'error',
|
||||
action: () => {
|
||||
@@ -980,40 +978,31 @@ onUnmounted(() => {
|
||||
|
||||
<!-- 非 app 模式下的 FAB 按钮 -->
|
||||
<Teleport to="body" v-if="!appMode && route.path === '/history'">
|
||||
<div v-if="isRefreshed">
|
||||
<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"
|
||||
/>
|
||||
<div v-if="isRefreshed" class="compact-fab-stack compact-fab-stack--history">
|
||||
<VFab
|
||||
v-if="selected.length > 0"
|
||||
icon="mdi-trash-can-outline"
|
||||
color="error"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
color="warning"
|
||||
variant="tonal"
|
||||
appear
|
||||
class="compact-fab compact-fab--secondary"
|
||||
@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>
|
||||
</Teleport>
|
||||
|
||||
@@ -393,17 +393,15 @@ useDynamicButton({
|
||||
/>
|
||||
<!-- 新增站点按钮 -->
|
||||
<Teleport to="body" v-if="route.path === '/site'">
|
||||
<VFab
|
||||
v-if="isRefreshed && !appMode"
|
||||
icon="mdi-web-plus"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
@click="siteAddDialog = true"
|
||||
:class="{ 'mb-12': appMode }"
|
||||
/>
|
||||
<div v-if="isRefreshed && !appMode" class="compact-fab-stack">
|
||||
<VFab
|
||||
icon="mdi-web-plus"
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="siteAddDialog = true"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
<!-- 新增站点弹窗 -->
|
||||
<SiteAddEditDialog
|
||||
|
||||
@@ -99,17 +99,15 @@ useDynamicButton({
|
||||
|
||||
<!-- 新增用户按钮 -->
|
||||
<Teleport to="body" v-if="route.path === '/user'">
|
||||
<VFab
|
||||
v-if="isRefreshed && !appMode"
|
||||
icon="mdi-account-plus"
|
||||
location="bottom"
|
||||
size="x-large"
|
||||
fixed
|
||||
app
|
||||
appear
|
||||
@click="openAddUserDialog"
|
||||
:class="{ 'mb-12': appMode }"
|
||||
/>
|
||||
<div v-if="isRefreshed && !appMode" class="compact-fab-stack">
|
||||
<VFab
|
||||
icon="mdi-account-plus"
|
||||
color="primary"
|
||||
appear
|
||||
class="compact-fab compact-fab--primary"
|
||||
@click="openAddUserDialog"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<!-- 用户添加弹窗 -->
|
||||
|
||||
Reference in New Issue
Block a user