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
This commit is contained in:
@@ -1,348 +1,348 @@
|
||||
<script setup lang="ts">
|
||||
import type { RecommendCategory, SolutionColumn, NavBanner } from '~/composables/useNavData';
|
||||
|
||||
const PORTAL_URL = 'https://inkpod.vip/portal/search';
|
||||
const SHOW_EXTENDED_NAV = false;
|
||||
|
||||
const mobileMenuOpen = ref(false);
|
||||
const recommendMenuOpen = ref(false);
|
||||
const solutionMenuOpen = ref(false);
|
||||
const mobileRecommendOpen = ref(false);
|
||||
const mobileSolutionOpen = ref(false);
|
||||
const mobileRecommendTab = ref(0);
|
||||
|
||||
const {
|
||||
recommendCategories,
|
||||
solutionColumns,
|
||||
solutionBanner,
|
||||
activeRecommendIdx,
|
||||
setActiveRecommend,
|
||||
} = useNavData();
|
||||
|
||||
let recommendTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let solutionTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function openRecommendMenu(): void {
|
||||
if (recommendTimeout) {
|
||||
clearTimeout(recommendTimeout);
|
||||
recommendTimeout = null;
|
||||
}
|
||||
recommendMenuOpen.value = true;
|
||||
}
|
||||
|
||||
function closeRecommendMenu(): void {
|
||||
recommendTimeout = setTimeout(() => {
|
||||
recommendMenuOpen.value = false;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function openSolutionMenu(): void {
|
||||
if (solutionTimeout) {
|
||||
clearTimeout(solutionTimeout);
|
||||
solutionTimeout = null;
|
||||
}
|
||||
solutionMenuOpen.value = true;
|
||||
}
|
||||
|
||||
function closeSolutionMenu(): void {
|
||||
solutionTimeout = setTimeout(() => {
|
||||
solutionMenuOpen.value = false;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function toggleMobileMenu(): void {
|
||||
mobileMenuOpen.value = !mobileMenuOpen.value;
|
||||
}
|
||||
|
||||
function closeMobileMenu(): void {
|
||||
mobileMenuOpen.value = false;
|
||||
mobileRecommendOpen.value = false;
|
||||
mobileSolutionOpen.value = false;
|
||||
}
|
||||
|
||||
function goToLogin(): void {
|
||||
window.location.href = 'https://inkpod.vip/user/login';
|
||||
}
|
||||
|
||||
function toggleMobileRecommend(): void {
|
||||
mobileRecommendOpen.value = !mobileRecommendOpen.value;
|
||||
mobileSolutionOpen.value = false;
|
||||
}
|
||||
|
||||
function toggleMobileSolution(): void {
|
||||
mobileSolutionOpen.value = !mobileSolutionOpen.value;
|
||||
mobileRecommendOpen.value = false;
|
||||
}
|
||||
|
||||
const mobileActiveCategory = computed(
|
||||
() => recommendCategories.value[mobileRecommendTab.value] ?? null,
|
||||
);
|
||||
|
||||
const route = useRoute();
|
||||
const isProductCenter = computed(() => route.path === '/product-center');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="pl-6 md:pl-10 lg:pl-16 pr-6 md:pr-10 lg:pr-20 flex items-center h-20 fixed top-0 left-0 right-0 z-50 bg-white border-b border-gray-100">
|
||||
<NuxtLink to="/" aria-label="返回首页">
|
||||
<img src="/logo-unified.svg" alt="Inkreach" class="h-12 w-[83px] lg:h-[56px] lg:w-[97px] object-contain" />
|
||||
</NuxtLink>
|
||||
|
||||
<nav class="desktop-nav hidden lg:flex gap-6 flex-1 ml-8 items-center h-20">
|
||||
<NuxtLink to="/product-center"
|
||||
class="transition-colors"
|
||||
:class="isProductCenter
|
||||
? 'text-black font-medium'
|
||||
: 'text-gray-400 hover:text-primary hover:underline underline-offset-14 decoration-primary decoration-2'"
|
||||
>产品中心</NuxtLink>
|
||||
<a :href="PORTAL_URL" target="_blank"
|
||||
class="text-gray-400 hover:text-primary hover:underline underline-offset-14 decoration-primary decoration-2 transition-colors"
|
||||
>在线设计</a>
|
||||
|
||||
<div
|
||||
v-if="SHOW_EXTENDED_NAV"
|
||||
class="relative h-full flex items-center"
|
||||
@mouseenter="openRecommendMenu"
|
||||
@mouseleave="closeRecommendMenu"
|
||||
>
|
||||
<button
|
||||
class="transition-colors flex items-center gap-1"
|
||||
:class="[
|
||||
recommendMenuOpen ? 'text-primary' : '',
|
||||
!recommendMenuOpen ? 'text-gray-400' : '',
|
||||
]"
|
||||
>
|
||||
<span class="hover:underline underline-offset-14 decoration-primary decoration-2">选品推荐</span>
|
||||
<i class="fa-solid fa-chevron-down text-xs transition-transform duration-200" :class="recommendMenuOpen ? 'rotate-180' : ''"></i>
|
||||
</button>
|
||||
<NavMegaMenu
|
||||
:categories="recommendCategories"
|
||||
:active-index="activeRecommendIdx"
|
||||
:visible="recommendMenuOpen"
|
||||
@update:active-index="setActiveRecommend"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="SHOW_EXTENDED_NAV"
|
||||
class="relative h-full flex items-center"
|
||||
@mouseenter="openSolutionMenu"
|
||||
@mouseleave="closeSolutionMenu"
|
||||
>
|
||||
<button
|
||||
class="transition-colors flex items-center gap-1"
|
||||
:class="[
|
||||
solutionMenuOpen ? 'text-primary' : '',
|
||||
!solutionMenuOpen ? 'text-gray-400' : '',
|
||||
]"
|
||||
>
|
||||
<span class="hover:underline underline-offset-14 decoration-primary decoration-2">解决方案</span>
|
||||
<i class="fa-solid fa-chevron-down text-xs transition-transform duration-200" :class="solutionMenuOpen ? 'rotate-180' : ''"></i>
|
||||
</button>
|
||||
<NavColumnMenu
|
||||
:columns="solutionColumns"
|
||||
:banner="solutionBanner"
|
||||
:visible="solutionMenuOpen"
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="desktop-nav hidden lg:flex gap-6 items-center">
|
||||
<button type="button" @click="goToLogin"
|
||||
class="hover:text-primary hover:underline underline-offset-12 decoration-primary decoration-2 transition-colors"
|
||||
>登录</button>
|
||||
<button type="button" @click="goToLogin"
|
||||
class="text-white rounded-lg px-6 py-1.5 self-center hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1 pulse-animation bg-primary"
|
||||
>立即开始</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="lg:hidden ml-auto p-2"
|
||||
@click="toggleMobileMenu"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<i v-if="!mobileMenuOpen" class="fa-solid fa-bars text-2xl"></i>
|
||||
<i v-else class="fa-solid fa-xmark text-2xl"></i>
|
||||
</button>
|
||||
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||
@click="closeMobileMenu"
|
||||
></div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
|
||||
<Transition name="slide">
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
class="fixed top-0 right-0 h-full w-80 bg-white shadow-2xl z-50 lg:hidden flex flex-col overflow-y-auto"
|
||||
>
|
||||
<div class="flex justify-between items-center p-6 pb-4">
|
||||
<NuxtLink to="/" aria-label="返回首页" @click="closeMobileMenu">
|
||||
<img src="/logo-unified.svg" alt="Inkreach" class="h-[56px] w-[97px] object-contain" />
|
||||
</NuxtLink>
|
||||
<button @click="closeMobileMenu" class="p-2" aria-label="Close menu">
|
||||
<i class="fa-solid fa-xmark text-2xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav class="flex flex-col px-6 gap-1 text-lg">
|
||||
<NuxtLink
|
||||
to="/product-center"
|
||||
class="py-3 hover:text-primary transition-colors"
|
||||
@click="closeMobileMenu"
|
||||
>产品中心</NuxtLink>
|
||||
<a :href="PORTAL_URL" target="_blank"
|
||||
class="py-3 hover:text-primary transition-colors"
|
||||
>在线设计</a>
|
||||
|
||||
<!-- Temporarily hidden until recommendation content is ready. -->
|
||||
<div v-if="SHOW_EXTENDED_NAV">
|
||||
<button
|
||||
class="w-full py-3 flex items-center justify-between hover:text-primary transition-colors"
|
||||
@click="toggleMobileRecommend"
|
||||
>
|
||||
<span>选品推荐</span>
|
||||
<i
|
||||
class="fa-solid fa-chevron-down text-xs transition-transform duration-200"
|
||||
:class="mobileRecommendOpen ? 'rotate-180' : ''"
|
||||
></i>
|
||||
</button>
|
||||
<Transition name="accordion">
|
||||
<div v-if="mobileRecommendOpen" class="pl-3 pb-2">
|
||||
<div class="flex gap-2 mb-3 flex-wrap">
|
||||
<button
|
||||
v-for="(cat, idx) in recommendCategories"
|
||||
:key="cat.id"
|
||||
class="px-3 py-1.5 rounded-full text-sm transition-colors"
|
||||
:class="mobileRecommendTab === idx
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-inkreach-homepage-1 text-gray-600'"
|
||||
@click="mobileRecommendTab = idx"
|
||||
>
|
||||
{{ cat.title }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<a
|
||||
v-for="item in mobileActiveCategory?.items ?? []"
|
||||
:key="item.id"
|
||||
:href="item.link"
|
||||
target="_blank"
|
||||
class="group block rounded-lg overflow-hidden"
|
||||
>
|
||||
<div class="w-full h-[70px] rounded-lg overflow-hidden bg-inkreach-homepage-1">
|
||||
<img
|
||||
:src="item.image"
|
||||
:alt="item.title"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-[13px] text-gray-700 group-hover:text-primary truncate">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Temporarily hidden until solution content is ready. -->
|
||||
<div v-if="SHOW_EXTENDED_NAV">
|
||||
<button
|
||||
class="w-full py-3 flex items-center justify-between hover:text-primary transition-colors"
|
||||
@click="toggleMobileSolution"
|
||||
>
|
||||
<span>解决方案</span>
|
||||
<i
|
||||
class="fa-solid fa-chevron-down text-xs transition-transform duration-200"
|
||||
:class="mobileSolutionOpen ? 'rotate-180' : ''"
|
||||
></i>
|
||||
</button>
|
||||
<Transition name="accordion">
|
||||
<div v-if="mobileSolutionOpen" class="pl-3 pb-2">
|
||||
<div
|
||||
v-for="col in solutionColumns"
|
||||
:key="col.id"
|
||||
class="mb-3"
|
||||
>
|
||||
<h4 class="text-[15px] font-medium text-gray-800 mb-2">
|
||||
{{ col.title }}
|
||||
</h4>
|
||||
<ul class="space-y-1.5">
|
||||
<li v-for="link in col.links" :key="link.id">
|
||||
<a
|
||||
:href="link.link"
|
||||
target="_blank"
|
||||
class="text-[14px] text-gray-600 hover:text-primary transition-colors flex items-center gap-1"
|
||||
>
|
||||
{{ link.title }}
|
||||
<span
|
||||
v-if="link.tag === 'hot'"
|
||||
class="text-[10px] text-white bg-red-500 px-1 py-0.5 rounded leading-none"
|
||||
>HOT</span>
|
||||
<span
|
||||
v-if="link.tag === 'new'"
|
||||
class="text-[10px] text-white bg-green-500 px-1 py-0.5 rounded leading-none"
|
||||
>NEW</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex flex-col gap-4 px-6 mt-auto pb-6 pt-4">
|
||||
<button type="button" @click="goToLogin"
|
||||
class="py-2.5 text-lg hover:text-primary transition-colors text-center"
|
||||
>登录</button>
|
||||
<button type="button" @click="goToLogin"
|
||||
class="text-white rounded-lg px-6 py-3 text-center hover:shadow-lg transition-all duration-300 pulse-animation bg-primary"
|
||||
>立即开始</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.desktop-nav { font-size: 18px; }
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.accordion-enter-active,
|
||||
.accordion-leave-active {
|
||||
transition: all 0.25s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.accordion-enter-from,
|
||||
.accordion-leave-to {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
.accordion-enter-to,
|
||||
.accordion-leave-from {
|
||||
max-height: 600px;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import type { RecommendCategory, SolutionColumn, NavBanner } from '~/composables/useNavData';
|
||||
|
||||
const PORTAL_URL = 'https://inkpod.vip/portal/search';
|
||||
const SHOW_EXTENDED_NAV = false;
|
||||
|
||||
const mobileMenuOpen = ref(false);
|
||||
const recommendMenuOpen = ref(false);
|
||||
const solutionMenuOpen = ref(false);
|
||||
const mobileRecommendOpen = ref(false);
|
||||
const mobileSolutionOpen = ref(false);
|
||||
const mobileRecommendTab = ref(0);
|
||||
|
||||
const {
|
||||
recommendCategories,
|
||||
solutionColumns,
|
||||
solutionBanner,
|
||||
activeRecommendIdx,
|
||||
setActiveRecommend,
|
||||
} = useNavData();
|
||||
|
||||
let recommendTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let solutionTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function openRecommendMenu(): void {
|
||||
if (recommendTimeout) {
|
||||
clearTimeout(recommendTimeout);
|
||||
recommendTimeout = null;
|
||||
}
|
||||
recommendMenuOpen.value = true;
|
||||
}
|
||||
|
||||
function closeRecommendMenu(): void {
|
||||
recommendTimeout = setTimeout(() => {
|
||||
recommendMenuOpen.value = false;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function openSolutionMenu(): void {
|
||||
if (solutionTimeout) {
|
||||
clearTimeout(solutionTimeout);
|
||||
solutionTimeout = null;
|
||||
}
|
||||
solutionMenuOpen.value = true;
|
||||
}
|
||||
|
||||
function closeSolutionMenu(): void {
|
||||
solutionTimeout = setTimeout(() => {
|
||||
solutionMenuOpen.value = false;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function toggleMobileMenu(): void {
|
||||
mobileMenuOpen.value = !mobileMenuOpen.value;
|
||||
}
|
||||
|
||||
function closeMobileMenu(): void {
|
||||
mobileMenuOpen.value = false;
|
||||
mobileRecommendOpen.value = false;
|
||||
mobileSolutionOpen.value = false;
|
||||
}
|
||||
|
||||
function goToLogin(): void {
|
||||
window.location.href = 'https://inkpod.vip/user/login';
|
||||
}
|
||||
|
||||
function toggleMobileRecommend(): void {
|
||||
mobileRecommendOpen.value = !mobileRecommendOpen.value;
|
||||
mobileSolutionOpen.value = false;
|
||||
}
|
||||
|
||||
function toggleMobileSolution(): void {
|
||||
mobileSolutionOpen.value = !mobileSolutionOpen.value;
|
||||
mobileRecommendOpen.value = false;
|
||||
}
|
||||
|
||||
const mobileActiveCategory = computed(
|
||||
() => recommendCategories.value[mobileRecommendTab.value] ?? null,
|
||||
);
|
||||
|
||||
const route = useRoute();
|
||||
const isProductCenter = computed(() => route.path === '/product-center');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="pl-6 md:pl-10 lg:pl-16 pr-6 md:pr-10 lg:pr-20 flex items-center h-20 fixed top-0 left-0 right-0 z-50 bg-white border-b border-gray-100">
|
||||
<NuxtLink to="/" aria-label="返回首页">
|
||||
<img src="/logo-unified.svg" alt="Inkreach" class="h-12 w-[83px] lg:h-[56px] lg:w-[97px] object-contain" />
|
||||
</NuxtLink>
|
||||
|
||||
<nav class="desktop-nav hidden lg:flex gap-6 flex-1 ml-8 items-center h-20">
|
||||
<NuxtLink to="/product-center"
|
||||
class="transition-colors"
|
||||
:class="isProductCenter
|
||||
? 'text-black font-medium'
|
||||
: 'text-gray-400 hover:text-primary hover:underline underline-offset-14 decoration-primary decoration-2'"
|
||||
>产品中心</NuxtLink>
|
||||
<a :href="PORTAL_URL" target="_blank"
|
||||
class="text-gray-400 hover:text-primary hover:underline underline-offset-14 decoration-primary decoration-2 transition-colors"
|
||||
>在线设计</a>
|
||||
|
||||
<div
|
||||
v-if="SHOW_EXTENDED_NAV"
|
||||
class="relative h-full flex items-center"
|
||||
@mouseenter="openRecommendMenu"
|
||||
@mouseleave="closeRecommendMenu"
|
||||
>
|
||||
<button
|
||||
class="transition-colors flex items-center gap-1"
|
||||
:class="[
|
||||
recommendMenuOpen ? 'text-primary' : '',
|
||||
!recommendMenuOpen ? 'text-gray-400' : '',
|
||||
]"
|
||||
>
|
||||
<span class="hover:underline underline-offset-14 decoration-primary decoration-2">选品推荐</span>
|
||||
<i class="fa-solid fa-chevron-down text-xs transition-transform duration-200" :class="recommendMenuOpen ? 'rotate-180' : ''"></i>
|
||||
</button>
|
||||
<NavMegaMenu
|
||||
:categories="recommendCategories"
|
||||
:active-index="activeRecommendIdx"
|
||||
:visible="recommendMenuOpen"
|
||||
@update:active-index="setActiveRecommend"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="SHOW_EXTENDED_NAV"
|
||||
class="relative h-full flex items-center"
|
||||
@mouseenter="openSolutionMenu"
|
||||
@mouseleave="closeSolutionMenu"
|
||||
>
|
||||
<button
|
||||
class="transition-colors flex items-center gap-1"
|
||||
:class="[
|
||||
solutionMenuOpen ? 'text-primary' : '',
|
||||
!solutionMenuOpen ? 'text-gray-400' : '',
|
||||
]"
|
||||
>
|
||||
<span class="hover:underline underline-offset-14 decoration-primary decoration-2">解决方案</span>
|
||||
<i class="fa-solid fa-chevron-down text-xs transition-transform duration-200" :class="solutionMenuOpen ? 'rotate-180' : ''"></i>
|
||||
</button>
|
||||
<NavColumnMenu
|
||||
:columns="solutionColumns"
|
||||
:banner="solutionBanner"
|
||||
:visible="solutionMenuOpen"
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="desktop-nav hidden lg:flex gap-6 items-center">
|
||||
<button type="button" @click="goToLogin"
|
||||
class="hover:text-primary hover:underline underline-offset-12 decoration-primary decoration-2 transition-colors"
|
||||
>登录</button>
|
||||
<button type="button" @click="goToLogin"
|
||||
class="text-white rounded-lg px-6 py-1.5 self-center hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1 pulse-animation bg-primary"
|
||||
>立即开始</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="lg:hidden ml-auto p-2"
|
||||
@click="toggleMobileMenu"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<i v-if="!mobileMenuOpen" class="fa-solid fa-bars text-2xl"></i>
|
||||
<i v-else class="fa-solid fa-xmark text-2xl"></i>
|
||||
</button>
|
||||
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||
@click="closeMobileMenu"
|
||||
></div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
|
||||
<Transition name="slide">
|
||||
<div
|
||||
v-if="mobileMenuOpen"
|
||||
class="fixed top-0 right-0 h-full w-80 bg-white shadow-2xl z-50 lg:hidden flex flex-col overflow-y-auto"
|
||||
>
|
||||
<div class="flex justify-between items-center p-6 pb-4">
|
||||
<NuxtLink to="/" aria-label="返回首页" @click="closeMobileMenu">
|
||||
<img src="/logo-unified.svg" alt="Inkreach" class="h-[56px] w-[97px] object-contain" />
|
||||
</NuxtLink>
|
||||
<button @click="closeMobileMenu" class="p-2" aria-label="Close menu">
|
||||
<i class="fa-solid fa-xmark text-2xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav class="flex flex-col px-6 gap-1 text-lg">
|
||||
<NuxtLink
|
||||
to="/product-center"
|
||||
class="py-3 hover:text-primary transition-colors"
|
||||
@click="closeMobileMenu"
|
||||
>产品中心</NuxtLink>
|
||||
<a :href="PORTAL_URL" target="_blank"
|
||||
class="py-3 hover:text-primary transition-colors"
|
||||
>在线设计</a>
|
||||
|
||||
<!-- Temporarily hidden until recommendation content is ready. -->
|
||||
<div v-if="SHOW_EXTENDED_NAV">
|
||||
<button
|
||||
class="w-full py-3 flex items-center justify-between hover:text-primary transition-colors"
|
||||
@click="toggleMobileRecommend"
|
||||
>
|
||||
<span>选品推荐</span>
|
||||
<i
|
||||
class="fa-solid fa-chevron-down text-xs transition-transform duration-200"
|
||||
:class="mobileRecommendOpen ? 'rotate-180' : ''"
|
||||
></i>
|
||||
</button>
|
||||
<Transition name="accordion">
|
||||
<div v-if="mobileRecommendOpen" class="pl-3 pb-2">
|
||||
<div class="flex gap-2 mb-3 flex-wrap">
|
||||
<button
|
||||
v-for="(cat, idx) in recommendCategories"
|
||||
:key="cat.id"
|
||||
class="px-3 py-1.5 rounded-full text-sm transition-colors"
|
||||
:class="mobileRecommendTab === idx
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-inkreach-homepage-1 text-gray-600'"
|
||||
@click="mobileRecommendTab = idx"
|
||||
>
|
||||
{{ cat.title }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<a
|
||||
v-for="item in mobileActiveCategory?.items ?? []"
|
||||
:key="item.id"
|
||||
:href="item.link"
|
||||
target="_blank"
|
||||
class="group block rounded-lg overflow-hidden"
|
||||
>
|
||||
<div class="w-full h-[70px] rounded-lg overflow-hidden bg-inkreach-homepage-1">
|
||||
<img
|
||||
:src="item.image"
|
||||
:alt="item.title"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-[13px] text-gray-700 group-hover:text-primary truncate">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Temporarily hidden until solution content is ready. -->
|
||||
<div v-if="SHOW_EXTENDED_NAV">
|
||||
<button
|
||||
class="w-full py-3 flex items-center justify-between hover:text-primary transition-colors"
|
||||
@click="toggleMobileSolution"
|
||||
>
|
||||
<span>解决方案</span>
|
||||
<i
|
||||
class="fa-solid fa-chevron-down text-xs transition-transform duration-200"
|
||||
:class="mobileSolutionOpen ? 'rotate-180' : ''"
|
||||
></i>
|
||||
</button>
|
||||
<Transition name="accordion">
|
||||
<div v-if="mobileSolutionOpen" class="pl-3 pb-2">
|
||||
<div
|
||||
v-for="col in solutionColumns"
|
||||
:key="col.id"
|
||||
class="mb-3"
|
||||
>
|
||||
<h4 class="text-[15px] font-medium text-gray-800 mb-2">
|
||||
{{ col.title }}
|
||||
</h4>
|
||||
<ul class="space-y-1.5">
|
||||
<li v-for="link in col.links" :key="link.id">
|
||||
<a
|
||||
:href="link.link"
|
||||
target="_blank"
|
||||
class="text-[14px] text-gray-600 hover:text-primary transition-colors flex items-center gap-1"
|
||||
>
|
||||
{{ link.title }}
|
||||
<span
|
||||
v-if="link.tag === 'hot'"
|
||||
class="text-[10px] text-white bg-red-500 px-1 py-0.5 rounded leading-none"
|
||||
>HOT</span>
|
||||
<span
|
||||
v-if="link.tag === 'new'"
|
||||
class="text-[10px] text-white bg-green-500 px-1 py-0.5 rounded leading-none"
|
||||
>NEW</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex flex-col gap-4 px-6 mt-auto pb-6 pt-4">
|
||||
<button type="button" @click="goToLogin"
|
||||
class="py-2.5 text-lg hover:text-primary transition-colors text-center"
|
||||
>登录</button>
|
||||
<button type="button" @click="goToLogin"
|
||||
class="text-white rounded-lg px-6 py-3 text-center hover:shadow-lg transition-all duration-300 pulse-animation bg-primary"
|
||||
>立即开始</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.desktop-nav { font-size: 18px; }
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.accordion-enter-active,
|
||||
.accordion-leave-active {
|
||||
transition: all 0.25s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.accordion-enter-from,
|
||||
.accordion-leave-to {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
.accordion-enter-to,
|
||||
.accordion-leave-from {
|
||||
max-height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user