Files
nl-admin-view/apps/web-antd/src/views/code-generation/components/mode-selector.vue
liqi dd41072fc9
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
新增多种生成方式
2025-11-28 10:55:29 +08:00

630 lines
17 KiB
Vue
Raw 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">
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>