big updates
This commit is contained in:
parent
34f2c110d6
commit
03a230b8f7
14 changed files with 2417 additions and 242 deletions
|
|
@ -272,6 +272,21 @@
|
|||
:severity="getBadgeColor(slotProps.data[col.fieldName])"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="col.type === 'status-button'" #body="slotProps">
|
||||
<Button
|
||||
:label="slotProps.data[col.fieldName]"
|
||||
:severity="getBadgeColor(slotProps.data[col.fieldName])"
|
||||
size="small"
|
||||
:variant="col.buttonVariant || 'filled'"
|
||||
@click="handleStatusButtonClick(col, slotProps.data)"
|
||||
:disabled="
|
||||
loading ||
|
||||
(col.disableCondition &&
|
||||
col.disableCondition(slotProps.data[col.fieldName]))
|
||||
"
|
||||
class="status-button"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="col.type === 'date'" #body="slotProps">
|
||||
<span>{{ formatDate(slotProps.data[col.fieldName]) }}</span>
|
||||
</template>
|
||||
|
|
@ -1044,6 +1059,17 @@ const handleBulkAction = (action, selectedRows) => {
|
|||
}
|
||||
};
|
||||
|
||||
// Handle status button clicks
|
||||
const handleStatusButtonClick = (column, rowData) => {
|
||||
try {
|
||||
if (column.onStatusClick && typeof column.onStatusClick === "function") {
|
||||
column.onStatusClick(rowData[column.fieldName], rowData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error executing status button click:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const getBadgeColor = (status) => {
|
||||
switch (status?.toLowerCase()) {
|
||||
case "completed":
|
||||
|
|
@ -1547,4 +1573,29 @@ defineExpose({
|
|||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Status Button Styles */
|
||||
.status-button {
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-button:not(:disabled):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.status-button:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.status-button:disabled:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,286 +1,289 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="localVisible"
|
||||
:persistent="options.persistent || false"
|
||||
:fullscreen="options.fullscreen || false"
|
||||
:max-width="options.maxWidth || '500px'"
|
||||
:width="options.width"
|
||||
:height="options.height"
|
||||
:attach="options.attach"
|
||||
:transition="options.transition || 'dialog-transition'"
|
||||
:scrollable="options.scrollable || false"
|
||||
:retain-focus="options.retainFocus !== false"
|
||||
:close-on-back="options.closeOnBack !== false"
|
||||
:close-on-content-click="options.closeOnContentClick || false"
|
||||
:overlay-color="options.overlayColor"
|
||||
:overlay-opacity="options.overlayOpacity"
|
||||
:z-index="options.zIndex"
|
||||
:class="options.dialogClass"
|
||||
@click:outside="handleOutsideClick"
|
||||
@keydown.esc="handleEscapeKey"
|
||||
>
|
||||
<v-card
|
||||
:class="[
|
||||
'modal-card',
|
||||
options.cardClass,
|
||||
{
|
||||
'elevation-0': options.flat,
|
||||
'rounded-0': options.noRadius
|
||||
}
|
||||
]"
|
||||
:color="options.cardColor"
|
||||
:variant="options.cardVariant"
|
||||
:elevation="options.elevation"
|
||||
>
|
||||
<!-- Header Section -->
|
||||
<v-card-title
|
||||
v-if="options.showHeader !== false"
|
||||
:class="[
|
||||
'modal-header d-flex align-center justify-space-between',
|
||||
options.headerClass
|
||||
]"
|
||||
>
|
||||
<div class="modal-title">
|
||||
<slot name="title">
|
||||
{{ options.title }}
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<!-- Close button -->
|
||||
<v-btn
|
||||
v-if="options.showCloseButton !== false && !options.persistent"
|
||||
icon
|
||||
variant="text"
|
||||
size="small"
|
||||
:color="options.closeButtonColor || 'grey'"
|
||||
@click="closeModal"
|
||||
class="modal-close-btn"
|
||||
>
|
||||
<v-icon>{{ options.closeIcon || 'mdi-close' }}</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-dialog
|
||||
v-model="localVisible"
|
||||
:persistent="options.persistent || false"
|
||||
:fullscreen="options.fullscreen || false"
|
||||
:max-width="options.maxWidth || '500px'"
|
||||
:width="options.width"
|
||||
:height="options.height"
|
||||
:attach="options.attach"
|
||||
:transition="options.transition || 'dialog-transition'"
|
||||
:scrollable="options.scrollable || false"
|
||||
:retain-focus="options.retainFocus !== false"
|
||||
:close-on-back="options.closeOnBack !== false"
|
||||
:close-on-content-click="options.closeOnContentClick || false"
|
||||
:overlay-color="options.overlayColor"
|
||||
:overlay-opacity="options.overlayOpacity"
|
||||
:z-index="options.zIndex"
|
||||
:class="options.dialogClass"
|
||||
@click:outside="handleOutsideClick"
|
||||
@keydown.esc="handleEscapeKey"
|
||||
>
|
||||
<v-card
|
||||
:class="[
|
||||
'modal-card',
|
||||
options.cardClass,
|
||||
{
|
||||
'elevation-0': options.flat,
|
||||
'rounded-0': options.noRadius,
|
||||
},
|
||||
]"
|
||||
:color="options.cardColor"
|
||||
:variant="options.cardVariant"
|
||||
:elevation="options.elevation"
|
||||
>
|
||||
<!-- Header Section -->
|
||||
<v-card-title
|
||||
v-if="options.showHeader !== false"
|
||||
:class="[
|
||||
'modal-header d-flex align-center justify-space-between',
|
||||
options.headerClass,
|
||||
]"
|
||||
>
|
||||
<div class="modal-title">
|
||||
<slot name="title">
|
||||
{{ options.title }}
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<v-divider v-if="options.showHeaderDivider && options.showHeader !== false" />
|
||||
<!-- Close button -->
|
||||
<v-btn
|
||||
v-if="options.showCloseButton !== false && !options.persistent"
|
||||
icon
|
||||
variant="text"
|
||||
size="small"
|
||||
:color="options.closeButtonColor || 'grey'"
|
||||
@click="closeModal"
|
||||
class="modal-close-btn"
|
||||
>
|
||||
<v-icon>{{ options.closeIcon || "mdi-close" }}</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<!-- Content Section -->
|
||||
<v-card-text
|
||||
:class="[
|
||||
'modal-content',
|
||||
options.contentClass,
|
||||
{
|
||||
'pa-0': options.noPadding,
|
||||
'overflow-y-auto': options.scrollable
|
||||
}
|
||||
]"
|
||||
:style="contentStyle"
|
||||
>
|
||||
<slot>
|
||||
<!-- Default content if no slot provided -->
|
||||
<div v-if="options.message" v-html="options.message"></div>
|
||||
</slot>
|
||||
</v-card-text>
|
||||
<v-divider v-if="options.showHeaderDivider && options.showHeader !== false" />
|
||||
|
||||
<!-- Actions Section -->
|
||||
<v-card-actions
|
||||
v-if="options.showActions !== false || $slots.actions"
|
||||
:class="[
|
||||
'modal-actions',
|
||||
options.actionsClass,
|
||||
{
|
||||
'justify-end': options.actionsAlign === 'right',
|
||||
'justify-center': options.actionsAlign === 'center',
|
||||
'justify-start': options.actionsAlign === 'left',
|
||||
'justify-space-between': options.actionsAlign === 'space-between'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<slot name="actions" :close="closeModal" :options="options">
|
||||
<!-- Default action buttons -->
|
||||
<v-btn
|
||||
v-if="options.showCancelButton !== false && !options.persistent"
|
||||
:color="options.cancelButtonColor || 'grey'"
|
||||
:variant="options.cancelButtonVariant || 'text'"
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ options.cancelButtonText || 'Cancel' }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-if="options.showConfirmButton !== false"
|
||||
:color="options.confirmButtonColor || 'primary'"
|
||||
:variant="options.confirmButtonVariant || 'elevated'"
|
||||
:loading="options.loading"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ options.confirmButtonText || 'Confirm' }}
|
||||
</v-btn>
|
||||
</slot>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<!-- Content Section -->
|
||||
<v-card-text
|
||||
:class="[
|
||||
'modal-content',
|
||||
options.contentClass,
|
||||
{
|
||||
'pa-0': options.noPadding,
|
||||
'overflow-y-auto': options.scrollable,
|
||||
},
|
||||
]"
|
||||
:style="contentStyle"
|
||||
>
|
||||
<slot>
|
||||
<!-- Default content if no slot provided -->
|
||||
<div v-if="options.message" v-html="options.message"></div>
|
||||
</slot>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Actions Section -->
|
||||
<v-card-actions
|
||||
v-if="options.showActions !== false || $slots.actions"
|
||||
:class="[
|
||||
'modal-actions',
|
||||
options.actionsClass,
|
||||
{
|
||||
'justify-end': options.actionsAlign === 'right',
|
||||
'justify-center': options.actionsAlign === 'center',
|
||||
'justify-start': options.actionsAlign === 'left',
|
||||
'justify-space-between': options.actionsAlign === 'space-between',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<slot name="actions" :close="closeModal" :options="options">
|
||||
<!-- Default action buttons -->
|
||||
<v-btn
|
||||
v-if="options.showCancelButton !== false"
|
||||
:color="options.cancelButtonColor || 'grey'"
|
||||
:variant="options.cancelButtonVariant || 'text'"
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ options.cancelButtonText || "Cancel" }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-if="options.showConfirmButton !== false"
|
||||
:color="options.confirmButtonColor || 'primary'"
|
||||
:variant="options.confirmButtonVariant || 'elevated'"
|
||||
:loading="options.loading"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ options.confirmButtonText || "Confirm" }}
|
||||
</v-btn>
|
||||
</slot>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch } from 'vue'
|
||||
import { computed, watch } from "vue";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
// Modal visibility state
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// Options object for configuration
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
// Modal visibility state
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
// Options object for configuration
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits([
|
||||
'update:visible',
|
||||
'close',
|
||||
'confirm',
|
||||
'cancel',
|
||||
'outside-click',
|
||||
'escape-key'
|
||||
])
|
||||
"update:visible",
|
||||
"close",
|
||||
"confirm",
|
||||
"cancel",
|
||||
"outside-click",
|
||||
"escape-key",
|
||||
]);
|
||||
|
||||
// Local visibility state that syncs with parent
|
||||
const localVisible = computed({
|
||||
get() {
|
||||
return props.visible
|
||||
},
|
||||
set(value) {
|
||||
emit('update:visible', value)
|
||||
}
|
||||
})
|
||||
get() {
|
||||
return props.visible;
|
||||
},
|
||||
set(value) {
|
||||
emit("update:visible", value);
|
||||
},
|
||||
});
|
||||
|
||||
// Computed styles for content area
|
||||
const contentStyle = computed(() => {
|
||||
const styles = {}
|
||||
|
||||
if (props.options.contentHeight) {
|
||||
styles.height = props.options.contentHeight
|
||||
}
|
||||
|
||||
if (props.options.contentMaxHeight) {
|
||||
styles.maxHeight = props.options.contentMaxHeight
|
||||
}
|
||||
|
||||
if (props.options.contentMinHeight) {
|
||||
styles.minHeight = props.options.contentMinHeight
|
||||
}
|
||||
|
||||
return styles
|
||||
})
|
||||
const styles = {};
|
||||
|
||||
if (props.options.contentHeight) {
|
||||
styles.height = props.options.contentHeight;
|
||||
}
|
||||
|
||||
if (props.options.contentMaxHeight) {
|
||||
styles.maxHeight = props.options.contentMaxHeight;
|
||||
}
|
||||
|
||||
if (props.options.contentMinHeight) {
|
||||
styles.minHeight = props.options.contentMinHeight;
|
||||
}
|
||||
|
||||
return styles;
|
||||
});
|
||||
|
||||
// Methods
|
||||
const closeModal = () => {
|
||||
localVisible.value = false
|
||||
emit('close')
|
||||
}
|
||||
localVisible.value = false;
|
||||
emit("close");
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
emit('confirm')
|
||||
|
||||
// Auto-close unless specified not to
|
||||
if (props.options.autoCloseOnConfirm !== false) {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
emit("confirm");
|
||||
|
||||
// Auto-close unless specified not to
|
||||
if (props.options.autoCloseOnConfirm !== false) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
|
||||
// Auto-close unless specified not to
|
||||
if (props.options.autoCloseOnCancel !== false) {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
emit("cancel");
|
||||
|
||||
// Auto-close unless specified not to
|
||||
if (props.options.autoCloseOnCancel !== false) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
emit('outside-click')
|
||||
|
||||
// Close on outside click unless persistent or disabled
|
||||
if (!props.options.persistent && props.options.closeOnOutsideClick !== false) {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
emit("outside-click");
|
||||
|
||||
// Close on outside click unless persistent or disabled
|
||||
if (!props.options.persistent && props.options.closeOnOutsideClick !== false) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscapeKey = () => {
|
||||
emit('escape-key')
|
||||
|
||||
// Close on escape key unless persistent or disabled
|
||||
if (!props.options.persistent && props.options.closeOnEscape !== false) {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
emit("escape-key");
|
||||
|
||||
// Close on escape key unless persistent or disabled
|
||||
if (!props.options.persistent && props.options.closeOnEscape !== false) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
// Watch for external visibility changes
|
||||
watch(() => props.visible, (newValue) => {
|
||||
if (newValue && props.options.onOpen) {
|
||||
props.options.onOpen()
|
||||
} else if (!newValue && props.options.onClose) {
|
||||
props.options.onClose()
|
||||
}
|
||||
})
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newValue) => {
|
||||
if (newValue && props.options.onOpen) {
|
||||
props.options.onOpen();
|
||||
} else if (!newValue && props.options.onClose) {
|
||||
props.options.onClose();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-card {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background-color: var(--v-theme-surface-variant);
|
||||
padding: 16px 24px;
|
||||
background-color: var(--v-theme-surface-variant);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-close-btn {
|
||||
flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
padding: 16px 24px;
|
||||
gap: 8px;
|
||||
padding: 16px 24px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 600px) {
|
||||
.modal-header {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.modal-header {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom transitions */
|
||||
.v-dialog--fullscreen .modal-card {
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.modal-card.loading {
|
||||
pointer-events: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue