EDMS/components/RsCard.vue
2025-06-05 14:57:08 +08:00

180 lines
3.7 KiB
Vue

<script setup>
import { computed } from 'vue';
const props = defineProps({
// Card height behavior
height: {
type: String,
default: 'auto',
validator: (value) => ['auto', 'full', 'screen', 'fit'].includes(value)
},
// Content overflow behavior
overflow: {
type: String,
default: 'auto',
validator: (value) => ['auto', 'hidden', 'scroll', 'visible'].includes(value)
},
// Maximum height
maxHeight: {
type: String,
default: null
},
// Padding for body content
bodyPadding: {
type: String,
default: 'md',
validator: (value) => ['none', 'sm', 'md', 'lg', 'xl'].includes(value)
},
// Make content scrollable
scrollable: {
type: Boolean,
default: false
}
});
// Computed classes
const cardClasses = computed(() => {
const classes = ['card'];
// Height classes
switch (props.height) {
case 'full':
classes.push('h-full');
break;
case 'screen':
classes.push('h-screen');
break;
case 'fit':
classes.push('h-fit');
break;
default:
classes.push('h-auto');
}
return classes.join(' ');
});
const bodyClasses = computed(() => {
const classes = ['card-body'];
// Padding classes
const paddingMap = {
none: 'p-0',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
xl: 'p-8'
};
classes.push(paddingMap[props.bodyPadding]);
// Overflow handling
if (props.scrollable || props.overflow !== 'auto') {
classes.push(`overflow-${props.overflow}`);
}
return classes.join(' ');
});
const bodyStyles = computed(() => {
const styles = {};
if (props.maxHeight) {
styles.maxHeight = props.maxHeight;
}
return styles;
});
</script>
<template>
<div :class="cardClasses">
<header v-if="!!$slots.header" class="card-header">
<slot name="header" />
</header>
<main v-if="!!$slots.default" class="card-main">
<slot />
</main>
<div
v-if="!!$slots.body"
:class="bodyClasses"
:style="bodyStyles"
>
<slot name="body" />
</div>
<footer v-if="!!$slots.footer" class="card-footer">
<slot name="footer" />
</footer>
</div>
</template>
<style scoped>
.card {
@apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-sm;
@apply flex flex-col;
}
.card-header {
@apply px-6 py-4 border-b border-gray-200 dark:border-gray-600;
@apply flex-shrink-0;
}
.card-main {
@apply flex-1 min-h-0;
}
.card-body {
@apply flex-1 min-h-0;
}
.card-footer {
@apply px-6 py-4 border-t border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700;
@apply flex-shrink-0;
}
/* Custom scrollbar for scrollable content */
.card-body.overflow-auto::-webkit-scrollbar,
.card-body.overflow-scroll::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.card-body.overflow-auto::-webkit-scrollbar-track,
.card-body.overflow-scroll::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 3px;
}
.card-body.overflow-auto::-webkit-scrollbar-thumb,
.card-body.overflow-scroll::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
.card-body.overflow-auto::-webkit-scrollbar-thumb:hover,
.card-body.overflow-scroll::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
.dark .card-body.overflow-auto::-webkit-scrollbar-track,
.dark .card-body.overflow-scroll::-webkit-scrollbar-track {
background: #374151;
}
.dark .card-body.overflow-auto::-webkit-scrollbar-thumb,
.dark .card-body.overflow-scroll::-webkit-scrollbar-thumb {
background: #6b7280;
}
.dark .card-body.overflow-auto::-webkit-scrollbar-thumb:hover,
.dark .card-body.overflow-scroll::-webkit-scrollbar-thumb:hover {
background: #9ca3af;
}
</style>