195 lines
4.0 KiB
Vue
195 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from "vue";
|
|
|
|
type UploadAction = "file" | "image";
|
|
|
|
interface ActionCard {
|
|
id: UploadAction;
|
|
title: string;
|
|
description: string;
|
|
icon: string;
|
|
color: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
supportsFiles?: boolean;
|
|
supportsVision?: boolean;
|
|
}>(),
|
|
{
|
|
supportsFiles: true,
|
|
supportsVision: true,
|
|
},
|
|
);
|
|
|
|
const emit = defineEmits<{
|
|
file: [];
|
|
image: [];
|
|
}>();
|
|
|
|
const cards = computed<ActionCard[]>(() => [
|
|
{
|
|
id: "file",
|
|
title: "附件",
|
|
description: props.supportsFiles ? "上传文档 / 压缩包" : "当前模型不支持附件",
|
|
icon: "📎",
|
|
color: "#06b6d4",
|
|
disabled: !props.supportsFiles,
|
|
},
|
|
{
|
|
id: "image",
|
|
title: "图片",
|
|
description: props.supportsVision ? "上传图片 / 截图" : "当前模型不支持图片",
|
|
icon: "🖼️",
|
|
color: "#8b5cf6",
|
|
disabled: !props.supportsVision,
|
|
},
|
|
]);
|
|
|
|
function handleClick(action: UploadAction) {
|
|
if (action === "file" && props.supportsFiles) {
|
|
emit("file");
|
|
}
|
|
if (action === "image" && props.supportsVision) {
|
|
emit("image");
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="upload-card-group" aria-label="上传入口">
|
|
<button
|
|
type="button"
|
|
v-for="(card, index) in cards"
|
|
:key="card.id"
|
|
class="upload-card"
|
|
:class="{ disabled: card.disabled, [`card-${card.id}`]: true }"
|
|
:style="{
|
|
'--card-color': card.color,
|
|
'--card-border': `${card.color}33`,
|
|
'--card-glow': `${card.color}40`,
|
|
'--card-glow-fade': `${card.color}14`,
|
|
'--card-offset': `${index * 14}px`,
|
|
zIndex: cards.length - index,
|
|
}"
|
|
:disabled="card.disabled"
|
|
:title="card.description"
|
|
@click="handleClick(card.id)"
|
|
>
|
|
<span class="card-glow" />
|
|
<span class="card-icon">{{ card.icon }}</span>
|
|
<span class="card-copy">
|
|
<span class="card-title">{{ card.title }}</span>
|
|
<span class="card-desc">{{ card.description }}</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.upload-card-group {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
min-width: 164px;
|
|
height: 44px;
|
|
}
|
|
|
|
.upload-card {
|
|
position: absolute;
|
|
left: var(--card-offset);
|
|
top: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: 138px;
|
|
height: 44px;
|
|
padding: 0 12px;
|
|
border: 1px solid var(--card-border);
|
|
border-radius: 999px;
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(245, 247, 250, 0.96));
|
|
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.08);
|
|
color: #1f2937;
|
|
cursor: pointer;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease, border-color 0.2s ease;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.upload-card:hover:not(:disabled) {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 14px 24px rgba(15, 23, 42, 0.12);
|
|
}
|
|
|
|
.upload-card.disabled,
|
|
.upload-card:disabled {
|
|
opacity: 0.45;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.card-glow {
|
|
position: absolute;
|
|
inset: -20%;
|
|
background: radial-gradient(circle at left center, var(--card-glow), transparent 58%);
|
|
opacity: 0.45;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card-icon {
|
|
position: relative;
|
|
z-index: 1;
|
|
flex-shrink: 0;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.card-copy {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
text-align: left;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
line-height: 1.1;
|
|
}
|
|
|
|
.card-desc {
|
|
margin-top: 2px;
|
|
font-size: 11px;
|
|
line-height: 1.2;
|
|
color: #6b7280;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.dark {
|
|
.upload-card {
|
|
background: linear-gradient(135deg, rgba(30, 30, 46, 0.98), rgba(24, 24, 37, 0.98));
|
|
color: #f3f4f6;
|
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.22);
|
|
}
|
|
|
|
.card-desc {
|
|
color: #9ca3af;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.upload-card-group {
|
|
min-width: 0;
|
|
height: 40px;
|
|
}
|
|
|
|
.upload-card {
|
|
width: 126px;
|
|
height: 40px;
|
|
}
|
|
}
|
|
</style>
|