优化首页账号展示体验
This commit is contained in:
Vendored
+2
@@ -17,6 +17,8 @@ declare module 'vue' {
|
|||||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
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']
|
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Bell } from '@element-plus/icons-vue'
|
import { Bell } from '@element-plus/icons-vue'
|
||||||
import { ElCarousel, ElCarouselItem, ElIcon } from 'element-plus'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
announcements: string[]
|
announcements: string[]
|
||||||
@@ -32,6 +31,7 @@ defineProps<Props>()
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
min-width: 0;
|
||||||
color: #854d0e;
|
color: #854d0e;
|
||||||
background: #fefce8;
|
background: #fefce8;
|
||||||
border: 1px solid #fef08a;
|
border: 1px solid #fef08a;
|
||||||
@@ -41,5 +41,18 @@ defineProps<Props>()
|
|||||||
|
|
||||||
.home-announcement :deep(.el-carousel) {
|
.home-announcement :deep(.el-carousel) {
|
||||||
flex: 1;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElCarousel, ElCarouselItem } from 'element-plus'
|
|
||||||
|
|
||||||
interface HomeBannerSlide {
|
interface HomeBannerSlide {
|
||||||
title?: string
|
title?: string
|
||||||
eyebrow?: string
|
eyebrow?: string
|
||||||
@@ -47,6 +45,20 @@ defineProps<Props>()
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
overflow: hidden;
|
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 {
|
.hero-slide {
|
||||||
@@ -55,7 +67,13 @@ defineProps<Props>()
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 40px;
|
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 {
|
.hero-slide.tone-orange {
|
||||||
@@ -77,7 +95,7 @@ defineProps<Props>()
|
|||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
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 {
|
.hero-copy {
|
||||||
@@ -86,22 +104,50 @@ defineProps<Props>()
|
|||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-copy span {
|
||||||
|
color: #ff6a00;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-slide.has-image .hero-copy {
|
.hero-slide.has-image .hero-copy {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-slide.has-image .hero-copy span {
|
||||||
|
color: #fed7aa;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-copy h1 {
|
.hero-copy h1 {
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
|
font-weight: 900;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy p {
|
.hero-copy p {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
color: #1e293b;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
font-size: 14px;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElRadioGroup, ElRadioButton } from 'element-plus'
|
|
||||||
|
|
||||||
interface ZoneOption {
|
interface ZoneOption {
|
||||||
key: string
|
key: string
|
||||||
label: string
|
label: string
|
||||||
@@ -24,6 +22,10 @@ const emit = defineEmits<{
|
|||||||
function selectZone(key: string) {
|
function selectZone(key: string) {
|
||||||
emit('update:activeZone', key)
|
emit('update:activeZone', key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSort(value: string | number | boolean) {
|
||||||
|
emit('update:sortBy', String(value))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -45,7 +47,7 @@ function selectZone(key: string) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-actions">
|
<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="recommended">综合推荐</el-radio-button>
|
||||||
<el-radio-button label="coinDesc">哈夫币</el-radio-button>
|
<el-radio-button label="coinDesc">哈夫币</el-radio-button>
|
||||||
<el-radio-button label="awmDesc">AWM数量</el-radio-button>
|
<el-radio-button label="awmDesc">AWM数量</el-radio-button>
|
||||||
@@ -58,22 +60,27 @@ function selectZone(key: string) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.zones-and-sort {
|
.zones-and-sort {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: minmax(0, 1fr) max-content;
|
||||||
gap: 16px;
|
gap: 14px;
|
||||||
|
align-items: end;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zone-tabs {
|
.zone-tabs {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, 1fr);
|
grid-template-columns: repeat(6, minmax(108px, 1fr));
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zone-tab {
|
.zone-tab {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 14px;
|
min-width: 0;
|
||||||
|
min-height: 72px;
|
||||||
|
padding: 12px 14px;
|
||||||
border: 2px solid #e2e8f0;
|
border: 2px solid #e2e8f0;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
@@ -99,9 +106,12 @@ function selectZone(key: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.zone-tab strong {
|
.zone-tab strong {
|
||||||
|
overflow: hidden;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #17233d;
|
color: #17233d;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zone-count {
|
.zone-count {
|
||||||
@@ -111,12 +121,39 @@ function selectZone(key: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.zone-tab small {
|
.zone-tab small {
|
||||||
|
overflow: hidden;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #7b8798;
|
color: #7b8798;
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-actions {
|
.list-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import type { Listing } from '@/features/listings'
|
import type { Listing } from '@/features/listings'
|
||||||
import {
|
import {
|
||||||
@@ -24,18 +25,19 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const coverURL = computed(() => props.listing.cover_url || props.listing.screenshot_urls?.[0] || '')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<RouterLink class="listing-card-v2" :to="`/listings/${listing.id}`">
|
<RouterLink class="listing-card-v2" :to="`/listings/${listing.id}`">
|
||||||
<div class="card-cover">
|
<div class="card-cover">
|
||||||
<img
|
<img
|
||||||
v-if="listing.cover_url"
|
v-if="coverURL"
|
||||||
:data-src="listing.cover_url"
|
:src="coverURL"
|
||||||
:alt="getListingTitle(listing)"
|
:alt="getListingTitle(listing)"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
class="lazy-image"
|
|
||||||
/>
|
/>
|
||||||
<div v-else class="empty-cover">HFB</div>
|
<div v-else class="empty-cover">HFB</div>
|
||||||
<div class="cover-badges">
|
<div class="cover-badges">
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { ElEmpty } from 'element-plus'
|
|
||||||
import {
|
import {
|
||||||
defaultHomeAnnouncements,
|
defaultHomeAnnouncements,
|
||||||
defaultHomeBanners,
|
defaultHomeBanners,
|
||||||
@@ -174,10 +173,11 @@ loadHome()
|
|||||||
@update:sort-by="sortBy = $event"
|
@update:sort-by="sortBy = $event"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-empty
|
<div v-if="!loading && listings.length === 0" class="home-empty-state">
|
||||||
v-if="!loading && listings.length === 0"
|
<strong>没有符合条件的账号</strong>
|
||||||
description="没有符合条件的账号"
|
<span>可以放宽筛选条件或切回全部专区。</span>
|
||||||
/>
|
<button type="button" @click="handleResetFilters">重置条件</button>
|
||||||
|
</div>
|
||||||
<div v-else v-loading="loading" class="enhanced-desktop-list">
|
<div v-else v-loading="loading" class="enhanced-desktop-list">
|
||||||
<ListingCard
|
<ListingCard
|
||||||
v-for="item in listings"
|
v-for="item in listings"
|
||||||
@@ -259,4 +259,38 @@ loadHome()
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
min-width: 0;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user