mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
Polish app center grouped menu styling
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 662 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,42 @@
|
|||||||
|
# App Center 移动端设计 QA
|
||||||
|
|
||||||
|
- Source visual truth: `/Users/jxxghp/.codex/generated_images/019f227f-02c8-74c0-bea9-a59b60a9fa57/exec-9ce518f2-5bba-4249-8187-781c8d2c626e.png`
|
||||||
|
- Implementation screenshot: `/Users/jxxghp/MPProjects/MoviePilot-Frontend/.codex-qa/appcenter-mobile-light.png`
|
||||||
|
- Transparent-theme screenshot: `/Users/jxxghp/MPProjects/MoviePilot-Frontend/.codex-qa/appcenter-mobile-transparent.png`
|
||||||
|
- Full-view comparison: `/Users/jxxghp/MPProjects/MoviePilot-Frontend/.codex-qa/appcenter-mobile-comparison.png`
|
||||||
|
- Viewport: 390px wide mobile viewport; full-page capture
|
||||||
|
- State: authenticated App Center, light/default skin and transparent/default skin
|
||||||
|
|
||||||
|
**Full-view comparison evidence**
|
||||||
|
|
||||||
|
- The implementation preserves the selected concept's grouped iOS menu hierarchy: external section labels, one surface per group, colored rounded-square icons, inset separators, and trailing chevrons.
|
||||||
|
- Existing MoviePilot shell chrome and permission-filtered menu content intentionally remain unchanged; the reference mock only defines the App Center content surface.
|
||||||
|
- Focused-region comparison was not needed because the full-height side-by-side comparison keeps row typography, icon tiles, dividers, radii, and section spacing readable.
|
||||||
|
|
||||||
|
**Required fidelity surfaces**
|
||||||
|
|
||||||
|
- Fonts and typography: existing product font stack retained; mobile titles render at 16px/22px and section labels at 13px/20px without wrapping or truncation.
|
||||||
|
- Spacing and layout rhythm: 58px tap rows, 12px page gutters, 18px group gaps, inset dividers, and theme-controlled surface radii match the selected direction without horizontal overflow.
|
||||||
|
- Colors and visual tokens: icon colors come from `NavMenu.iconColor` and global Vuetify semantic color tokens; grouped surfaces, borders, separators, hover/active states, and transparent blur use global tokens.
|
||||||
|
- Image quality and asset fidelity: the reference contains no raster content; existing Material Design Icons are used rather than generated or handcrafted substitutes.
|
||||||
|
- Copy and content: existing localized labels and permission filtering are preserved.
|
||||||
|
- Accessibility and interaction: full-row route links remain functional, touch targets are at least 58px high, active feedback remains visible, and decorative separators are hidden from accessibility APIs.
|
||||||
|
|
||||||
|
**Findings**
|
||||||
|
|
||||||
|
- No actionable P0, P1, or P2 differences remain.
|
||||||
|
- [P3] The existing Agent Assistant bubble can overlap the far-right page edge in captures; this is shared shell behavior outside the App Center redesign scope.
|
||||||
|
|
||||||
|
**Patches made since the previous QA pass**
|
||||||
|
|
||||||
|
- Replaced the row pseudo-element divider, which conflicted with Vuetify's internal pseudo-element, with an explicit 1px separator element.
|
||||||
|
- Removed the transparent theme's hard-coded group border so the card follows the global bordered/default skin token.
|
||||||
|
- Moved icon color metadata into shared menu definitions and kept dynamic plugin menus on the primary fallback.
|
||||||
|
- Constrained the page, cards, lists, rows, and row content to prevent horizontal scrolling.
|
||||||
|
- Added transparent-theme blur-mode token overrides for lightweight, realtime, and blur-disabled modes.
|
||||||
|
|
||||||
|
**Follow-up polish**
|
||||||
|
|
||||||
|
- The existing Agent Assistant edge placement can be refined separately if it becomes distracting across other mobile pages.
|
||||||
|
|
||||||
|
final result: passed
|
||||||
Vendored
+3
@@ -113,10 +113,13 @@ export interface NavLinkProps {
|
|||||||
rel?: ATagRelAttrValues
|
rel?: ATagRelAttrValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NavMenuIconColor = 'primary' | 'info' | 'success' | 'warning' | 'secondary'
|
||||||
|
|
||||||
export interface NavLink extends NavLinkProps, Partial<AclProperties> {
|
export interface NavLink extends NavLinkProps, Partial<AclProperties> {
|
||||||
title: string
|
title: string
|
||||||
full_title?: string
|
full_title?: string
|
||||||
icon?: unknown
|
icon?: unknown
|
||||||
|
iconColor?: NavMenuIconColor
|
||||||
badgeContent?: string
|
badgeContent?: string
|
||||||
badgeClass?: string
|
badgeClass?: string
|
||||||
disable?: boolean
|
disable?: boolean
|
||||||
|
|||||||
+153
-11
@@ -18,7 +18,7 @@ const userPermissions = computed(() => buildUserPermissionContext(userStore.supe
|
|||||||
// 应用分组(以header分组)
|
// 应用分组(以header分组)
|
||||||
const appGroups = ref<Record<string, NavMenu[]>>({})
|
const appGroups = ref<Record<string, NavMenu[]>>({})
|
||||||
|
|
||||||
// 根据header属性对应用进行分类(含插件侧栏项,与桌面端侧栏一致)
|
/** 按菜单 header 聚合内置与插件入口,并保持与桌面侧栏一致的权限过滤结果。 */
|
||||||
async function categorizeApps() {
|
async function categorizeApps() {
|
||||||
const allMenus = getNavMenus(t)
|
const allMenus = getNavMenus(t)
|
||||||
const filteredMenus = filterMenusByPermission(allMenus, userPermissions.value)
|
const filteredMenus = filterMenusByPermission(allMenus, userPermissions.value)
|
||||||
@@ -53,14 +53,14 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="app-settings-container">
|
<div class="app-settings-container">
|
||||||
<VContainer>
|
<VContainer class="app-settings-content">
|
||||||
<!-- 遍历所有分组 -->
|
<!-- 遍历所有分组 -->
|
||||||
<div v-for="(apps, header) in appGroups" :key="header" class="mb-3">
|
<section v-for="(apps, header) in appGroups" :key="header" class="settings-section">
|
||||||
<VListSubheader class="ps-1">
|
<VListSubheader class="settings-section-title">
|
||||||
{{ header }}
|
{{ header }}
|
||||||
</VListSubheader>
|
</VListSubheader>
|
||||||
<!-- 分组内容 - 使用卡片包装 -->
|
<!-- 分组内容 - 使用卡片包装 -->
|
||||||
<VCard variant="flat" class="settings-section-card">
|
<VCard variant="flat" class="app-grouped-list settings-section-card">
|
||||||
<VList lines="one" class="settings-list">
|
<VList lines="one" class="settings-list">
|
||||||
<VListItem
|
<VListItem
|
||||||
v-for="(app, appIndex) in apps"
|
v-for="(app, appIndex) in apps"
|
||||||
@@ -71,35 +71,57 @@ onMounted(() => {
|
|||||||
rounded="0"
|
rounded="0"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VAvatar size="42" color="primary" variant="text" class="me-3">
|
<VAvatar
|
||||||
|
size="42"
|
||||||
|
color="primary"
|
||||||
|
variant="text"
|
||||||
|
class="settings-list-icon me-3"
|
||||||
|
:class="`settings-list-icon--tone-${app.iconColor || 'primary'}`"
|
||||||
|
>
|
||||||
<VIcon :icon="app.icon as string" size="24"></VIcon>
|
<VIcon :icon="app.icon as string" size="24"></VIcon>
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<VListItemTitle class="font-weight-medium">
|
<VListItemTitle class="settings-list-title font-weight-medium">
|
||||||
{{ app.full_title || app.title }}
|
{{ app.full_title || app.title }}
|
||||||
</VListItemTitle>
|
</VListItemTitle>
|
||||||
|
|
||||||
<VListItemSubtitle v-if="app.description">
|
<VListItemSubtitle v-if="app.description" class="settings-list-subtitle">
|
||||||
{{ app.description }}
|
{{ app.description }}
|
||||||
</VListItemSubtitle>
|
</VListItemSubtitle>
|
||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<VIcon icon="mdi-chevron-right"></VIcon>
|
<VIcon class="settings-list-chevron" icon="mdi-chevron-right"></VIcon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="appIndex < apps.length - 1"
|
||||||
|
class="settings-list-separator"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
</VCard>
|
</VCard>
|
||||||
</div>
|
</section>
|
||||||
</VContainer>
|
</VContainer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-settings-container {
|
.app-settings-container {
|
||||||
|
inline-size: 100%;
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
max-inline-size: 960px;
|
max-inline-size: 960px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-block-end: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-title {
|
||||||
|
padding-inline-start: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-section-card {
|
.settings-section-card {
|
||||||
@@ -125,7 +147,127 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(var(--v-theme-primary), 0.05);
|
background-color: var(--app-grouped-list-hover-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
.app-settings-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-inline-size: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-block: 8px calc(24px + env(safe-area-inset-bottom));
|
||||||
|
padding-inline: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-block-end: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section:last-child {
|
||||||
|
margin-block-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-title {
|
||||||
|
min-block-size: auto;
|
||||||
|
padding-block: 0 7px;
|
||||||
|
padding-inline: 12px;
|
||||||
|
color: var(--app-grouped-list-header-color);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.015em;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-card {
|
||||||
|
max-inline-size: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
border-radius: var(--app-grouped-list-radius) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list {
|
||||||
|
max-inline-size: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-item {
|
||||||
|
max-inline-size: 100%;
|
||||||
|
min-inline-size: 0;
|
||||||
|
min-block-size: 58px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-block: 7px;
|
||||||
|
padding-inline: 12px 9px;
|
||||||
|
border-block-end: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-separator {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--app-grouped-list-separator-color);
|
||||||
|
block-size: 1px;
|
||||||
|
inset-block-end: 0;
|
||||||
|
inset-inline: 58px 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-item:active {
|
||||||
|
background-color: var(--app-grouped-list-active-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon {
|
||||||
|
--app-grouped-list-icon-rgb: var(--app-grouped-list-icon-primary-rgb);
|
||||||
|
|
||||||
|
border-radius: max(var(--app-control-radius), 8px) !important;
|
||||||
|
background: rgba(var(--app-grouped-list-icon-rgb), var(--app-grouped-list-icon-opacity)) !important;
|
||||||
|
color: var(--app-grouped-list-icon-foreground) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon--tone-info {
|
||||||
|
--app-grouped-list-icon-rgb: var(--app-grouped-list-icon-info-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon--tone-success {
|
||||||
|
--app-grouped-list-icon-rgb: var(--app-grouped-list-icon-success-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon--tone-warning {
|
||||||
|
--app-grouped-list-icon-rgb: var(--app-grouped-list-icon-warning-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon--tone-secondary {
|
||||||
|
--app-grouped-list-icon-rgb: var(--app-grouped-list-icon-secondary-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-item :deep(.v-list-item__content) {
|
||||||
|
min-inline-size: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-icon :deep(.v-icon) {
|
||||||
|
font-size: 1.375rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
letter-spacing: 0;
|
||||||
|
line-height: 1.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-subtitle {
|
||||||
|
margin-block-start: 2px;
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.58);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-list-chevron {
|
||||||
|
color: var(--app-grouped-list-chevron-color);
|
||||||
|
font-size: 1.375rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+23
-8
@@ -2,7 +2,7 @@ import { useGlobalSettingsStore } from '@/stores'
|
|||||||
import type { NavMenu, NavMenuTabItem } from '@/@layouts/types'
|
import type { NavMenu, NavMenuTabItem } from '@/@layouts/types'
|
||||||
import type { Composer } from 'vue-i18n'
|
import type { Composer } from 'vue-i18n'
|
||||||
|
|
||||||
// 构建路由菜单,每次调用时使用当前的语言环境
|
/** 构建当前语言与全局模式对应的主导航菜单。 */
|
||||||
export function getNavMenus(t: Composer['t']): NavMenu[] {
|
export function getNavMenus(t: Composer['t']): NavMenu[] {
|
||||||
const globalSettingsStore = useGlobalSettingsStore()
|
const globalSettingsStore = useGlobalSettingsStore()
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.dashboard'),
|
title: t('navItems.dashboard'),
|
||||||
icon: 'mdi-home-outline',
|
icon: 'mdi-home-outline',
|
||||||
|
iconColor: 'primary',
|
||||||
to: '/dashboard',
|
to: '/dashboard',
|
||||||
header: t('menu.start'),
|
header: t('menu.start'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -22,6 +23,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.searchResult'),
|
title: t('navItems.searchResult'),
|
||||||
icon: 'mdi-magnify',
|
icon: 'mdi-magnify',
|
||||||
|
iconColor: 'info',
|
||||||
to: '/resource',
|
to: '/resource',
|
||||||
header: t('menu.start'),
|
header: t('menu.start'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -30,6 +32,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.recommend'),
|
title: t('navItems.recommend'),
|
||||||
icon: 'mdi-star-outline',
|
icon: 'mdi-star-outline',
|
||||||
|
iconColor: 'primary',
|
||||||
to: '/recommend',
|
to: '/recommend',
|
||||||
header: t('menu.discovery'),
|
header: t('menu.discovery'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -40,6 +43,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.explore'),
|
title: t('navItems.explore'),
|
||||||
icon: 'mdi-apple-safari',
|
icon: 'mdi-apple-safari',
|
||||||
|
iconColor: 'info',
|
||||||
to: '/discover',
|
to: '/discover',
|
||||||
header: t('menu.discovery'),
|
header: t('menu.discovery'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -51,6 +55,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
title: t('navItems.movie'),
|
title: t('navItems.movie'),
|
||||||
full_title: t('navItems.movieSubscribe'),
|
full_title: t('navItems.movieSubscribe'),
|
||||||
icon: 'mdi-movie-open-outline',
|
icon: 'mdi-movie-open-outline',
|
||||||
|
iconColor: 'success',
|
||||||
to: '/subscribe/movie',
|
to: '/subscribe/movie',
|
||||||
header: t('menu.subscribe'),
|
header: t('menu.subscribe'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -62,6 +67,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
title: t('navItems.tv'),
|
title: t('navItems.tv'),
|
||||||
full_title: t('navItems.tvSubscribe'),
|
full_title: t('navItems.tvSubscribe'),
|
||||||
icon: 'mdi-television',
|
icon: 'mdi-television',
|
||||||
|
iconColor: 'warning',
|
||||||
to: '/subscribe/tv',
|
to: '/subscribe/tv',
|
||||||
header: t('menu.subscribe'),
|
header: t('menu.subscribe'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -73,6 +79,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
title: t('navItems.workflow'),
|
title: t('navItems.workflow'),
|
||||||
full_title: t('navItems.workflow'),
|
full_title: t('navItems.workflow'),
|
||||||
icon: 'mdi-state-machine',
|
icon: 'mdi-state-machine',
|
||||||
|
iconColor: 'primary',
|
||||||
to: '/workflow',
|
to: '/workflow',
|
||||||
header: t('menu.subscribe'),
|
header: t('menu.subscribe'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -84,6 +91,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
title: t('navItems.calendar'),
|
title: t('navItems.calendar'),
|
||||||
full_title: t('navItems.calendar'),
|
full_title: t('navItems.calendar'),
|
||||||
icon: 'mdi-calendar',
|
icon: 'mdi-calendar',
|
||||||
|
iconColor: 'info',
|
||||||
to: '/calendar',
|
to: '/calendar',
|
||||||
header: t('menu.subscribe'),
|
header: t('menu.subscribe'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -92,6 +100,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.downloadManager'),
|
title: t('navItems.downloadManager'),
|
||||||
icon: 'mdi-download-outline',
|
icon: 'mdi-download-outline',
|
||||||
|
iconColor: 'info',
|
||||||
to: '/downloading',
|
to: '/downloading',
|
||||||
header: t('menu.organize'),
|
header: t('menu.organize'),
|
||||||
admin: false,
|
admin: false,
|
||||||
@@ -100,6 +109,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.mediaOrganize'),
|
title: t('navItems.mediaOrganize'),
|
||||||
icon: 'mdi-folder-play-outline',
|
icon: 'mdi-folder-play-outline',
|
||||||
|
iconColor: 'warning',
|
||||||
to: '/history',
|
to: '/history',
|
||||||
header: t('menu.organize'),
|
header: t('menu.organize'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -108,6 +118,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.fileManager'),
|
title: t('navItems.fileManager'),
|
||||||
icon: 'mdi-folder-multiple-outline',
|
icon: 'mdi-folder-multiple-outline',
|
||||||
|
iconColor: 'success',
|
||||||
to: '/filemanager',
|
to: '/filemanager',
|
||||||
header: t('menu.organize'),
|
header: t('menu.organize'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -116,6 +127,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.pluginManager'),
|
title: t('navItems.pluginManager'),
|
||||||
icon: 'mdi-puzzle-outline',
|
icon: 'mdi-puzzle-outline',
|
||||||
|
iconColor: 'primary',
|
||||||
to: '/plugins',
|
to: '/plugins',
|
||||||
header: t('menu.system'),
|
header: t('menu.system'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -125,6 +137,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.siteManager'),
|
title: t('navItems.siteManager'),
|
||||||
icon: 'mdi-web',
|
icon: 'mdi-web',
|
||||||
|
iconColor: 'info',
|
||||||
to: '/site',
|
to: '/site',
|
||||||
header: t('menu.system'),
|
header: t('menu.system'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -133,6 +146,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.userManager'),
|
title: t('navItems.userManager'),
|
||||||
icon: 'mdi-account-group-outline',
|
icon: 'mdi-account-group-outline',
|
||||||
|
iconColor: 'success',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
header: t('menu.system'),
|
header: t('menu.system'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -143,6 +157,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
{
|
{
|
||||||
title: t('navItems.settings'),
|
title: t('navItems.settings'),
|
||||||
icon: 'mdi-cog-outline',
|
icon: 'mdi-cog-outline',
|
||||||
|
iconColor: 'secondary',
|
||||||
to: '/setting',
|
to: '/setting',
|
||||||
header: t('menu.system'),
|
header: t('menu.system'),
|
||||||
admin: true,
|
admin: true,
|
||||||
@@ -154,7 +169,7 @@ export function getNavMenus(t: Composer['t']): NavMenu[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取推荐标签页
|
/** 返回推荐页可用的分类标签。 */
|
||||||
export function getRecommendTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getRecommendTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{ title: t('recommend.all'), icon: 'mdi-filmstrip-box-multiple', tab: t('recommend.all') },
|
{ title: t('recommend.all'), icon: 'mdi-filmstrip-box-multiple', tab: t('recommend.all') },
|
||||||
@@ -165,7 +180,7 @@ export function getRecommendTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取设置标签页
|
/** 返回系统设置页的配置标签。 */
|
||||||
export function getSettingTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getSettingTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -213,7 +228,7 @@ export function getSettingTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取电影订阅标签页
|
/** 返回电影订阅页的业务标签。 */
|
||||||
export function getSubscribeMovieTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getSubscribeMovieTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -229,7 +244,7 @@ export function getSubscribeMovieTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取电视剧订阅标签页
|
/** 返回电视剧订阅页的业务标签。 */
|
||||||
export function getSubscribeTvTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getSubscribeTvTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -250,7 +265,7 @@ export function getSubscribeTvTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取插件标签页
|
/** 返回插件管理页的业务标签。 */
|
||||||
export function getPluginTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getPluginTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -266,7 +281,7 @@ export function getPluginTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取发现标签页
|
/** 返回发现页的媒体来源标签。 */
|
||||||
export function getDiscoverTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getDiscoverTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -287,7 +302,7 @@ export function getDiscoverTabs(t: Composer['t']): NavMenuTabItem[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取工作流标签页
|
/** 返回工作流页的业务标签。 */
|
||||||
export function getWorkflowTabs(t: Composer['t']): NavMenuTabItem[] {
|
export function getWorkflowTabs(t: Composer['t']): NavMenuTabItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,6 +97,22 @@ html {
|
|||||||
--app-overlay-shadow: var(--app-elevation-0);
|
--app-overlay-shadow: var(--app-elevation-0);
|
||||||
--app-surface-shadow: var(--app-elevation-0);
|
--app-surface-shadow: var(--app-elevation-0);
|
||||||
--app-surface-hover-shadow: var(--app-elevation-0);
|
--app-surface-hover-shadow: var(--app-elevation-0);
|
||||||
|
--app-grouped-list-background: rgb(var(--v-theme-surface));
|
||||||
|
--app-grouped-list-backdrop-filter: none;
|
||||||
|
--app-grouped-list-border: var(--app-surface-border);
|
||||||
|
--app-grouped-list-radius: var(--app-theme-surface-radius);
|
||||||
|
--app-grouped-list-separator-color: rgba(var(--v-theme-on-surface), 0.14);
|
||||||
|
--app-grouped-list-header-color: rgba(var(--v-theme-on-surface), 0.58);
|
||||||
|
--app-grouped-list-chevron-color: rgba(var(--v-theme-on-surface), 0.42);
|
||||||
|
--app-grouped-list-hover-background: rgba(var(--v-theme-primary), 0.05);
|
||||||
|
--app-grouped-list-active-background: rgba(var(--v-theme-primary), 0.1);
|
||||||
|
--app-grouped-list-icon-foreground: rgb(var(--v-theme-on-primary));
|
||||||
|
--app-grouped-list-icon-opacity: 0.92;
|
||||||
|
--app-grouped-list-icon-primary-rgb: var(--v-theme-primary);
|
||||||
|
--app-grouped-list-icon-info-rgb: var(--v-theme-info);
|
||||||
|
--app-grouped-list-icon-success-rgb: var(--v-theme-success);
|
||||||
|
--app-grouped-list-icon-warning-rgb: var(--v-theme-warning);
|
||||||
|
--app-grouped-list-icon-secondary-rgb: var(--v-theme-secondary);
|
||||||
--mp-motion-duration-page: 180ms;
|
--mp-motion-duration-page: 180ms;
|
||||||
--mp-motion-duration-overlay: 160ms;
|
--mp-motion-duration-overlay: 160ms;
|
||||||
--mp-motion-ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
|
--mp-motion-ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||||
@@ -206,6 +222,17 @@ html[data-theme-radius='extra'] {
|
|||||||
box-shadow: var(--app-surface-shadow) !important;
|
box-shadow: var(--app-surface-shadow) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
// iOS 式分组菜单的共享表面,主题只需覆盖 token 即可适配实色或玻璃背景。
|
||||||
|
.app-grouped-list.v-card {
|
||||||
|
--app-surface-radius: var(--app-grouped-list-radius);
|
||||||
|
|
||||||
|
border: var(--app-grouped-list-border);
|
||||||
|
backdrop-filter: var(--app-grouped-list-backdrop-filter) !important;
|
||||||
|
background-color: var(--app-grouped-list-background) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 统一卡片上浮反馈;hover 命中区域应放在静止外层,避免上浮后底边反复触发 mouseleave。
|
// 统一卡片上浮反馈;hover 命中区域应放在静止外层,避免上浮后底边反复触发 mouseleave。
|
||||||
.v-card.app-hover-lift-card {
|
.v-card.app-hover-lift-card {
|
||||||
border: var(--app-card-light-border);
|
border: var(--app-card-light-border);
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ html[data-theme="transparent"] {
|
|||||||
--transparent-blur: 10px;
|
--transparent-blur: 10px;
|
||||||
--transparent-blur-light: 6px;
|
--transparent-blur-light: 6px;
|
||||||
--transparent-blur-heavy: 16px;
|
--transparent-blur-heavy: 16px;
|
||||||
|
--app-grouped-list-background: rgba(var(--v-theme-surface), var(--transparent-opacity-light));
|
||||||
|
--app-grouped-list-backdrop-filter: blur(var(--transparent-blur));
|
||||||
|
--app-grouped-list-separator-color: rgba(var(--v-theme-on-surface), 0.18);
|
||||||
|
--app-grouped-list-header-color: rgba(var(--v-theme-on-surface), 0.64);
|
||||||
|
--app-grouped-list-chevron-color: rgba(var(--v-theme-on-surface), 0.5);
|
||||||
|
--app-grouped-list-hover-background: rgba(var(--v-theme-primary), 0.08);
|
||||||
|
--app-grouped-list-active-background: rgba(var(--v-theme-primary), 0.14);
|
||||||
|
--app-grouped-list-icon-opacity: 0.88;
|
||||||
|
|
||||||
// 应用、布局、主内容区域
|
// 应用、布局、主内容区域
|
||||||
.v-application, .v-layout, .v-main, .layout-page-content {
|
.v-application, .v-layout, .v-main, .layout-page-content {
|
||||||
@@ -245,6 +253,8 @@ html[data-theme="transparent"].transparent-background-blur-disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="transparent"].transparent-glass-lightweight {
|
html[data-theme="transparent"].transparent-glass-lightweight {
|
||||||
|
--app-grouped-list-backdrop-filter: none;
|
||||||
|
|
||||||
.global-blur-layer {
|
.global-blur-layer {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
backdrop-filter: none !important;
|
backdrop-filter: none !important;
|
||||||
@@ -307,6 +317,8 @@ html[data-theme="transparent"].transparent-glass-lightweight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="transparent"].transparent-glass-realtime {
|
html[data-theme="transparent"].transparent-glass-realtime {
|
||||||
|
--app-grouped-list-backdrop-filter: none;
|
||||||
|
|
||||||
.background-image.active {
|
.background-image.active {
|
||||||
filter: none;
|
filter: none;
|
||||||
transform: none;
|
transform: none;
|
||||||
@@ -350,6 +362,8 @@ html[data-theme="transparent"].transparent-glass-realtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="transparent"].transparent-blur-disabled {
|
html[data-theme="transparent"].transparent-blur-disabled {
|
||||||
|
--app-grouped-list-backdrop-filter: none;
|
||||||
|
|
||||||
.layout-vertical-nav,
|
.layout-vertical-nav,
|
||||||
.v-list,
|
.v-list,
|
||||||
.v-card:not(.no-blur),
|
.v-card:not(.no-blur),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export function navMenuFromPluginSidebarItem(
|
|||||||
return {
|
return {
|
||||||
title: item.title,
|
title: item.title,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
|
iconColor: 'primary',
|
||||||
to: {
|
to: {
|
||||||
name: 'plugin-app',
|
name: 'plugin-app',
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
Reference in New Issue
Block a user