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,98 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
|
||||
const activeStep = ref(0);
|
||||
const STEP_INTERVAL_MS = 2000;
|
||||
|
||||
const steps = [
|
||||
{ num: '1', title: '选择产品', desc: '海量精选产品,满足多元定制需求', image: '/01步骤-选择产品.png' },
|
||||
{ num: '2', title: '上传设计', desc: '可视化编辑器,轻松实现创意落地', image: '/02步骤-上传设计.png' },
|
||||
{ num: '3', title: '在线销售', desc: '一键开通店铺,多渠道触达目标客群', image: '/03步骤-在线销售.png' },
|
||||
{ num: '4', title: '生产配送', desc: '智能化柔性生产,快速响应全球配送', image: '/04步骤-生产配送.png' },
|
||||
];
|
||||
|
||||
const selectStep = (idx: number) => {
|
||||
activeStep.value = idx;
|
||||
startStepTimer();
|
||||
};
|
||||
|
||||
let stepTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
function goToLogin(): void {
|
||||
window.location.href = 'https://inkpod.vip/user/login';
|
||||
}
|
||||
|
||||
function startStepTimer(): void {
|
||||
if (stepTimer) clearInterval(stepTimer);
|
||||
stepTimer = setInterval(() => {
|
||||
activeStep.value = (activeStep.value + 1) % steps.length;
|
||||
}, STEP_INTERVAL_MS);
|
||||
}
|
||||
|
||||
onMounted(startStepTimer);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (stepTimer) clearInterval(stepTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="step-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-6xl text-center">4步开始你的按需定制生意</div>
|
||||
|
||||
<div class="flex flex-col lg:flex-row gap-5 w-full max-w-7xl items-stretch">
|
||||
<div class="flex flex-row lg:flex-col gap-3 lg:gap-8 lg:p-5 lg:w-5/12 overflow-x-auto lg:overflow-visible justify-center">
|
||||
<div
|
||||
v-for="(step, idx) in steps"
|
||||
:key="idx"
|
||||
class="flex-shrink-0 flex flex-col gap-2 lg:gap-5 cursor-pointer group min-w-[140px] lg:min-w-0"
|
||||
@click="selectStep(idx)"
|
||||
@mouseenter="selectStep(idx)"
|
||||
>
|
||||
<p class="step-title text-xl lg:text-4xl">{{ step.num }} {{ step.title }}</p>
|
||||
<p class="hidden lg:block indent-10 text-gray-500 text-xl">{{ step.desc }}</p>
|
||||
<div class="h-0.5 w-full rounded-full bg-gray-200 overflow-hidden">
|
||||
<div v-if="activeStep === idx" class="step-progress h-full rounded-full bg-primary"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden lg:flex items-center pt-6">
|
||||
<button type="button" class="text-xl lg:text-2xl text-white bg-primary rounded-lg px-10 lg:px-14 py-3 lg:py-4 hover:opacity-90 transition-opacity" @click="goToLogin">
|
||||
立即开始
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-hidden rounded-xl relative">
|
||||
<img
|
||||
v-for="(step, idx) in steps"
|
||||
:key="'img-' + idx"
|
||||
:src="step.image"
|
||||
:alt="step.title"
|
||||
:data-testid="`step-image-${idx}`"
|
||||
class="w-full h-full object-contain transition-opacity duration-500 absolute inset-0"
|
||||
:class="activeStep === idx ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex lg:hidden">
|
||||
<button type="button" class="text-xl text-white bg-primary rounded-lg px-10 py-3 hover:opacity-90 transition-opacity" @click="goToLogin">
|
||||
立即开始
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.step-progress { width: 0; animation: fill-step-progress 2s linear forwards; }
|
||||
|
||||
@keyframes fill-step-progress {
|
||||
to { width: 100%; }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.step-section { height: 935px; padding-top: 54px; }
|
||||
.step-section > div:first-child { font-size: 48px; }
|
||||
.step-section > div:nth-child(2) { height: 708px; }
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
|
||||
const activeStep = ref(0);
|
||||
const STEP_INTERVAL_MS = 2000;
|
||||
|
||||
const steps = [
|
||||
{ num: '1', title: '选择产品', desc: '海量精选产品,满足多元定制需求', image: '/01步骤-选择产品.png' },
|
||||
{ num: '2', title: '上传设计', desc: '可视化编辑器,轻松实现创意落地', image: '/02步骤-上传设计.png' },
|
||||
{ num: '3', title: '在线销售', desc: '一键开通店铺,多渠道触达目标客群', image: '/03步骤-在线销售.png' },
|
||||
{ num: '4', title: '生产配送', desc: '智能化柔性生产,快速响应全球配送', image: '/04步骤-生产配送.png' },
|
||||
];
|
||||
|
||||
const selectStep = (idx: number) => {
|
||||
activeStep.value = idx;
|
||||
startStepTimer();
|
||||
};
|
||||
|
||||
let stepTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
function goToLogin(): void {
|
||||
window.location.href = 'https://inkpod.vip/user/login';
|
||||
}
|
||||
|
||||
function startStepTimer(): void {
|
||||
if (stepTimer) clearInterval(stepTimer);
|
||||
stepTimer = setInterval(() => {
|
||||
activeStep.value = (activeStep.value + 1) % steps.length;
|
||||
}, STEP_INTERVAL_MS);
|
||||
}
|
||||
|
||||
onMounted(startStepTimer);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (stepTimer) clearInterval(stepTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="step-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-6xl text-center">4步开始你的按需定制生意</div>
|
||||
|
||||
<div class="flex flex-col lg:flex-row gap-5 w-full max-w-7xl items-stretch">
|
||||
<div class="flex flex-row lg:flex-col gap-3 lg:gap-8 lg:p-5 lg:w-5/12 overflow-x-auto lg:overflow-visible justify-center">
|
||||
<div
|
||||
v-for="(step, idx) in steps"
|
||||
:key="idx"
|
||||
class="flex-shrink-0 flex flex-col gap-2 lg:gap-5 cursor-pointer group min-w-[140px] lg:min-w-0"
|
||||
@click="selectStep(idx)"
|
||||
@mouseenter="selectStep(idx)"
|
||||
>
|
||||
<p class="step-title text-xl lg:text-4xl">{{ step.num }} {{ step.title }}</p>
|
||||
<p class="hidden lg:block indent-10 text-gray-500 text-xl">{{ step.desc }}</p>
|
||||
<div class="h-0.5 w-full rounded-full bg-gray-200 overflow-hidden">
|
||||
<div v-if="activeStep === idx" class="step-progress h-full rounded-full bg-primary"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden lg:flex items-center pt-6">
|
||||
<button type="button" class="text-xl lg:text-2xl text-white bg-primary rounded-lg px-10 lg:px-14 py-3 lg:py-4 hover:opacity-90 transition-opacity" @click="goToLogin">
|
||||
立即开始
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-hidden rounded-xl relative">
|
||||
<img
|
||||
v-for="(step, idx) in steps"
|
||||
:key="'img-' + idx"
|
||||
:src="step.image"
|
||||
:alt="step.title"
|
||||
:data-testid="`step-image-${idx}`"
|
||||
class="w-full h-full object-contain transition-opacity duration-500 absolute inset-0"
|
||||
:class="activeStep === idx ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex lg:hidden">
|
||||
<button type="button" class="text-xl text-white bg-primary rounded-lg px-10 py-3 hover:opacity-90 transition-opacity" @click="goToLogin">
|
||||
立即开始
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.step-progress { width: 0; animation: fill-step-progress 2s linear forwards; }
|
||||
|
||||
@keyframes fill-step-progress {
|
||||
to { width: 100%; }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.step-section { height: 935px; padding-top: 54px; }
|
||||
.step-section > div:first-child { font-size: 48px; }
|
||||
.step-section > div:nth-child(2) { height: 708px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user