Files
gao-customer-h5/src/components/EventSelector.vue
T
2026-08-14 16:01:05 +08:00

166 lines
3.2 KiB
Vue

<template>
<div class="registration-event-selector">
<p v-if="props.label" class="t-form__label" :class="{required: props.requiredMark}" v-text="props.label" />
<div v-if="props.eventList.length" class="group" role="group">
<button
v-for="item in props.eventList"
:key="item.id || item.name"
type="button"
class="item"
:class="{selected: isSelected(item.id)}"
:title="item.name"
:disabled="props.disabled || !item.id"
:aria-pressed="isSelected(item.id)"
@click="toggleEvent(item.id)"
>
<span
class="swatch"
:style="{background: item.color || defaultColor}"
aria-hidden="true"
/>
<span class="clip-text" v-text="item.name" />
<span v-if="isSelected(item.id)" class="mark" aria-hidden="true" />
</button>
</div>
<div v-else class="empty-block">暂无可用登记事件</div>
</div>
</template>
<script setup lang="ts">
import type { EventItem } from "@/types/EventItem";
interface Props {
eventList: EventItem[];
label?: string;
requiredMark?: boolean;
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
label: "登记事件",
requiredMark: false,
disabled: false
});
const modelValue = defineModel<string[]>({ required: true });
const defaultColor = "#2f88ff";
const isSelected = (eventId?: string) => Boolean(eventId && modelValue.value.includes(eventId));
const toggleEvent = (eventId?: string) => {
if (props.disabled || !eventId) {
return;
}
if (isSelected(eventId)) {
modelValue.value = modelValue.value.filter(id => id !== eventId);
return;
}
modelValue.value = [...modelValue.value, eventId];
};
</script>
<style scoped lang="less">
.registration-event-selector {
width: 100%;
display: grid;
gap: .5rem;
.t-form__label {
margin: 0;
&.required::before {
color: #e34d59;
content: "*";
margin-right: .25rem;
}
}
.group {
width: 100%;
gap: .75rem;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.item {
gap: .25rem;
width: 100%;
margin: 0;
border: 1px solid gray;
display: flex;
padding: .5rem;
position: relative;
overflow: hidden;
background: #F8FBFF;
text-align: left;
align-items: center;
border-radius: .5rem;
&.selected {
background: var(--td-brand-color-1, #f0f7ff);
border-color: var(--td-brand-color, #2f88ff);
}
&:disabled {
cursor: not-allowed;
opacity: .6;
}
.swatch {
width: 1.5rem;
height: 1.5rem;
border-radius: .25rem;
}
.mark {
top: -2px;
left: -2px;
color: #fff;
width: 1.5rem;
height: 1.5rem;
position: absolute;
clip-path: polygon(0 0, 100% 0, 0 100%);
background: var(--td-brand-color, #2F88FF);
pointer-events: none;
&::after {
position: absolute;
top: .2rem;
left: .3rem;
font-size: .75rem;
font-weight: 700;
line-height: 1;
content: "✓";
}
}
}
.empty-block {
color: #52607a;
text-align: center;
}
}
:global(html.theme-dark) {
.registration-event-selector {
.t-form__label {
color: #94a3b8;
}
.empty-block,
.item {
color: #e2e8f0;
background: rgba(15, 23, 42, .72);
border-color: rgba(148, 163, 184, .16);
}
.item.selected {
background: rgba(47, 136, 255, .16);
border-color: var(--td-brand-color, #2f88ff);
}
}
}
</style>