1
0

Исправления фронта

Множество оптимизаций по фронту
This commit is contained in:
2026-01-16 10:15:33 +07:00
parent cb075e56be
commit 3258fa9137
30 changed files with 1797 additions and 2584 deletions

View File

@@ -2,27 +2,28 @@
<Transition name="dialog">
<div v-if="show" class="dialog-overlay" @click.self="handleCancel">
<div class="dialog">
<h3>{{ title }}</h3>
<p v-html="message"></p>
<h3>{{ dialogTitle }}</h3>
<p v-html="dialogMessage"></p>
<div class="dialog-buttons">
<button class="btn-cancel" @click="handleCancel">
{{ cancelText }}
<button class="btn-cancel" @click="handleCancel" :disabled="loading">
{{ dialogCancelText }}
</button>
<button
v-if="showDiscard"
v-if="dialogShowDiscard"
class="btn-discard"
@click="handleDiscard"
:disabled="loading"
>
{{ discardText }}
{{ dialogDiscardText }}
</button>
<button
class="btn-confirm"
:class="variant"
:class="dialogVariant"
@click="handleConfirm"
:disabled="isLoading"
:disabled="loading"
>
<span v-if="isLoading" class="btn-loader"></span>
<span v-else>{{ confirmText }}</span>
<span v-if="loading" class="btn-loader"></span>
<span v-else>{{ dialogConfirmText }}</span>
</button>
</div>
</div>
@@ -31,58 +32,87 @@
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { DIALOGS } from '../stores/dialogs'
const props = defineProps({
show: {
type: Boolean,
default: false
},
title: {
// Тип диалога из конфига (archive, restore, deleteTask, etc.)
type: {
type: String,
default: 'Подтверждение'
default: null
},
message: {
type: String,
default: 'Вы уверены?'
},
confirmText: {
type: String,
default: 'Подтвердить'
},
cancelText: {
type: String,
default: 'Отмена'
},
discardText: {
type: String,
default: 'Не сохранять'
},
showDiscard: {
type: Boolean,
default: false
},
// Варианты: 'default', 'danger', 'warning'
variant: {
type: String,
default: 'default'
},
// Состояние загрузки (блокирует кнопку подтверждения)
isLoading: {
type: Boolean,
default: false
// Прямые props (переопределяют type если заданы)
title: String,
message: String,
confirmText: String,
cancelText: String,
discardText: String,
showDiscard: Boolean,
variant: String,
// Async callback для подтверждения — сам управляет loading
action: {
type: Function,
default: null
}
})
// Получаем конфиг по типу
const config = computed(() => props.type ? DIALOGS[props.type] : {})
// Computed свойства с fallback: props → config → default
const dialogTitle = computed(() => props.title ?? config.value.title ?? 'Подтверждение')
const dialogMessage = computed(() => props.message ?? config.value.message ?? 'Вы уверены?')
const dialogConfirmText = computed(() => props.confirmText ?? config.value.confirmText ?? 'Подтвердить')
const dialogCancelText = computed(() => props.cancelText ?? 'Отмена')
const dialogDiscardText = computed(() => props.discardText ?? 'Не сохранять')
const dialogShowDiscard = computed(() => props.showDiscard ?? config.value.showDiscard ?? false)
const dialogVariant = computed(() => props.variant ?? config.value.variant ?? 'default')
const emit = defineEmits(['confirm', 'cancel', 'discard'])
const handleConfirm = () => {
emit('confirm')
// Внутреннее состояние загрузки
const loading = ref(false)
// Сброс состояния при закрытии диалога
watch(() => props.show, (newVal) => {
if (!newVal) {
loading.value = false
}
})
const handleConfirm = async () => {
if (loading.value) return
// Если есть async action — вызываем его и управляем loading
if (props.action) {
loading.value = true
try {
await props.action()
// Успех — эмитим confirm для закрытия
emit('confirm')
} catch (e) {
console.error('ConfirmDialog action failed:', e)
// При ошибке — не закрываем диалог
} finally {
loading.value = false
}
} else {
// Простой режим — просто эмитим
emit('confirm')
}
}
const handleCancel = () => {
if (loading.value) return
emit('cancel')
}
const handleDiscard = () => {
if (loading.value) return
emit('discard')
}
</script>