优化多个页面的标签样式,提升组件一致性和用户体验

This commit is contained in:
jxxghp
2025-04-02 13:20:22 +08:00
parent 3822ab20d5
commit 7a691fe4e7
7 changed files with 120 additions and 113 deletions
+73 -69
View File
@@ -45,102 +45,104 @@ interface Props {
<style scoped> <style scoped>
.no-data-container { .no-data-container {
width: 100%; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
align-items: center; align-items: center;
padding: 3rem 1rem; justify-content: center;
min-height: 300px; inline-size: 100%;
min-block-size: 300px;
padding-block: 3rem;
padding-inline: 1rem;
text-align: center; text-align: center;
position: relative;
} }
/* 图标样式 */ /* 图标样式 */
.icon-wrapper { .icon-wrapper {
position: relative; position: relative;
margin: 0 auto 2rem;
width: 100px;
height: 100px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
block-size: 100px;
inline-size: 100px;
margin-block: 0 2rem;
margin-inline: auto;
} }
.icon-glow { .icon-glow {
position: absolute; position: absolute;
width: 80px;
height: 80px;
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle, rgba(var(--v-theme-primary), 0.8) 0%, rgba(var(--v-theme-primary), 0) 70%);
filter: blur(15px);
opacity: 0.8;
animation: pulse 3s infinite ease-in-out; animation: pulse 3s infinite ease-in-out;
background: radial-gradient(circle, rgba(var(--v-theme-primary), 0.8) 0%, rgba(var(--v-theme-primary), 0) 70%);
block-size: 80px;
filter: blur(15px);
inline-size: 80px;
opacity: 0.8;
} }
.icon-container { .icon-container {
position: relative; position: relative;
width: 80px; z-index: 2;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.9), rgba(var(--v-theme-secondary), 0.8));
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 4px 20px rgba(var(--v-theme-primary), 0.3), border-radius: 50%;
0 2px 5px rgba(0, 0, 0, 0.1), background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.9), rgba(var(--v-theme-secondary), 0.8));
inset 0 1px 1px rgba(255, 255, 255, 0.2); block-size: 80px;
z-index: 2; inline-size: 80px;
} }
.main-icon { .main-icon {
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
animation: slight-bounce 3s infinite ease-in-out; animation: slight-bounce 3s infinite ease-in-out;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 30%));
} }
.pulse-ring { .pulse-ring {
position: absolute; position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
border-radius: 50%;
border: 2px solid rgba(var(--v-theme-primary), 0.5);
opacity: 0;
z-index: 1; z-index: 1;
border: 2px solid rgba(var(--v-theme-primary), 0.5);
border-radius: 50%;
animation: ripple 2s infinite ease-out; animation: ripple 2s infinite ease-out;
block-size: 100px;
inline-size: 100px;
inset-block-start: 50%;
inset-inline-start: 50%;
opacity: 0;
transform: translate(-50%, -50%);
} }
.pulse-ring::before { .pulse-ring::before {
content: '';
position: absolute; position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 85px;
height: 85px;
border-radius: 50%;
border: 2px solid rgba(var(--v-theme-primary), 0.3); border: 2px solid rgba(var(--v-theme-primary), 0.3);
border-radius: 50%;
animation: ripple 2s infinite 0.5s ease-out; animation: ripple 2s infinite 0.5s ease-out;
block-size: 85px;
content: '';
inline-size: 85px;
inset-block-start: 50%;
inset-inline-start: 50%;
transform: translate(-50%, -50%);
} }
@keyframes ripple { @keyframes ripple {
0% { 0% {
transform: translate(-50%, -50%) scale(0.9);
opacity: 1; opacity: 1;
transform: translate(-50%, -50%) scale(0.9);
} }
100% { 100% {
transform: translate(-50%, -50%) scale(1.5);
opacity: 0; opacity: 0;
transform: translate(-50%, -50%) scale(1.5);
} }
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { 0%,
100% {
opacity: 0.5; opacity: 0.5;
transform: scale(0.8); transform: scale(0.8);
} }
50% { 50% {
opacity: 1; opacity: 1;
transform: scale(1.1); transform: scale(1.1);
@@ -148,9 +150,11 @@ interface Props {
} }
@keyframes slight-bounce { @keyframes slight-bounce {
0%, 100% { 0%,
100% {
transform: translateY(0); transform: translateY(0);
} }
50% { 50% {
transform: translateY(-3px); transform: translateY(-3px);
} }
@@ -158,36 +162,36 @@ interface Props {
/* 文字样式 */ /* 文字样式 */
.error-title { .error-title {
position: relative;
color: rgba(var(--v-theme-on-surface), 0.95);
font-size: 1.75rem; font-size: 1.75rem;
font-weight: 700; font-weight: 700;
margin-bottom: 0.75rem; margin-block-end: 0.75rem;
color: rgba(var(--v-theme-on-surface), 0.95); text-shadow: 0 1px 2px rgba(0, 0, 0, 5%);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
position: relative;
} }
.error-title::after { .error-title::after {
content: '';
display: block; display: block;
width: 40px;
height: 3px;
background: linear-gradient(90deg, rgba(var(--v-theme-primary), 0.8), rgba(var(--v-theme-primary), 0.2));
border-radius: 3px; border-radius: 3px;
margin: 0.5rem auto 0; background: linear-gradient(90deg, rgba(var(--v-theme-primary), 0.8), rgba(var(--v-theme-primary), 0.2));
block-size: 3px;
content: '';
inline-size: 40px;
margin-block: 0.5rem 0;
margin-inline: auto;
} }
.error-description { .error-description {
font-size: 1.1rem;
color: rgba(var(--v-theme-on-surface), 0.75); color: rgba(var(--v-theme-on-surface), 0.75);
margin-bottom: 1.5rem; font-size: 1.1rem;
line-height: 1.6; line-height: 1.6;
max-width: 80%; margin-block-end: 1.5rem;
margin-left: auto; margin-inline: auto;
margin-right: auto; max-inline-size: 80%;
} }
.actions-container { .actions-container {
margin-top: 1.5rem; margin-block-start: 1.5rem;
} }
.actions-container :deep(.v-btn) { .actions-container :deep(.v-btn) {
@@ -200,31 +204,32 @@ interface Props {
} }
/* 响应式调整 */ /* 响应式调整 */
@media (max-width: 600px) { @media (width <= 600px) {
.no-data-container { .no-data-container {
padding: 2rem 1rem; padding-block: 2rem;
padding-inline: 1rem;
} }
.icon-wrapper { .icon-wrapper {
width: 80px; block-size: 80px;
height: 80px; inline-size: 80px;
margin-bottom: 1.5rem; margin-block-end: 1.5rem;
} }
.icon-container { .icon-container {
width: 70px; block-size: 70px;
height: 70px; inline-size: 70px;
} }
.icon-glow { .icon-glow {
width: 70px; block-size: 70px;
height: 70px; inline-size: 70px;
} }
.pulse-ring, .pulse-ring,
.pulse-ring::before { .pulse-ring::before {
width: 80px; block-size: 80px;
height: 80px; inline-size: 80px;
} }
.error-title { .error-title {
@@ -233,8 +238,7 @@ interface Props {
.error-description { .error-description {
font-size: 0.95rem; font-size: 0.95rem;
max-width: 90%; max-inline-size: 90%;
} }
} }
</style> </style>
+4 -7
View File
@@ -118,14 +118,11 @@ onActivated(async () => {
<template> <template>
<div> <div>
<VTabs v-model="activeTab" show-arrows> <VTabs v-model="activeTab" show-arrows stacked>
<draggable v-model="discoverTabs" handle=".cursor-move" item-key="tab" tag="div" @end="saveTabOrder"> <draggable v-model="discoverTabs" handle=".tab-move" item-key="tab" tag="div" @end="saveTabOrder">
<template #item="{ element }"> <template #item="{ element }">
<VTab :key="element.mediaid_prefix" :value="element.mediaid_prefix"> <VTab :key="element.mediaid_prefix" :value="element.mediaid_prefix" class="px-10 rounded-t-lg">
<div><VIcon class="cursor-move" start icon="mdi-drag" /></div> <span class="tab-move">{{ element.name }}</span>
<div class="min-w-24">
<div>{{ element.name }}</div>
</div>
</VTab> </VTab>
</template> </template>
</draggable> </draggable>
+3 -3
View File
@@ -37,9 +37,9 @@ onActivated(async () => {
<template> <template>
<div v-if="downloaders.length > 0"> <div v-if="downloaders.length > 0">
<VTabs v-model="activeTab"> <VTabs v-model="activeTab" show-arrows stacked>
<VTab v-for="item in downloaders" :value="item.name" @to="jumpTab(item.name)"> <VTab v-for="item in downloaders" :value="item.name" @to="jumpTab(item.name)" class="px-10 rounded-t-lg">
<span class="min-w-24">{{ item.name }}</span> {{ item.name }}
</VTab> </VTab>
</VTabs> </VTabs>
+21 -13
View File
@@ -18,25 +18,33 @@ function jumpTab(tab: string) {
<template> <template>
<div> <div>
<VTabs v-model="activeTab" show-arrows> <VTabs v-model="activeTab" show-arrows stacked>
<VTab v-if="subType == '电影'" v-for="item in SubscribeMovieTabs" :value="item.tab" @to="jumpTab(item.tab)"> <VTab
<div class="flex align-center min-w-24"> v-if="subType == '电影'"
<VIcon size="20" start :icon="item.icon" /> v-for="item in SubscribeMovieTabs"
{{ item.title }} :value="item.tab"
</div> @to="jumpTab(item.tab)"
class="px-10 rounded-t-lg"
>
<VIcon size="x-large" start :icon="item.icon" />
{{ item.title }}
</VTab> </VTab>
<VTab v-if="subType == '电视剧'" v-for="item in SubscribeTvTabs" :value="item.tab" @to="jumpTab(item.tab)"> <VTab
<div class="flex align-center min-w-24"> v-if="subType == '电视剧'"
<VIcon size="20" start :icon="item.icon" /> v-for="item in SubscribeTvTabs"
{{ item.title }} :value="item.tab"
</div> @to="jumpTab(item.tab)"
class="px-10 rounded-t-lg"
>
<VIcon size="x-large" start :icon="item.icon" />
{{ item.title }}
</VTab> </VTab>
</VTabs> </VTabs>
<VWindow v-model="activeTab" class="mt-5 disable-tab-transition" :touch="false"> <VWindow v-model="activeTab" class="disable-tab-transition" :touch="false">
<VWindowItem value="mysub"> <VWindowItem value="mysub">
<transition name="fade-slide" appear> <transition name="fade-slide" appear>
<div> <div class="mt-4">
<SubscribeListView :type="subType" :subid="subId" /> <SubscribeListView :type="subType" :subid="subId" />
</div> </div>
</transition> </transition>
+3 -3
View File
@@ -187,7 +187,7 @@ export const SubscribeMovieTabs = [
{ {
title: '我的订阅', title: '我的订阅',
tab: 'mysub', tab: 'mysub',
icon: 'mdi-heart', icon: 'mdi-bell-check',
}, },
{ {
title: '热门订阅', title: '热门订阅',
@@ -201,7 +201,7 @@ export const SubscribeTvTabs = [
{ {
title: '我的订阅', title: '我的订阅',
tab: 'mysub', tab: 'mysub',
icon: 'mdi-heart', icon: 'mdi-bell-check',
}, },
{ {
title: '热门订阅', title: '热门订阅',
@@ -225,7 +225,7 @@ export const PluginTabs = [
{ {
title: '插件市场', title: '插件市场',
tab: 'market', tab: 'market',
icon: 'mdi-store', icon: 'mdi-shopping',
}, },
] ]
+4 -6
View File
@@ -397,12 +397,10 @@ onMounted(async () => {
<template> <template>
<div> <div>
<VTabs v-model="activeTab" show-arrows> <VTabs v-model="activeTab" show-arrows stacked>
<VTab v-for="item in PluginTabs" :value="item.tab"> <VTab v-for="item in PluginTabs" :value="item.tab" class="px-10 rounded-t-lg">
<div class="flex align-center min-w-24"> <VIcon size="x-large" start :icon="item.icon" />
<VIcon size="20" start :icon="item.icon" /> {{ item.title }}
{{ item.title }}
</div>
</VTab> </VTab>
</VTabs> </VTabs>
+1 -1
View File
@@ -151,7 +151,7 @@ onActivated(async () => {
handle=".cursor-move" handle=".cursor-move"
item-key="id" item-key="id"
tag="div" tag="div"
:component-data="{ class: 'grid gap-4 grid-subscribe-card p-1' }" :component-data="{ class: 'grid gap-4 grid-subscribe-card' }"
> >
<template #item="{ element }"> <template #item="{ element }">
<SubscribeCard :key="element.id" :media="element" @remove="fetchData" @save="fetchData" /> <SubscribeCard :key="element.id" :media="element" @remove="fetchData" @save="fetchData" />