优化 SiteCard 组件,调整样式和结构,提升可读性和用户体验

This commit is contained in:
jxxghp
2025-04-01 00:06:31 +08:00
parent ed53fbae93
commit 410ff78ef5
+58 -99
View File
@@ -122,10 +122,10 @@ const getMaxDataValue = computed(() => {
// 获取站点数据中的最大值作为基准 // 获取站点数据中的最大值作为基准
const upload = cardProps.data?.upload || 0 const upload = cardProps.data?.upload || 0
const download = cardProps.data?.download || 0 const download = cardProps.data?.download || 0
// 避免两者都为0的情况 // 避免两者都为0的情况
if (upload === 0 && download === 0) return 1 if (upload === 0 && download === 0) return 1
return Math.max(upload, download) return Math.max(upload, download)
}) })
@@ -164,112 +164,78 @@ onMounted(() => {
getSiteIcon() getSiteIcon()
getSiteStats() getSiteStats()
}) })
// 在 script 部分添加计算速度等级的函数
function calcSpeedClass(speed: number) {
if (speed === undefined || speed <= 0) return 'speed-idle';
if (speed < 50 * 1024) return 'speed-low';
if (speed < 500 * 1024) return 'speed-medium';
return 'speed-high';
}
</script> </script>
<template> <template>
<div> <div>
<div <VCard
class="site-card relative h-full flex flex-col overflow-hidden group" class="site-card relative h-full flex flex-col overflow-hidden group"
:class="[ :class="[
cardProps.site?.is_active ? '' : 'inactive', cardProps.site?.is_active ? '' : 'inactive',
{ {
'status-error': statColor === 'error', 'status-error': statColor === 'error',
'status-warning': statColor === 'warning', 'status-warning': statColor === 'warning',
'status-success': statColor === 'success' 'status-success': statColor === 'success',
} },
]" ]"
:ripple="false"
@click="handleResourceBrowse" @click="handleResourceBrowse"
> >
<!-- 装饰性状态指示器 --> <!-- 装饰性状态指示器 -->
<div <div v-if="cardProps.site?.is_active" class="site-status-indicator" :class="statColor"></div>
v-if="cardProps.site?.is_active"
class="site-status-indicator"
:class="statColor"
></div>
<!-- 主体部分 --> <!-- 主体部分 -->
<div class="site-card-content relative flex-1 flex flex-col"> <div class="site-card-content relative flex-1 flex flex-col">
<!-- 顶部图标和站点名称 --> <!-- 顶部图标和站点名称 -->
<div class="flex items-center mb-1"> <div class="flex items-center mb-1">
<!-- 站点图标 --> <!-- 站点图标 -->
<div <div class="site-icon-container mr-2.5" @click.stop="siteEditDialog = true">
class="site-icon-container mr-2.5" <img :src="siteIcon" class="site-icon" :alt="cardProps.site?.name" />
@click.stop="siteEditDialog = true"
>
<img
:src="siteIcon"
class="site-icon"
:alt="cardProps.site?.name"
/>
<div class="site-icon-edit-overlay"> <div class="site-icon-edit-overlay">
<VIcon icon="mdi-pencil" color="white" size="16" /> <VIcon icon="mdi-pencil" color="white" size="16" />
</div> </div>
</div> </div>
<!-- 拖动图标 --> <!-- 拖动图标 -->
<VIcon icon="mdi-drag" size="16" class="drag-handle cursor-move opacity-40 mr-1.5 z-10" /> <VIcon icon="mdi-drag" size="20" class="drag-handle cursor-move opacity-40 mr-1.5 z-10" />
<!-- 站点名称和特性图标 --> <!-- 站点名称和特性图标 -->
<div class="flex-1 min-w-0 flex items-center"> <div class="flex-1 min-w-0 flex items-center">
<h3 class="site-title truncate">{{ cardProps.site?.name }}</h3> <h3 class="site-title truncate">{{ cardProps.site?.name }}</h3>
<!-- 站点特性图标 --> <!-- 站点特性图标 -->
<div class="site-features flex items-center gap-1 ml-auto"> <div class="site-features flex items-center gap-1 ml-auto">
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<div v-if="cardProps.site?.limit_interval" v-bind="props" class="feature-icon-wrapper"> <div v-if="cardProps.site?.limit_interval" v-bind="props" class="feature-icon-wrapper">
<VIcon <VIcon icon="mdi-speedometer" size="16" class="site-feature-icon" />
icon="mdi-speedometer"
size="16"
class="site-feature-icon"
/>
</div> </div>
</template> </template>
<span>流控</span> <span>流控</span>
</VTooltip> </VTooltip>
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<div v-if="cardProps.site?.proxy === 1" v-bind="props" class="feature-icon-wrapper"> <div v-if="cardProps.site?.proxy === 1" v-bind="props" class="feature-icon-wrapper">
<VIcon <VIcon icon="mdi-network-outline" size="16" class="site-feature-icon" />
icon="mdi-network-outline"
size="16"
class="site-feature-icon"
/>
</div> </div>
</template> </template>
<span>代理</span> <span>代理</span>
</VTooltip> </VTooltip>
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<div v-if="cardProps.site?.render === 1" v-bind="props" class="feature-icon-wrapper"> <div v-if="cardProps.site?.render === 1" v-bind="props" class="feature-icon-wrapper">
<VIcon <VIcon icon="mdi-apple-safari" size="16" class="site-feature-icon" />
icon="mdi-apple-safari"
size="16"
class="site-feature-icon"
/>
</div> </div>
</template> </template>
<span>仿真</span> <span>仿真</span>
</VTooltip> </VTooltip>
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<div v-if="cardProps.site?.filter" v-bind="props" class="feature-icon-wrapper"> <div v-if="cardProps.site?.filter" v-bind="props" class="feature-icon-wrapper">
<VIcon <VIcon icon="mdi-filter-cog-outline" size="16" class="site-feature-icon" />
icon="mdi-filter-cog-outline"
size="16"
class="site-feature-icon"
/>
</div> </div>
</template> </template>
<span>过滤</span> <span>过滤</span>
@@ -277,14 +243,14 @@ function calcSpeedClass(speed: number) {
</div> </div>
</div> </div>
</div> </div>
<!-- 中间部分网址 --> <!-- 中间部分网址 -->
<div class="site-meta mb-1.5"> <div class="site-meta mb-1.5">
<div class="site-url truncate" @click.stop="openSitePage"> <div class="site-url truncate" @click.stop="openSitePage">
{{ cardProps.site?.url }} {{ cardProps.site?.url }}
</div> </div>
</div> </div>
<!-- 底部数据统计 --> <!-- 底部数据统计 -->
<div class="site-stats flex-1 flex flex-col justify-end"> <div class="site-stats flex-1 flex flex-col justify-end">
<!-- 更直观的上传下载数据条 --> <!-- 更直观的上传下载数据条 -->
@@ -296,15 +262,12 @@ function calcSpeedClass(speed: number) {
<span>{{ formatFileSize(cardProps.data?.upload || 0) }}</span> <span>{{ formatFileSize(cardProps.data?.upload || 0) }}</span>
</div> </div>
<div class="data-progress-bar"> <div class="data-progress-bar">
<div <div class="progress-filled upload-filled" :style="`width: ${getUploadPercent}%`">
class="progress-filled upload-filled"
:style="`width: ${getUploadPercent}%`"
>
<div class="progress-glow"></div> <div class="progress-glow"></div>
</div> </div>
</div> </div>
</div> </div>
<!-- 下载数据 --> <!-- 下载数据 -->
<div class="data-row download-row"> <div class="data-row download-row">
<div class="data-label"> <div class="data-label">
@@ -312,10 +275,7 @@ function calcSpeedClass(speed: number) {
<span>{{ formatFileSize(cardProps.data?.download || 0) }}</span> <span>{{ formatFileSize(cardProps.data?.download || 0) }}</span>
</div> </div>
<div class="data-progress-bar"> <div class="data-progress-bar">
<div <div class="progress-filled download-filled" :style="`width: ${getDownloadPercent}%`">
class="progress-filled download-filled"
:style="`width: ${getDownloadPercent}%`"
>
<div class="progress-glow"></div> <div class="progress-glow"></div>
</div> </div>
</div> </div>
@@ -323,16 +283,16 @@ function calcSpeedClass(speed: number) {
</div> </div>
</div> </div>
</div> </div>
<!-- 右侧操作按钮区 --> <!-- 右侧操作按钮区 -->
<div class="site-card-actions"> <div class="site-card-actions">
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<button <button
v-bind="props" v-bind="props"
class="site-action-btn test-btn" class="site-action-btn test-btn"
@click.stop="testSite" @click.stop="testSite"
:class="{'testing': testButtonDisable}" :class="{ 'testing': testButtonDisable }"
> >
<div class="test-btn-content"> <div class="test-btn-content">
<div class="pulse-dot" :class="statColor"></div> <div class="pulse-dot" :class="statColor"></div>
@@ -348,39 +308,28 @@ function calcSpeedClass(speed: number) {
</template> </template>
<span>测试站点连通性</span> <span>测试站点连通性</span>
</VTooltip> </VTooltip>
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<button <button v-bind="props" class="site-action-btn" @click.stop="handleSiteUserData">
v-bind="props"
class="site-action-btn"
@click.stop="handleSiteUserData"
>
<VIcon icon="mdi-chart-bell-curve" size="18" /> <VIcon icon="mdi-chart-bell-curve" size="18" />
</button> </button>
</template> </template>
<span>查看站点数据</span> <span>查看站点数据</span>
</VTooltip> </VTooltip>
<VTooltip v-if="!cardProps.site?.public"> <VTooltip v-if="!cardProps.site?.public">
<template #activator="{ props }"> <template #activator="{ props }">
<button <button v-bind="props" class="site-action-btn" @click.stop="handleSiteUpdate">
v-bind="props"
class="site-action-btn"
@click.stop="handleSiteUpdate"
>
<VIcon icon="mdi-refresh" size="18" /> <VIcon icon="mdi-refresh" size="18" />
</button> </button>
</template> </template>
<span>更新Cookie/UA</span> <span>更新Cookie/UA</span>
</VTooltip> </VTooltip>
<VTooltip> <VTooltip>
<template #activator="{ props }"> <template #activator="{ props }">
<button <button v-bind="props" class="site-action-btn more-btn">
v-bind="props"
class="site-action-btn more-btn"
>
<VIcon icon="mdi-dots-vertical" size="18" /> <VIcon icon="mdi-dots-vertical" size="18" />
<VMenu activator="parent" close-on-content-click location="left"> <VMenu activator="parent" close-on-content-click location="left">
<VList density="compact" nav class="dropdown-menu"> <VList density="compact" nav class="dropdown-menu">
@@ -403,8 +352,8 @@ function calcSpeedClass(speed: number) {
<span>更多操作</span> <span>更多操作</span>
</VTooltip> </VTooltip>
</div> </div>
</div> </VCard>
<!-- 对话框组件 --> <!-- 对话框组件 -->
<SiteCookieUpdateDialog <SiteCookieUpdateDialog
v-if="siteCookieDialog" v-if="siteCookieDialog"
@@ -528,7 +477,7 @@ function calcSpeedClass(speed: number) {
.data-label { .data-label {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 12px; font-size: 0.8rem;
color: rgba(var(--v-theme-on-surface), 0.8); color: rgba(var(--v-theme-on-surface), 0.8);
min-width: 70px; min-width: 70px;
} }
@@ -577,7 +526,8 @@ function calcSpeedClass(speed: number) {
} }
@keyframes pulse-width { @keyframes pulse-width {
0%, 100% { 0%,
100% {
opacity: 0.85; opacity: 0.85;
} }
50% { 50% {
@@ -617,7 +567,8 @@ function calcSpeedClass(speed: number) {
} }
@keyframes pulse-width { @keyframes pulse-width {
0%, 100% { 0%,
100% {
transform: scaleX(0.95); transform: scaleX(0.95);
} }
50% { 50% {
@@ -673,14 +624,14 @@ function calcSpeedClass(speed: number) {
/* 站点标题 */ /* 站点标题 */
.site-title { .site-title {
font-size: 15px; font-size: 1.1rem;
font-weight: 600; font-weight: 600;
line-height: 1.2; line-height: 1.2;
} }
/* 站点网址 */ /* 站点网址 */
.site-url { .site-url {
font-size: 11px; font-size: 0.9rem;
color: rgba(var(--v-theme-on-surface), 0.6); color: rgba(var(--v-theme-on-surface), 0.6);
transition: color 0.2s ease; transition: color 0.2s ease;
cursor: pointer; cursor: pointer;
@@ -840,8 +791,12 @@ function calcSpeedClass(speed: number) {
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% {
100% { transform: rotate(360deg); } transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
} }
.loading-text { .loading-text {
@@ -855,8 +810,12 @@ function calcSpeedClass(speed: number) {
} }
@keyframes fade-in { @keyframes fade-in {
from { opacity: 0; } from {
to { opacity: 1; } opacity: 0;
}
to {
opacity: 1;
}
} }
.pulse-dot { .pulse-dot {