优化首页账号展示体验

This commit is contained in:
yml2213
2026-06-04 12:57:34 +08:00
parent 75e849a91a
commit e41d3a923a
6 changed files with 157 additions and 23 deletions
+2
View File
@@ -17,6 +17,8 @@ declare module 'vue' {
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElDialog: typeof import('element-plus/es')['ElDialog']
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { Bell } from '@element-plus/icons-vue'
import { ElCarousel, ElCarouselItem, ElIcon } from 'element-plus'
interface Props {
announcements: string[]
@@ -32,6 +31,7 @@ defineProps<Props>()
align-items: center;
gap: 12px;
padding: 10px 20px;
min-width: 0;
color: #854d0e;
background: #fefce8;
border: 1px solid #fef08a;
@@ -41,5 +41,18 @@ defineProps<Props>()
.home-announcement :deep(.el-carousel) {
flex: 1;
min-width: 0;
}
.home-announcement :deep(.el-carousel__container) {
overflow: hidden;
}
.home-announcement span {
display: block;
overflow: hidden;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
@@ -1,6 +1,4 @@
<script setup lang="ts">
import { ElCarousel, ElCarouselItem } from 'element-plus'
interface HomeBannerSlide {
title?: string
eyebrow?: string
@@ -47,6 +45,20 @@ defineProps<Props>()
min-width: 0;
border-radius: 16px;
overflow: hidden;
background: #ffffff;
}
.hero-board :deep(.el-carousel) {
min-width: 0;
}
.hero-board :deep(.el-carousel__container) {
height: 240px;
overflow: hidden;
}
.hero-board :deep(.el-carousel__item) {
height: 100%;
}
.hero-slide {
@@ -55,7 +67,13 @@ defineProps<Props>()
display: flex;
align-items: center;
padding: 40px;
background: #f8fafc;
overflow: hidden;
color: #17233d;
background: linear-gradient(135deg, #eff6ff 0%, #ffffff 58%, #fff7ed 100%);
}
.hero-slide.tone-blue {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 56%, #fff7ed 100%);
}
.hero-slide.tone-orange {
@@ -77,7 +95,7 @@ defineProps<Props>()
content: '';
position: absolute;
inset: 0;
background: linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, transparent 60%);
background: linear-gradient(90deg, rgba(15, 23, 42, 0.74) 0%, rgba(15, 23, 42, 0.24) 62%, transparent 100%);
}
.hero-copy {
@@ -86,22 +104,50 @@ defineProps<Props>()
max-width: 480px;
}
.hero-copy span {
color: #ff6a00;
font-size: 13px;
font-weight: 900;
}
.hero-slide.has-image .hero-copy {
color: #ffffff;
}
.hero-slide.has-image .hero-copy span {
color: #fed7aa;
}
.hero-copy h1 {
margin: 12px 0;
font-size: 32px;
font-weight: 900;
line-height: 1.2;
}
.hero-copy p {
display: inline-block;
margin: 0;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.2);
color: #1e293b;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(4px);
font-size: 14px;
font-weight: 800;
}
.hero-slide em {
position: absolute;
top: 22px;
right: 24px;
z-index: 1;
padding: 6px 10px;
border-radius: 999px;
color: #ffffff;
background: #ff6a00;
font-size: 12px;
font-style: normal;
font-weight: 900;
}
</style>
@@ -1,6 +1,4 @@
<script setup lang="ts">
import { ElRadioGroup, ElRadioButton } from 'element-plus'
interface ZoneOption {
key: string
label: string
@@ -24,6 +22,10 @@ const emit = defineEmits<{
function selectZone(key: string) {
emit('update:activeZone', key)
}
function updateSort(value: string | number | boolean) {
emit('update:sortBy', String(value))
}
</script>
<template>
@@ -45,7 +47,7 @@ function selectZone(key: string) {
</button>
</div>
<div class="list-actions">
<el-radio-group :model-value="sortBy" size="small" @update:model-value="(val) => emit('update:sortBy', val as string)">
<el-radio-group :model-value="sortBy" size="small" @update:model-value="updateSort">
<el-radio-button label="recommended">综合推荐</el-radio-button>
<el-radio-button label="coinDesc">哈夫币</el-radio-button>
<el-radio-button label="awmDesc">AWM数量</el-radio-button>
@@ -58,22 +60,27 @@ function selectZone(key: string) {
<style scoped>
.zones-and-sort {
display: flex;
flex-direction: column;
gap: 16px;
display: grid;
grid-template-columns: minmax(0, 1fr) max-content;
gap: 14px;
align-items: end;
min-width: 0;
}
.zone-tabs {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 12px;
grid-template-columns: repeat(6, minmax(108px, 1fr));
gap: 10px;
min-width: 0;
}
.zone-tab {
display: flex;
flex-direction: column;
gap: 6px;
padding: 14px;
min-width: 0;
min-height: 72px;
padding: 12px 14px;
border: 2px solid #e2e8f0;
border-radius: 12px;
background: #ffffff;
@@ -99,9 +106,12 @@ function selectZone(key: string) {
}
.zone-tab strong {
overflow: hidden;
font-size: 15px;
font-weight: 700;
color: #17233d;
text-overflow: ellipsis;
white-space: nowrap;
}
.zone-count {
@@ -111,12 +121,39 @@ function selectZone(key: string) {
}
.zone-tab small {
overflow: hidden;
font-size: 11px;
color: #7b8798;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.list-actions {
display: flex;
justify-content: flex-end;
min-width: max-content;
padding-bottom: 2px;
}
.list-actions :deep(.el-radio-group) {
white-space: nowrap;
}
@media (max-width: 1380px) {
.zones-and-sort {
grid-template-columns: 1fr;
align-items: stretch;
}
.list-actions {
justify-content: flex-start;
}
}
@media (max-width: 900px) {
.zone-tabs {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
</style>
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { computed } from 'vue'
import { RouterLink } from 'vue-router'
import type { Listing } from '@/features/listings'
import {
@@ -24,18 +25,19 @@ interface Props {
}
const props = defineProps<Props>()
const coverURL = computed(() => props.listing.cover_url || props.listing.screenshot_urls?.[0] || '')
</script>
<template>
<RouterLink class="listing-card-v2" :to="`/listings/${listing.id}`">
<div class="card-cover">
<img
v-if="listing.cover_url"
:data-src="listing.cover_url"
v-if="coverURL"
:src="coverURL"
:alt="getListingTitle(listing)"
loading="lazy"
decoding="async"
class="lazy-image"
/>
<div v-else class="empty-cover">HFB</div>
<div class="cover-badges">
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElEmpty } from 'element-plus'
import {
defaultHomeAnnouncements,
defaultHomeBanners,
@@ -174,10 +173,11 @@ loadHome()
@update:sort-by="sortBy = $event"
/>
<el-empty
v-if="!loading && listings.length === 0"
description="没有符合条件的账号"
/>
<div v-if="!loading && listings.length === 0" class="home-empty-state">
<strong>没有符合条件的账号</strong>
<span>可以放宽筛选条件或切回全部专区</span>
<button type="button" @click="handleResetFilters">重置条件</button>
</div>
<div v-else v-loading="loading" class="enhanced-desktop-list">
<ListingCard
v-for="item in listings"
@@ -259,4 +259,38 @@ loadHome()
gap: 16px;
min-width: 0;
}
.home-empty-state {
display: grid;
place-items: center;
gap: 8px;
min-height: 148px;
padding: 28px;
border: 1px dashed #d8e1ee;
border-radius: 16px;
background: #ffffff;
color: #7b8798;
text-align: center;
}
.home-empty-state strong {
color: #17233d;
font-size: 18px;
}
.home-empty-state span {
font-size: 13px;
}
.home-empty-state button {
height: 34px;
margin-top: 4px;
padding: 0 16px;
border: 1px solid #ff6a00;
border-radius: 8px;
background: #fff7ed;
color: #ff6a00;
font-weight: 800;
cursor: pointer;
}
</style>