Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
630 lines
17 KiB
Vue
630 lines
17 KiB
Vue
<script setup lang="ts">
|
||
export type GenerationMode = 'json' | 'manual' | 'ai';
|
||
|
||
interface ModeOption {
|
||
mode: GenerationMode;
|
||
title: string;
|
||
description: string;
|
||
iconColor: string;
|
||
borderGradient: string;
|
||
}
|
||
|
||
const modes: ModeOption[] = [
|
||
{
|
||
mode: 'json',
|
||
title: 'JSON导入',
|
||
description: '直接提供JSON数据,快速生成代码',
|
||
iconColor: '#6366f1',
|
||
borderGradient:
|
||
'linear-gradient(90deg, #6366f1, #8b5cf6, #a855f7, #8b5cf6, #6366f1)',
|
||
},
|
||
{
|
||
mode: 'manual',
|
||
title: '手动填写',
|
||
description: '逐步填写表单,精确控制每个字段',
|
||
iconColor: '#f59e0b',
|
||
borderGradient:
|
||
'linear-gradient(90deg, #f59e0b, #f97316, #fb923c, #f97316, #f59e0b)',
|
||
},
|
||
{
|
||
mode: 'ai',
|
||
title: 'AI提示词',
|
||
description: '使用AI智能生成代码结构(开发中)',
|
||
iconColor: '#10b981',
|
||
borderGradient:
|
||
'linear-gradient(90deg, #10b981, #059669, #34d399, #059669, #10b981)',
|
||
},
|
||
];
|
||
|
||
const emit = defineEmits<{
|
||
select: [mode: GenerationMode];
|
||
}>();
|
||
|
||
const handleSelect = (mode: GenerationMode) => {
|
||
emit('select', mode);
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<div class="mode-selector">
|
||
<div class="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||
<div
|
||
v-for="(item, index) in modes"
|
||
:key="item.mode"
|
||
class="mode-card enter-y group relative overflow-hidden"
|
||
:style="{ animationDelay: `${index * 0.15}s` }"
|
||
:data-mode="item.mode"
|
||
@click="handleSelect(item.mode)"
|
||
>
|
||
|
||
<!-- 装饰背景 -->
|
||
<div class="mode-card-decoration">
|
||
<!-- JSON模式装饰:网格 -->
|
||
<div v-if="item.mode === 'json'" class="json-decoration">
|
||
<svg
|
||
class="absolute inset-0 h-full w-full opacity-5"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<defs>
|
||
<pattern
|
||
id="grid-json"
|
||
width="20"
|
||
height="20"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<path
|
||
d="M 20 0 L 0 0 0 20"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="1"
|
||
/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#grid-json)" />
|
||
</svg>
|
||
<!-- 浮动圆点 -->
|
||
<div class="absolute right-4 top-4 h-2 w-2 rounded-full bg-current opacity-20"></div>
|
||
<div class="absolute bottom-6 left-6 h-1.5 w-1.5 rounded-full bg-current opacity-30"></div>
|
||
<div class="absolute right-8 top-1/2 h-1 w-1 rounded-full bg-current opacity-25"></div>
|
||
</div>
|
||
|
||
<!-- 手动模式装饰:线条 -->
|
||
<div v-else-if="item.mode === 'manual'" class="manual-decoration">
|
||
<svg
|
||
class="absolute inset-0 h-full w-full opacity-5"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<defs>
|
||
<pattern
|
||
id="lines-manual"
|
||
width="30"
|
||
height="30"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<line
|
||
x1="0"
|
||
y1="0"
|
||
x2="30"
|
||
y2="30"
|
||
stroke="currentColor"
|
||
stroke-width="0.5"
|
||
/>
|
||
<line
|
||
x1="30"
|
||
y1="0"
|
||
x2="0"
|
||
y2="30"
|
||
stroke="currentColor"
|
||
stroke-width="0.5"
|
||
/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#lines-manual)" />
|
||
</svg>
|
||
<!-- 几何图形 -->
|
||
<div class="absolute right-6 top-6 h-8 w-8 rotate-45 border border-current opacity-10"></div>
|
||
<div class="absolute bottom-8 left-8 h-6 w-6 rounded-full border border-current opacity-15"></div>
|
||
</div>
|
||
|
||
<!-- AI模式装饰:点阵 -->
|
||
<div v-else-if="item.mode === 'ai'" class="ai-decoration">
|
||
<svg
|
||
class="absolute inset-0 h-full w-full opacity-5"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<defs>
|
||
<pattern
|
||
id="dots-ai"
|
||
width="25"
|
||
height="25"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<circle cx="12.5" cy="12.5" r="1.5" fill="currentColor" />
|
||
</pattern>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#dots-ai)" />
|
||
</svg>
|
||
<!-- 星形装饰 -->
|
||
<div class="absolute right-5 top-5">
|
||
<svg
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 16 16"
|
||
class="opacity-20"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M8 0L9.8 5.5L15.5 5.5L10.9 8.9L12.7 14.4L8 11L3.3 14.4L5.1 8.9L0.5 5.5L6.2 5.5L8 0Z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<div class="absolute bottom-7 left-7">
|
||
<svg
|
||
width="12"
|
||
height="12"
|
||
viewBox="0 0 16 16"
|
||
class="opacity-15"
|
||
fill="currentColor"
|
||
>
|
||
<path
|
||
d="M8 0L9.8 5.5L15.5 5.5L10.9 8.9L12.7 14.4L8 11L3.3 14.4L5.1 8.9L0.5 5.5L6.2 5.5L8 0Z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容 -->
|
||
<div class="relative z-10 flex h-full flex-col items-center justify-center text-center">
|
||
<!-- 图标容器 -->
|
||
<div
|
||
class="mb-6 flex h-20 w-20 items-center justify-center rounded-2xl transition-all duration-300 group-hover:scale-110 group-hover:shadow-lg"
|
||
:style="{
|
||
backgroundColor: item.iconColor + '15',
|
||
border: `2px solid ${item.iconColor}40`,
|
||
}"
|
||
>
|
||
<!-- JSON图标 -->
|
||
<svg
|
||
v-if="item.mode === 'json'"
|
||
width="48"
|
||
height="48"
|
||
viewBox="0 0 48 48"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
:style="{ color: item.iconColor }"
|
||
>
|
||
<path
|
||
d="M12 8C10.8954 8 10 8.89543 10 10V38C10 39.1046 10.8954 40 12 40H36C37.1046 40 38 39.1046 38 38V10C38 8.89543 37.1046 8 36 8H12Z"
|
||
fill="currentColor"
|
||
fill-opacity="0.1"
|
||
/>
|
||
<path
|
||
d="M14 14H34M14 20H34M14 26H28M14 32H24"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
/>
|
||
<path
|
||
d="M18 14V20M24 20V26M18 26V32"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
/>
|
||
<circle cx="32" cy="28" r="3" fill="currentColor" />
|
||
<circle cx="36" cy="32" r="2" fill="currentColor" />
|
||
</svg>
|
||
|
||
<!-- 手动填写图标 -->
|
||
<svg
|
||
v-else-if="item.mode === 'manual'"
|
||
width="48"
|
||
height="48"
|
||
viewBox="0 0 48 48"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
:style="{ color: item.iconColor }"
|
||
>
|
||
<path
|
||
d="M24 8L30 14H36C37.1046 14 38 14.8954 38 16V36C38 37.1046 37.1046 38 36 38H12C10.8954 38 10 37.1046 10 36V12C10 10.8954 10.8954 10 12 10H18L24 8Z"
|
||
fill="currentColor"
|
||
fill-opacity="0.1"
|
||
/>
|
||
<path
|
||
d="M24 8V14H30"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
/>
|
||
<path
|
||
d="M16 22H32M16 28H28M16 34H24"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
/>
|
||
<path
|
||
d="M20 18C20 19.1046 19.1046 20 18 20C16.8954 20 16 19.1046 16 18C16 16.8954 16.8954 16 18 16C19.1046 16 20 16.8954 20 18Z"
|
||
fill="currentColor"
|
||
/>
|
||
</svg>
|
||
|
||
<!-- AI图标 -->
|
||
<svg
|
||
v-else-if="item.mode === 'ai'"
|
||
width="48"
|
||
height="48"
|
||
viewBox="0 0 48 48"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
:style="{ color: item.iconColor }"
|
||
>
|
||
<circle
|
||
cx="24"
|
||
cy="24"
|
||
r="16"
|
||
fill="currentColor"
|
||
fill-opacity="0.1"
|
||
/>
|
||
<path
|
||
d="M20 18C20 19.1046 19.1046 20 18 20C16.8954 20 16 19.1046 16 18C16 16.8954 16.8954 16 18 16C19.1046 16 20 16.8954 20 18Z"
|
||
fill="currentColor"
|
||
/>
|
||
<path
|
||
d="M32 18C32 19.1046 31.1046 20 30 20C28.8954 20 28 19.1046 28 18C28 16.8954 28.8954 16 30 16C31.1046 16 32 16.8954 32 18Z"
|
||
fill="currentColor"
|
||
/>
|
||
<path
|
||
d="M24 28C26.2091 28 28 26.2091 28 24C28 21.7909 26.2091 20 24 20C21.7909 20 20 21.7909 20 24C20 26.2091 21.7909 28 24 28Z"
|
||
fill="currentColor"
|
||
/>
|
||
<path
|
||
d="M18 32C18 32 20 30 24 30C28 30 30 32 30 32"
|
||
stroke="currentColor"
|
||
stroke-width="2.5"
|
||
stroke-linecap="round"
|
||
/>
|
||
<path
|
||
d="M16 14L18 12M30 12L32 14M32 34L30 36M18 36L16 34"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- 标题 -->
|
||
<h3 class="mb-3 text-2xl font-bold text-gray-800 dark:text-gray-200">
|
||
{{ item.title }}
|
||
</h3>
|
||
|
||
<!-- 描述 -->
|
||
<p class="mb-4 text-gray-600 dark:text-gray-400">
|
||
{{ item.description }}
|
||
</p>
|
||
|
||
<!-- 底部提示 -->
|
||
<div
|
||
class="mt-4 flex translate-y-2 items-center gap-2 text-sm font-medium opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100"
|
||
:style="{ color: item.iconColor }"
|
||
>
|
||
<span>点击开始</span>
|
||
<svg
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 16 16"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<path
|
||
d="M6 12L10 8L6 4"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.mode-selector {
|
||
padding: 1rem 0;
|
||
}
|
||
|
||
.mode-card {
|
||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||
border-radius: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
background: #ffffff;
|
||
min-height: 320px;
|
||
height: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
z-index: 1;
|
||
padding: 24px;
|
||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02),
|
||
0 2px 4px 0 rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
/* 暗色模式卡片背景 */
|
||
.dark .mode-card {
|
||
background: #1f2937;
|
||
border-color: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.mode-card:hover {
|
||
transform: translateY(-8px);
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.mode-card:active {
|
||
transform: translateY(-4px);
|
||
}
|
||
|
||
/* 流光边框 - 使用圆锥渐变旋转效果 */
|
||
.mode-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -3px;
|
||
border-radius: 18px;
|
||
padding: 3px;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
z-index: -1;
|
||
pointer-events: none;
|
||
background: transparent;
|
||
transform-origin: center center;
|
||
will-change: transform;
|
||
}
|
||
|
||
.mode-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 3px;
|
||
background: #ffffff;
|
||
border-radius: 15px;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* 暗色模式遮挡层背景 */
|
||
.dark .mode-card::after {
|
||
background: #1f2937;
|
||
}
|
||
|
||
.mode-card {
|
||
position: relative;
|
||
overflow: hidden;
|
||
border-radius: 12px;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
backdrop-filter: blur(10px);
|
||
/* 防止闪烁的关键属性 */
|
||
transform: translateZ(0);
|
||
isolation: isolate;
|
||
/* 添加边框防止内容溢出 */
|
||
border: 1px solid transparent;
|
||
}
|
||
|
||
/* 修复白色闪烁的关键 - 完全重写 ::before */
|
||
.mode-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -5px; /* 增大边框宽度 */
|
||
opacity: 0;
|
||
border-radius: 16px; /* 增大圆角匹配边框 */
|
||
z-index: -1;
|
||
/* 只对opacity进行过渡,避免其他属性变化 */
|
||
transition: opacity 0.3s ease;
|
||
/* 硬件加速 */
|
||
transform: translateZ(0);
|
||
backface-visibility: hidden;
|
||
perspective: 1000px;
|
||
/* 初始状态 - 完全透明但保持渐变结构 */
|
||
background:
|
||
conic-gradient(
|
||
from 0deg at 50% 50%,
|
||
transparent 0%,
|
||
transparent 20%,
|
||
transparent 40%,
|
||
transparent 60%,
|
||
transparent 80%,
|
||
transparent 100%
|
||
);
|
||
background-size: 300% 300%;
|
||
/* 移除所有可能引起闪烁的滤镜 */
|
||
filter: none;
|
||
}
|
||
|
||
.mode-card:hover::before {
|
||
opacity: 1;
|
||
animation:
|
||
rotate-glow 2.5s linear infinite,
|
||
pulse-glow 4s ease-in-out infinite;
|
||
/* 悬停时才应用渐变和滤镜 */
|
||
background:
|
||
radial-gradient(circle at 30% 30%, rgba(255,255,255,0.8) 0%, transparent 50%),
|
||
radial-gradient(circle at 70% 70%, rgba(255,255,255,0.6) 0%, transparent 50%),
|
||
conic-gradient(
|
||
from 0deg at 50% 50%,
|
||
var(--color-start, #6366f1) 0%,
|
||
var(--color-mid, #8b5cf6) 20%,
|
||
var(--color-end, #a855f7) 40%,
|
||
var(--color-mid, #8b5cf6) 60%,
|
||
var(--color-start, #6366f1) 80%,
|
||
var(--color-start, #6366f1) 100%
|
||
);
|
||
filter: blur(12px) brightness(1.3);
|
||
}
|
||
|
||
/* 颜色定义 */
|
||
.mode-card[data-mode='json'] {
|
||
--color-start: #6366f1;
|
||
--color-mid: #8b5cf6;
|
||
--color-end: #a855f7;
|
||
}
|
||
|
||
.mode-card[data-mode='manual'] {
|
||
--color-start: #f59e0b;
|
||
--color-mid: #f97316;
|
||
--color-end: #fb923c;
|
||
}
|
||
|
||
.mode-card[data-mode='ai'] {
|
||
--color-start: #10b981;
|
||
--color-mid: #059669;
|
||
--color-end: #34d399;
|
||
}
|
||
|
||
/* 旋转动画 - 优化性能 */
|
||
@keyframes rotate-glow {
|
||
0% {
|
||
transform: translateZ(0) rotate(0deg);
|
||
background-position: 0% 0%, 0% 0%, 0% 50%;
|
||
}
|
||
25% {
|
||
background-position: 100% 0%, 0% 100%, 25% 75%;
|
||
}
|
||
50% {
|
||
transform: translateZ(0) rotate(180deg);
|
||
background-position: 100% 100%, 100% 100%, 50% 100%;
|
||
}
|
||
75% {
|
||
background-position: 0% 100%, 100% 0%, 75% 75%;
|
||
}
|
||
100% {
|
||
transform: translateZ(0) rotate(360deg);
|
||
background-position: 0% 0%, 0% 0%, 100% 50%;
|
||
}
|
||
}
|
||
|
||
/* 脉动光晕 - 简化避免闪烁 */
|
||
@keyframes pulse-glow {
|
||
0%, 100% {
|
||
filter: blur(12px) brightness(1.3) saturate(1.2);
|
||
}
|
||
50% {
|
||
filter: blur(14px) brightness(1.5) saturate(1.4);
|
||
}
|
||
}
|
||
|
||
/* 内部光效 - 完全重写避免闪烁 */
|
||
.mode-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 2px; /* 调整内部间距 */
|
||
border-radius: 12px;
|
||
background: transparent;
|
||
opacity: 0;
|
||
transition: opacity 0.4s ease 0.1s;
|
||
z-index: -1;
|
||
pointer-events: none;
|
||
/* 使用box-shadow替代背景渐变 */
|
||
box-shadow:
|
||
inset 0 0 20px rgba(255, 255, 255, 0.1),
|
||
inset 0 0 30px rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.mode-card:hover::after {
|
||
opacity: 1;
|
||
/* 简化动画,只做微妙的脉动 */
|
||
animation: inner-pulse 3s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes inner-pulse {
|
||
0%, 100% {
|
||
box-shadow:
|
||
inset 0 0 20px rgba(255, 255, 255, 0.1),
|
||
inset 0 0 30px rgba(255, 255, 255, 0.05);
|
||
}
|
||
50% {
|
||
box-shadow:
|
||
inset 0 0 25px rgba(255, 255, 255, 0.15),
|
||
inset 0 0 35px rgba(255, 255, 255, 0.08);
|
||
}
|
||
}
|
||
|
||
/* 添加一个额外的容器层确保内容在光效之上 */
|
||
.mode-card > * {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* 性能优化 */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.mode-card:hover::before {
|
||
animation:
|
||
rotate-glow 6s linear infinite,
|
||
pulse-glow 8s ease-in-out infinite;
|
||
}
|
||
}
|
||
|
||
/* 针对某些浏览器的额外修复 */
|
||
@supports not (backdrop-filter: blur(10px)) {
|
||
.mode-card {
|
||
background: rgba(255, 255, 255, 0.02);
|
||
}
|
||
}
|
||
|
||
/* 装饰元素 */
|
||
.mode-card-decoration {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
color: currentColor;
|
||
}
|
||
|
||
.json-decoration,
|
||
.manual-decoration,
|
||
.ai-decoration {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.enter-y {
|
||
opacity: 0;
|
||
animation: enter-y-animation 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||
}
|
||
|
||
@keyframes enter-y-animation {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px) scale(0.95);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
}
|
||
}
|
||
|
||
|
||
.dark .mode-card:hover {
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.dark .mode-card-border-inner {
|
||
background: rgba(255, 255, 255, 0.02);
|
||
}
|
||
|
||
/* 响应式优化 */
|
||
@media (max-width: 768px) {
|
||
.mode-card {
|
||
min-height: 280px;
|
||
}
|
||
|
||
.mode-card:hover {
|
||
transform: translateY(-4px);
|
||
}
|
||
}
|
||
</style>
|