fix(портал-мобилка): диалоги на телефоне — на весь экран (fullscreen)

Узкие центрированные модалки на телефоне резали содержимое (ряды кнопок,
вкладки, дни недели, сетку регионов — контент вылезал за края, текст обрезался
слева и справа). Диалоги с богатым содержимым теперь на телефоне открываются
на весь экран (:fullscreen по useLayoutTier + scrollable):
- Проект (создание/редактирование) — NewProjectDialog
- Новая сделка — NewDealDialog
- Пополнение баланса — TopupDialog
- Дни оптом — DaysBulkDialog (те же кнопки дней)
- Регионы оптом — RegionsBulkDialog
- Импорт: сопоставление статусов — UnknownStatusesDialog
Десктоп/планшет — прежний центрированный модал, без изменений.
Тесты диалогов 59/59 (+2 skip) зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-07 20:46:47 +03:00
parent 21505163b2
commit 25116a8abb
6 changed files with 36 additions and 6 deletions
@@ -12,6 +12,10 @@ import { ref, computed, watch } from 'vue';
import { topup, createInvoice } from '../../api/billing';
import { extractErrorMessage, extractValidationErrors } from '../../api/client';
import { redirectTo } from '../../utils/redirect';
import { useLayoutTier } from '../../composables/useLayoutTier';
// Телефон: диалог на весь экран (плитки сумм/способы оплаты — шире узкого модала).
const { isPhone } = useLayoutTier();
const model = defineModel<boolean>({ required: true });
const emit = defineEmits<{ success: [balanceRub: string]; invoiced: [invoiceNumber: string] }>();
@@ -95,7 +99,7 @@ defineExpose({ method, amount, submit, canSubmit, errorMsg });
</script>
<template>
<v-dialog v-model="model" max-width="460">
<v-dialog v-model="model" max-width="460" :fullscreen="isPhone" scrollable>
<v-card>
<v-card-title class="text-h6">Пополнить баланс</v-card-title>
<v-card-text>
@@ -15,6 +15,10 @@ import { extractErrorMessage } from '../../api/client';
import { ref, watch } from 'vue';
import { LEAD_STATUSES } from '../../composables/leadStatuses';
import { type MockDeal, type MockManager } from '../../composables/mockDeals';
import { useLayoutTier } from '../../composables/useLayoutTier';
// Телефон: диалог на весь экран (узкий модал резал поля).
const { isPhone } = useLayoutTier();
/**
* Списки проектов и менеджеров грузятся с backend через GET /api/projects,
@@ -184,7 +188,7 @@ function close() {
</script>
<template>
<v-dialog v-model="dialogOpen" :max-width="560" data-testid="new-deal-dialog">
<v-dialog v-model="dialogOpen" :max-width="560" :fullscreen="isPhone" scrollable data-testid="new-deal-dialog">
<v-card>
<v-card-title>Новая сделка</v-card-title>
<v-card-text>
@@ -7,6 +7,10 @@
*/
import { computed, reactive, ref } from 'vue';
import { resolveUnknownStatuses, type StatusMapping, type UnknownStatus } from '../../api/imports';
import { useLayoutTier } from '../../composables/useLayoutTier';
// Телефон: на весь экран (таблица сопоставления статусов широкая).
const { isPhone } = useLayoutTier();
const props = defineProps<{
modelValue: boolean;
@@ -62,7 +66,7 @@ defineExpose({ selection, save });
</script>
<template>
<v-dialog v-model="dialogOpen" max-width="640">
<v-dialog v-model="dialogOpen" max-width="640" :fullscreen="isPhone" scrollable>
<v-card>
<v-card-title class="text-h6">Маппинг неизвестных статусов</v-card-title>
<v-card-text>
@@ -1,5 +1,5 @@
<template>
<v-dialog v-model="open" max-width="560">
<v-dialog v-model="open" max-width="560" :fullscreen="isPhone" scrollable>
<v-card>
<v-card-title>Дни сбора лидов для {{ count }} проектов</v-card-title>
<v-card-text>
@@ -48,6 +48,10 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { WEEKDAYS } from '../../constants/weekdays';
import { useLayoutTier } from '../../composables/useLayoutTier';
// Телефон: на весь экран (ряд кнопок дней недели не влезал в узкий модал).
const { isPhone } = useLayoutTier();
const props = defineProps<{ modelValue: boolean; count: number }>();
const emit = defineEmits<{
@@ -1,5 +1,5 @@
<template>
<v-dialog v-model="open" max-width="560">
<v-dialog v-model="open" max-width="560" :fullscreen="isPhone" scrollable>
<v-card>
<v-card-title>Регионы для {{ count }} проектов</v-card-title>
<v-card-text>
@@ -76,6 +76,10 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { REGIONS_ALPHA, FEDERAL_DISTRICT_NAMES } from '../../constants/regions';
import { useLayoutTier } from '../../composables/useLayoutTier';
// Телефон: на весь экран (сетка регионов шире узкого модала).
const { isPhone } = useLayoutTier();
const props = defineProps<{ modelValue: boolean; count: number }>();
const emit = defineEmits<{
@@ -1,5 +1,11 @@
<template>
<v-dialog :model-value="modelValue" max-width="720" @update:model-value="$emit('update:modelValue', $event)">
<v-dialog
:model-value="modelValue"
max-width="720"
:fullscreen="isPhone"
scrollable
@update:model-value="$emit('update:modelValue', $event)"
>
<v-card style="position: relative">
<DevIndexBadge
:index="mode === 'edit' ? 19 : 18"
@@ -330,6 +336,7 @@
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue';
import { useLayoutTier } from '../../composables/useLayoutTier';
import { apiClient, ensureCsrfCookie, extractErrorMessage } from '../../api/client';
import { getRequisites, updateRequisites, type Requisites } from '../../api/requisites';
import { firstLeadDate, formatLeadDate } from '../../utils/leadDate';
@@ -408,6 +415,9 @@ const emit = defineEmits<{
saved: [appliesFrom: string | null];
}>();
// На телефоне диалог — на весь экран (иначе узкий модал режет ряды кнопок/дней).
const { isPhone } = useLayoutTier();
// Plan 6: regions = subject codes (1..89) — backend dual-writes region_mask/region_mode.
// Пустой массив = вся РФ.
const form = reactive({