iot-ui-vue/src/components/CardSelect/CardSelect.vue

283 lines
7.6 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.

<template>
<div :class="{ 'j-card-panel': true, 'no-column': noColumn }">
<j-row v-if="!noColumn" :gutter="[16, 16]">
<j-col
v-for="item in itemOptions"
:key="item.value"
:span="24 / column"
>
<div
:class="{
'j-card-item': true,
active: activeKeys.includes(item.value),
disabled: disabled || item.disabled,
horizontal: type === 'horizontal',
vertical: type === 'vertical',
right: float === 'right',
left: float === 'left',
}"
@click="() => handleSelect(item.value, item)"
>
<div class="j-card-title-warp">
<div class="title">
<slot
name="title"
:title="item.label"
:option="item"
>
<j-ellipsis>
{{ item.label }}
</j-ellipsis>
</slot>
</div>
<div
v-if="item.subLabel && showSubLabel"
class="sub-title"
>
<slot
name="subLabel"
:sub-label="item.subLabel"
:option="item"
>
{{ item.subLabel }}
</slot>
</div>
</div>
<div v-if="showImage" class="j-card-image">
<slot name="image" :image="item.iconUrl" :option="item">
<j-avatar
class="icon box-shadow"
:src="item.iconUrl"
/>
</slot>
</div>
</div>
</j-col>
</j-row>
<template v-else>
<div
v-for="item in itemOptions"
:key="item.value"
:class="{
'j-card-item': true,
active: activeKeys.includes(item.value),
disabled: disabled || item.disabled,
horizontal: type === 'horizontal',
vertical: type === 'vertical',
right: float === 'right',
left: float === 'left',
}"
@click="() => handleSelect(item.value, item)"
>
<div class="j-card-title-warp">
<div class="title">
<slot name="title" :title="item.label" :option="item">
<j-ellipsis>
{{ item.label }}
</j-ellipsis>
</slot>
</div>
<div v-if="item.subLabel && showSubLabel" class="sub-title">
<slot
name="subLabel"
:sub-label="item.subLabel"
:option="item"
>
{{ item.subLabel }}
</slot>
</div>
</div>
<div v-if="showImage" class="j-card-image">
<slot name="image" :image="item.iconUrl" :option="item">
<j-avatar class="icon box-shadow" :src="item.iconUrl" />
</slot>
</div>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import { computed, PropType, ref, toRefs, watch } from 'vue';
interface CardOption {
value: string | number;
label: string;
subLabel?: string;
iconUrl: string;
disabled?: boolean;
}
const props = defineProps({
type: {
type: String as PropType<'vertical' | 'horizontal'>,
default: 'horizontal',
},
float: {
type: String as PropType<'left' | 'right'>,
default: 'left',
},
options: {
type: Array as PropType<Array<CardOption>>,
default: () => [],
},
disabled: {
type: Boolean,
default: false,
},
multiple: {
type: Boolean,
default: false,
},
column: {
type: Number,
default: 3,
},
noColumn: {
type: Boolean,
default: false,
},
showImage: {
type: Boolean,
default: true,
},
showSubLabel: {
type: Boolean,
default: true,
},
value: {
type: [String, Array],
default: undefined,
},
allowClear: {
type: Boolean,
default: false,
},
});
const { multiple, type, disabled, float } = toRefs(props);
const emits = defineEmits(['update:value', 'change', 'select']);
const activeKeys = ref<Array<string | number>>([]);
const itemOptions = computed(() => props.options);
const isAllowClear = computed(() => {
return props.allowClear !== false;
});
const getOptions = (keys: Array<string | number>): CardOption[] => {
return itemOptions.value.filter((item) => {
return keys.includes(item.value);
});
};
const handleSelect = (key: string | number, item: CardOption) => {
if (disabled.value || item.disabled) return;
let cloneActiveKeys = new Set(activeKeys.value);
const isActive = cloneActiveKeys.has(key);
// 已选中并且单选allowClear为false则return
if (isActive && !multiple.value && isAllowClear.value === false) return;
if (isActive) {
// 选中
cloneActiveKeys.delete(key);
} else {
// 添加
multiple.value
? cloneActiveKeys.add(key)
: (cloneActiveKeys = new Set([key]));
}
activeKeys.value = [...cloneActiveKeys.keys()];
const options = multiple.value ? getOptions(activeKeys.value) : item;
const values = props.multiple ? activeKeys.value : activeKeys.value[0]
emits('update:value', values);
emits('change', values, options);
emits('select', values, key, !isActive)
};
watch(
() => props.value,
() => {
activeKeys.value = Array.isArray(props.value)
? props.value
: [props.value];
},
{ immediate: true },
);
</script>
<style lang="less" scoped>
@card-border: #e6e6e6;
.j-card-panel {
.j-card-item {
border: 1px solid @card-border;
border-radius: 4px;
cursor: pointer;
color: @black;
display: flex;
width: 100%;
gap: 12px;
.j-card-title-warp {
flex: 1 1 auto;
max-width: 100%;
.title {
word-break: keep-all;
width: 100%;
}
}
&.vertical {
flex-direction: column-reverse;
padding: 22px 4px;
align-items: center;
.j-card-image {
margin-bottom: 4px;
}
}
&.horizontal {
padding: 20px;
}
.sub-title {
color: rgba(0, 0, 0, 0.24);
}
&.right {
flex-direction: row-reverse;
}
}
&.no-column {
display: flex;
flex-wrap: wrap;
gap: 16px;
.j-card-item {
min-width: 36px;
width: unset;
&.vertical {
padding: 14px 16px;
}
}
}
.active {
border: 1px solid var(--ant-primary-color) !important;
}
.disabled {
cursor: not-allowed;
opacity: 0.75;
}
}
</style>