Files
yeuimu 6c61a4e871 feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
2026-08-26 14:23:09 +08:00

78 lines
2.8 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
const activeFeature = ref(0);
const FEATURE_INTERVAL_MS = 2000;
const features = [
{ title: '设计工具', desc: '轻松设计,快速定制', image: '/设计工具.png' },
{ title: '自动化履约', desc: '从生产到配送,全程无忧,准时交付', image: '/自动化履约.png' },
{ title: '全球配送', desc: '全球20+国家本土工厂', image: '/全球配送.png' },
{ title: '店铺集成', desc: '与Shopify、Amazon等平台无缝对接', image: '/店铺集成.png' },
];
const selectFeature = (idx: number) => {
activeFeature.value = idx;
};
let featureTimer: ReturnType<typeof setInterval> | null = null;
onMounted(() => {
featureTimer = setInterval(() => {
activeFeature.value = (activeFeature.value + 1) % features.length;
}, FEATURE_INTERVAL_MS);
});
onBeforeUnmount(() => {
if (featureTimer) clearInterval(featureTimer);
});
</script>
<template>
<section class="feature-section w-full flex flex-col gap-8 lg:gap-10 items-center p-6 lg:p-10">
<div class="text-[48px] font-extrabold md:text-4xl lg:text-5xl text-center">用强大功能开启盈利快车道</div>
<div class="flex flex-col lg:flex-row gap-5 w-full max-w-7xl items-stretch">
<div class="feature-list flex flex-row gap-3 overflow-x-auto lg:overflow-visible">
<div
v-for="(feat, idx) in features"
:key="idx"
@click="selectFeature(idx)"
@mouseenter="activeFeature = idx"
class="flex-shrink-0 flex flex-col justify-center gap-2 lg:gap-4 pl-4 lg:pl-5 w-40 lg:w-100 h-20 lg:h-auto rounded-lg cursor-pointer transition-colors duration-300"
:class="activeFeature === idx ? 'bg-primary text-white' : 'bg-inkreach-homepage-2'"
>
<p class="feature-item-title text-xl lg:text-3xl">{{ feat.title }}</p>
<p class="text-sm lg:text-base indent-1">{{ feat.desc }}</p>
</div>
</div>
<div class="flex-1 overflow-hidden rounded-xl relative">
<img
v-for="(feat, idx) in features"
:key="'feat-img-' + idx"
:src="feat.image"
:alt="feat.title"
:data-testid="`feature-image-${idx}`"
class="w-full h-full object-contain transition-opacity duration-500 absolute inset-0"
:class="activeFeature === idx ? 'opacity-100' : 'opacity-0 pointer-events-none'"
/>
</div>
</div>
</section>
</template>
<style scoped>
@media (min-width: 1024px) {
.feature-section { height: 754px; padding-top: 54px; }
.feature-section > div:nth-child(2) { height: 550px; }
.feature-list {
display: grid;
grid-template-rows: repeat(4, minmax(0, 1fr));
width: 400px;
height: 100%;
gap: 12px;
}
}
</style>