108 lines
2.3 KiB
Vue
108 lines
2.3 KiB
Vue
<template>
|
|
<a
|
|
:href="props.icon.url"
|
|
class="icon-card-wrapper"
|
|
:class="`size-${props.icon.size ?? '1x1'}`"
|
|
@click.prevent
|
|
target="_blank"
|
|
>
|
|
<div class="icon-card" :style="{ backgroundColor: props.icon.bgColor }">
|
|
<img v-if="props.icon.img" :src="props.icon.img" :alt="props.icon.name" />
|
|
<span v-else>{{ props.icon.name.charAt(0) }}</span>
|
|
</div>
|
|
<div class="label">{{ props.icon.name }}</div>
|
|
</a>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
type IconSize = '1x1' | '1x2' | '2x1' | '2x2' | '2x4';
|
|
|
|
interface Icon {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
size?: IconSize;
|
|
img?: string;
|
|
bgColor?: string;
|
|
}
|
|
|
|
const props = defineProps<{
|
|
icon: Icon;
|
|
}>();
|
|
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/styles/tokens.scss';
|
|
|
|
.icon-card-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
justify-content: flex-start;
|
|
width: 100%;
|
|
height: 100%;
|
|
text-decoration: none;
|
|
border-radius: $border-radius-small;
|
|
padding: var(--icon-card-padding);
|
|
transition: transform $motion-duration-sm $motion-easing-standard;
|
|
user-select: none; // 拖拽时避免选中文本
|
|
box-sizing: border-box;
|
|
|
|
// 悬停效果由父级拖拽状态控制
|
|
&:hover {
|
|
.icon-card {
|
|
box-shadow: $shadow-lg;
|
|
}
|
|
}
|
|
}
|
|
|
|
.icon-card {
|
|
width: 100%;
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
border-radius: $border-radius-small;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: var(--icon-font-size);
|
|
font-weight: 500;
|
|
color: white;
|
|
box-shadow: $shadow-md;
|
|
transition: box-shadow $motion-duration-sm $motion-easing-standard;
|
|
margin-bottom: var(--icon-label-margin-top);
|
|
background-size: cover;
|
|
background-position: center;
|
|
pointer-events: none; // 指针事件交给外层容器处理
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: $border-radius-small;
|
|
object-fit: cover;
|
|
}
|
|
}
|
|
|
|
.icon-card-wrapper.size-1x1 {
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.icon-card-wrapper.size-1x1 .icon-card {
|
|
width: var(--icon-size);
|
|
height: var(--icon-size);
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.label {
|
|
font-size: var(--icon-label-font-size);
|
|
color: $color-text-primary;
|
|
text-align: center;
|
|
width: 100%;
|
|
white-space: normal;
|
|
word-break: break-word;
|
|
line-height: 1.2;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|