feat ui设计稿bug修复

This commit is contained in:
Ironsp
2026-10-01 10:53:13 +08:00
parent 9ab6253c81
commit 1601bdd82c
89 changed files with 4741 additions and 3652 deletions

View File

@@ -1,161 +1,157 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import type { BannerVo } from '@/types/business/home'
import { useRouter } from 'vue-router'
import bannerIllustration from '@/assets/home/banner-illustration.png'
interface Props {
banners: BannerVo[]
// 设计稿顶部为静态头图(非轮播、不走接口):底 #F3F4F8 渐变,
// 左侧标题 60px + 副标题 24px + 双按钮 183×55,右侧插画 723×598(蓝湖设计稿切图)
const router = useRouter()
function goDemand(): void {
router.push('/demand')
}
const props = defineProps<Props>()
/** 仅保留启用且带图的轮播项,按后端 sort 升序 */
const slides = computed(() =>
props.banners
.filter((item) => item.enabled !== false && !!item.imageUrl)
.slice()
.sort((prev, next) => (prev.sort ?? 0) - (next.sort ?? 0)),
)
const current = ref(0)
let timer: number | undefined
function stop(): void {
if (timer !== undefined) {
window.clearInterval(timer)
timer = undefined
}
function goCases(): void {
router.push('/cases')
}
function go(index: number): void {
const total = slides.value.length
if (total === 0) return
current.value = (index + total) % total
}
function start(): void {
stop()
if (slides.value.length <= 1) return
timer = window.setInterval(() => go(current.value + 1), 5000)
}
watch(
() => slides.value.length,
() => {
current.value = 0
start()
},
)
onMounted(start)
onBeforeUnmount(stop)
</script>
<template>
<section v-if="slides.length" class="home-banner">
<img
v-for="(slide, index) in slides"
v-show="index === current"
:key="slide.id ?? index"
class="home-banner__img"
:src="slide.imageUrl ?? ''"
:alt="`轮播图 ${index + 1}`"
>
<section class="home-banner">
<div class="home-banner__inner">
<h1 class="home-banner__title">虫子智造研发一体化平台</h1>
<p class="home-banner__subtitle">一站式智能软硬件研发与量产综合服务商</p>
<template v-if="slides.length > 1">
<button
class="home-banner__arrow home-banner__arrow--prev"
type="button"
aria-label="上一张"
@click="go(current - 1)"
>
‹
</button>
<button
class="home-banner__arrow home-banner__arrow--next"
type="button"
aria-label="下一张"
@click="go(current + 1)"
>
›
</button>
<!-- 设计稿底部居中的指示条(长条=当前,圆点=其余) -->
<div class="home-banner__dots">
<span
v-for="(slide, index) in slides"
:key="`dot-${slide.id ?? index}`"
class="home-banner__dot"
:class="{ 'is-active': index === current }"
/>
<div class="home-banner__actions">
<button class="home-banner__btn home-banner__btn--primary" type="button" @click="goDemand">
需求提报
</button>
<button class="home-banner__btn home-banner__btn--ghost" type="button" @click="goCases">
案例中心
</button>
</div>
</template>
<div class="home-banner__visual">
<img
class="home-banner__illustration"
:src="bannerIllustration"
alt="虫子智造研发一体化平台"
>
</div>
</div>
</section>
</template>
<style lang="less" scoped>
.home-banner {
// 设计稿头图通栏 1920×661(画板 y73,位于顶栏 72 之下)
position: relative;
// 设计稿头图通栏 1920×661 设计px → 高 509 CSS,紧贴顶栏无圆角
height: 509px;
height: 661px;
overflow: hidden;
background: @home-banner-bg;
// .home 是 column flex 容器,需显式占满整行,否则被内容宽度收缩
width: 100%;
flex: 0 0 auto;
&__img {
// 内容区按设计稿 1920 画布居中;两侧内容一律相对它定位,保证 1:1
&__inner {
position: relative;
width: 100%;
max-width: @container-width;
height: 100%;
margin: 0 auto;
}
// 设计稿标题 x150 y257(画板绝对)→ banner 内 y257−73=184
&__title {
position: absolute;
top: 184px;
left: 150px;
margin: 0;
color: @banner-title-color;
font-size: @font-size-hero-lg;
font-weight: 700;
line-height: 87px;
white-space: nowrap;
}
// 设计稿副标题 x150 y360 → banner 内 y287
&__subtitle {
position: absolute;
top: 287px;
left: 150px;
margin: 0;
color: @banner-title-color;
font-size: 24px;
font-weight: 500;
line-height: 35px;
white-space: nowrap;
}
// 设计稿按钮组 x150 y465 → banner 内 y392;两钮 183×55,间距 20(150+183=333 → 353)
&__actions {
position: absolute;
top: 392px;
left: 150px;
.flex(row, flex-start, center);
gap: 20px;
}
&__btn {
width: 183px;
height: 55px;
border-radius: 8px;
font-size: 18px;
font-weight: 500;
line-height: 1;
cursor: pointer;
&--primary {
background: linear-gradient(135deg, @btn-gradient-from 0%, @btn-gradient-to 100%);
border: none;
color: @white;
}
// 设计稿:2px 渐变描边 + 白底 + 蓝色文字(实测 #2B5FF0)
&--ghost {
position: relative;
background: @white;
border: none;
color: @btn-ghost-color;
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: 8px;
padding: 2px;
background: linear-gradient(135deg, @btn-gradient-from 0%, @btn-gradient-to 100%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
}
}
// 设计稿插画 723×598 @ canvas (928,104) → banner 内 y104−73=31
&__visual {
position: absolute;
top: 31px;
left: 928px;
width: 723px;
height: 598px;
}
&__illustration {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
&__arrow {
position: absolute;
top: 50%;
width: 32px;
height: 32px;
transform: translateY(-50%);
border-radius: @radius-round;
background: fade(@black, 35%);
color: @white;
font-size: @font-size-xl;
line-height: 1;
opacity: 0;
transition: opacity 0.2s;
&--prev {
left: @spacing-base;
}
&--next {
right: @spacing-base;
}
}
&:hover &__arrow {
opacity: 1;
}
&__dots {
position: absolute;
bottom: 48px;
left: 50%;
transform: translateX(-50%);
.flex(row, center, center);
gap: 10px;
}
&__dot {
width: 4px;
height: 4px;
border-radius: @radius-round;
background: @home-banner-dot;
transition: width 0.2s;
// 当前页:设计稿 44×5 设计px 长条
&.is-active {
width: 34px;
height: 4px;
border-radius: @radius-round;
}
object-fit: contain;
}
}
</style>