工具完成
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
2025-06-28 22:38:49 +08:00
parent 2c668fedd0
commit 8400dbfab9
60 changed files with 23197 additions and 144 deletions

View File

@ -0,0 +1,332 @@
<template>
<div class="space-y-6">
<!-- 标题和描述 -->
<div class="card p-6">
<h2 class="text-2xl font-bold text-primary mb-4">CSS渐变生成器</h2>
<p class="text-secondary mb-4">创建线性渐变和径向渐变生成CSS代码并实时预览效果</p>
</div>
<!-- 渐变类型选择 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">渐变类型</h3>
<div class="flex gap-3">
<button
@click="gradientType = 'linear'"
:class="gradientType === 'linear' ? 'btn-primary' : 'btn-secondary'"
class="px-4 py-2 rounded-lg"
>
线性渐变
</button>
<button
@click="gradientType = 'radial'"
:class="gradientType === 'radial' ? 'btn-primary' : 'btn-secondary'"
class="px-4 py-2 rounded-lg"
>
径向渐变
</button>
</div>
</div>
<!-- 线性渐变设置 -->
<div v-if="gradientType === 'linear'" class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">角度设置</h3>
<div class="flex items-center gap-4">
<input
v-model.number="linearAngle"
type="range"
min="0"
max="360"
class="flex-1"
/>
<input
v-model.number="linearAngle"
type="number"
min="0"
max="360"
class="input w-20"
/>
<span>°</span>
</div>
</div>
<!-- 径向渐变设置 -->
<div v-if="gradientType === 'radial'" class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">径向渐变设置</h3>
<!-- 形状 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">形状</label>
<div class="flex gap-3">
<button
v-for="shape in radialShapes"
:key="shape.value"
@click="radialShape = shape.value"
:class="{
'btn-primary': radialShape === shape.value,
'btn-secondary': radialShape !== shape.value
}"
class="px-4 py-2 rounded transition-colors"
>
{{ shape.label }}
</button>
</div>
</div>
<!-- 大小 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">大小</label>
<div class="grid grid-cols-2 gap-2">
<button
v-for="size in radialSizes"
:key="size.value"
@click="radialSize = size.value"
:class="{
'btn-primary': radialSize === size.value,
'btn-secondary': radialSize !== size.value
}"
class="px-3 py-2 text-sm rounded transition-colors"
>
{{ size.label }}
</button>
</div>
</div>
<!-- 位置 -->
<div class="mb-4">
<label class="block text-sm font-medium text-secondary mb-2">位置</label>
<div class="grid grid-cols-3 gap-2">
<button
v-for="position in radialPositions"
:key="position.value"
@click="radialPosition = position.value"
:class="{
'btn-primary': radialPosition === position.value,
'btn-secondary': radialPosition !== position.value
}"
class="px-3 py-2 text-sm rounded transition-colors"
>
{{ position.label }}
</button>
</div>
</div>
</div>
<!-- 颜色设置 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">颜色设置</h3>
<div class="space-y-3 mb-4">
<div v-for="(color, index) in colors" :key="index" class="flex items-center gap-3">
<input
v-model="color.color"
type="color"
class="w-12 h-10 rounded"
/>
<input
v-model="color.color"
type="text"
class="input flex-1"
/>
<input
v-model.number="color.position"
type="number"
min="0"
max="100"
class="input w-20"
/>
<span>%</span>
<button
@click="removeColor(index)"
:disabled="colors.length <= 2"
class="btn-secondary"
>
删除
</button>
</div>
</div>
<button @click="addColor" class="btn-primary">添加颜色</button>
</div>
<!-- 预览 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">预览</h3>
<div
class="w-full h-40 rounded-lg border"
:style="{ background: generatedCSS }"
></div>
</div>
<!-- 生成的CSS -->
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-primary">生成的CSS</h3>
<button @click="copyCSS" class="btn-secondary">复制</button>
</div>
<textarea
:value="generatedCSS"
readonly
class="input w-full h-20 font-mono"
></textarea>
</div>
<!-- 使用说明 -->
<div class="card p-6">
<h3 class="text-lg font-semibold text-primary mb-4">使用说明</h3>
<div class="prose text-secondary max-w-none">
<ul class="space-y-2">
<li><strong>线性渐变</strong>沿直线方向的颜色过渡可调整角度和方向</li>
<li><strong>径向渐变</strong>从中心点向外辐射的颜色过渡可调整形状大小和位置</li>
<li><strong>颜色设置</strong>点击颜色块选择颜色调整位置百分比控制渐变分布</li>
<li><strong>预设渐变</strong>提供多种常用渐变效果点击即可应用</li>
<li><strong>实时预览</strong>所有修改都会实时显示在预览区域</li>
<li><strong>代码生成</strong>自动生成标准CSS代码支持复制完整规则或仅background属性</li>
</ul>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
// 颜色接口
interface ColorStop {
color: string
position: number
}
// 渐变类型
const gradientTypes = [
{ label: '线性渐变', value: 'linear' },
{ label: '径向渐变', value: 'radial' }
]
// 线性渐变方向
const linearDirections = [
{ label: '向右', value: 90 },
{ label: '向左', value: 270 },
{ label: '向下', value: 180 },
{ label: '向上', value: 0 },
{ label: '右下', value: 135 },
{ label: '左上', value: 315 },
{ label: '右上', value: 45 },
{ label: '左下', value: 225 }
]
// 径向渐变形状
const radialShapes = [
{ label: '椭圆', value: 'ellipse' },
{ label: '圆形', value: 'circle' }
]
// 径向渐变大小
const radialSizes = [
{ label: '最近边', value: 'closest-side' },
{ label: '最近角', value: 'closest-corner' },
{ label: '最远边', value: 'farthest-side' },
{ label: '最远角', value: 'farthest-corner' }
]
// 径向渐变位置
const radialPositions = [
{ label: '左上', value: 'top left' },
{ label: '上方', value: 'top' },
{ label: '右上', value: 'top right' },
{ label: '左侧', value: 'left' },
{ label: '中心', value: 'center' },
{ label: '右侧', value: 'right' },
{ label: '左下', value: 'bottom left' },
{ label: '下方', value: 'bottom' },
{ label: '右下', value: 'bottom right' }
]
// 响应式状态
const gradientType = ref('linear')
const linearAngle = ref(90)
const radialShape = ref('ellipse')
const radialSize = ref('farthest-corner')
const radialPosition = ref('center')
const colors = ref<ColorStop[]>([
{ color: '#667eea', position: 0 },
{ color: '#764ba2', position: 100 }
])
// 计算生成的CSS
const generatedCSS = computed(() => {
const colorStops = colors.value
.sort((a, b) => a.position - b.position)
.map(color => `${color.color} ${color.position}%`)
.join(', ')
if (gradientType.value === 'linear') {
return `linear-gradient(${linearAngle.value}deg, ${colorStops})`
} else {
return `radial-gradient(${radialShape.value} ${radialSize.value} at ${radialPosition.value}, ${colorStops})`
}
})
// 添加颜色
const addColor = () => {
const newPosition = colors.value.length > 0
? Math.max(...colors.value.map(c => c.position)) + 10
: 50
colors.value.push({
color: '#000000',
position: Math.min(newPosition, 100)
})
}
// 删除颜色
const removeColor = (index: number) => {
if (colors.value.length > 2) {
colors.value.splice(index, 1)
}
}
// 复制CSS
const copyCSS = async () => {
try {
await navigator.clipboard.writeText(generatedCSS.value)
alert('CSS代码已复制到剪贴板')
} catch (error) {
console.error('复制失败:', error)
alert('复制失败,请手动复制')
}
}
</script>
<style scoped>
.slider {
-webkit-appearance: none;
appearance: none;
height: 6px;
border-radius: 3px;
background: linear-gradient(to right, #ddd, #ddd);
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary-color);
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary-color);
cursor: pointer;
border: none;
}
.prose ul {
list-style-type: disc;
padding-left: 1.5rem;
}
</style>