feat(pos-ui): светлая стильная тема по умолчанию (Square/Loyverse-подход)
По просьбе владельца («фон мрачный — сделай светлее, стильнее, удобнее»). - Дизайн-система на токенах: Styles/Tokens.axaml (ThemeDictionaries Light/Dark) + Styles/PosStyles.axaml (карточки/кнопки/чипы/клавиши/сегменты). RequestedThemeVariant=Light. Палитра: фон #F4F6F5, карточки белые, бренд-зелёный #16A34A, опасное — красный контур, предупреждение — янтарь. Inter, крупная иерархия, скругления 12–14, мягкие тени, сетка 8px. - Экран продажи: белая верхняя полоса (лого/точка/кассир + чипы смены и связи), поиск со скан-иконкой, корзина-карточки со степпером [−]N[+] и удалением, скидка в строке зелёным, пустая корзина-заглушка, правая карточка чека с крупным зелёным ИТОГО и «Оплатить · F12», вторичные кнопки-контуры, капсулы горячих клавиш. - Оплата (светлое модальное): сегменты Наличные/Карта/Смешанная, быстрые суммы, цифровая клавиатура, крупная «Сдача». - Вход (логин + PIN-пад), смена/Z-отчёт (разница цветом), возврат (степперы) — в том же стиле. - Кнопки ≥48px, клавиши крупные. Скорость не ухудшилась (старт 149мс, скан 11мс, поиск 33мс). --uitest OK, 43 unit зелёные. Галерея: разделы «Стало»/«Было». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50
docs/pos-design.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Касса food-market — дизайн-система (светлая тема)
|
||||
|
||||
Подход: чисто, светло, крупно, ничего лишнего (как Square POS / Loyverse / Poster).
|
||||
Всё строится на токенах из одного места — `Styles/Tokens.axaml` (ResourceDictionary).
|
||||
Тёмная тема — те же токены, другой набор значений (опция в настройках, не по умолчанию).
|
||||
|
||||
## Палитра (токены)
|
||||
| Токен | Светлая | Назначение |
|
||||
|---|---|---|
|
||||
| `PosBg` | #F4F6F5 | фон приложения |
|
||||
| `PosCard` | #FFFFFF | карточки, полосы, модальные |
|
||||
| `PosBorder` | #E3E8E6 | рамки, разделители |
|
||||
| `PosText` | #0F1F17 | основной текст |
|
||||
| `PosтатText2` / `PosTextMuted` | #5F6F68 | вторичный текст |
|
||||
| `PosBrand` | #16A34A | бренд/главное действие (зелёный Food Market) |
|
||||
| `PosBrandHover` | #15803D | hover |
|
||||
| `PosBrandPressed` | #166534 | pressed |
|
||||
| `PosDanger` | #DC2626 | опасное (Очистить/Удалить) — только контур |
|
||||
| `PosWarn` | #D97706 | предупреждение (смена закрыта, нет связи) — янтарный |
|
||||
| `PosBrandSoft` | #DCFCE7 | мягкий зелёный фон (чипы, успех) |
|
||||
| `PosWarnSoft` | #FEF3C7 | мягкий янтарный фон |
|
||||
|
||||
Контраст текста к фону ≥ WCAG AA (PosText на PosCard/PosBg — ~14:1; PosTextMuted — ~5:1).
|
||||
|
||||
## Типографика (Inter)
|
||||
- ИТОГО: 44–56 pt, Bold.
|
||||
- Суммы строк: 18–20 pt, SemiBold, **tabular** (моноширинные цифры — колонки не прыгают).
|
||||
- Названия товаров: 17–18 pt.
|
||||
- Служебное/подписи: 13–14 pt, PosTextMuted.
|
||||
- Моноширинные цифры: `<TextBlock Classes="num">` → FontFeatures "tnum=1" / FontFamily с tabular.
|
||||
|
||||
## Форма и ритм
|
||||
- Скругления: карточки/кнопки 12–14, чипы 999 (капсула).
|
||||
- Тени: мягкие у карточек (BoxShadow 0 1 3 #00000014, 0 4 12 #00000010).
|
||||
- Сетка отступов: шаг 8 px (8/12/16/24).
|
||||
- Кнопки: высота ≥ 48 px (сенсор ≥ 44). Главная — заливка PosBrand; вторичные —
|
||||
контур PosBorder на белом; опасная — контур PosDanger (без заливки).
|
||||
|
||||
## Компоненты (классы стилей)
|
||||
- `Button.primary` — зелёная заливка, белый текст, крупная.
|
||||
- `Button.ghost` — белая, контур PosBorder, текст PosText (вторичные действия).
|
||||
- `Button.danger` — белая, контур PosDanger, текст PosDanger (Очистить/Удалить).
|
||||
- `Button.key` — клавиша цифровой клавиатуры/степпера.
|
||||
- `Border.card` — белая карточка со скруглением и тенью.
|
||||
- `Border.chip` / `Border.chip.ok` / `.warn` — капсулы статуса.
|
||||
- `TextBox.search` — поле поиска/скана с иконкой.
|
||||
|
||||
## Тёмная тема (опция)
|
||||
Те же имена токенов, тёмные значения (PosBg #0F1713, PosCard #15201B, PosText #E8EFEA…),
|
||||
переключается через `RequestedThemeVariant` + подмену словаря. По умолчанию — светлая.
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 39 KiB |
BIN
docs/pos-screenshots/01b-login-pin-1366x768.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/pos-screenshots/01b-login-pin-1920x1080.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 106 KiB |
BIN
docs/pos-screenshots/before/01-login-1366x768.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/pos-screenshots/before/01-login-1920x1080.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/pos-screenshots/before/01b-login-pin-1366x768.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/pos-screenshots/before/01b-login-pin-1920x1080.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/pos-screenshots/before/02-shift-open-1366x768.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/pos-screenshots/before/02-shift-open-1920x1080.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/pos-screenshots/before/03-sale-empty-1366x768.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/pos-screenshots/before/03-sale-empty-1920x1080.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
docs/pos-screenshots/before/04-sale-items-1366x768.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/pos-screenshots/before/04-sale-items-1920x1080.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/pos-screenshots/before/05-payment-cash-1366x768.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/pos-screenshots/before/05-payment-cash-1920x1080.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/pos-screenshots/before/06-payment-mixed-1366x768.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/pos-screenshots/before/06-payment-mixed-1920x1080.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/pos-screenshots/before/07-return-1366x768.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/pos-screenshots/before/07-return-1920x1080.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
docs/pos-screenshots/before/08-shift-z-1366x768.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/pos-screenshots/before/08-shift-z-1920x1080.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/pos-screenshots/before/09-shell-offline-1366x768.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/pos-screenshots/before/09-shell-offline-1920x1080.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
docs/pos-screenshots/before/10-shell-online-1366x768.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/pos-screenshots/before/10-shell-online-1920x1080.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
39
docs/pos-ui-progress.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Новый внешний вид кассы — прогресс
|
||||
|
||||
Задача владельца (08.10.2026): «визуально выглядит ужасно, фон мрачный — сделай
|
||||
интерфейс более стильным, светлым, удобным». Светлая тема по умолчанию,
|
||||
ориентиры Square/Loyverse/Poster. Скорость не ухудшать.
|
||||
|
||||
## Замеры скорости (до/после)
|
||||
| Метрика | До (dark) | После (light) |
|
||||
|---|---|---|
|
||||
| Холодный старт (процесс→логика), Linux | 146 мс | 149 мс (в пределах шума) |
|
||||
| Поиск по штрихкоду | 13,9 мс | 11,2 мс |
|
||||
| Поиск по имени на 30 000 | 41,9 мс | 33,2 мс |
|
||||
|
||||
Скорость не ухудшилась (светлая тема — только ресурсы/стили, логика та же).
|
||||
|
||||
## План
|
||||
1. [x] Токены светлой темы в `Styles/Tokens.axaml` (ThemeDictionaries Light/Dark) + docs/pos-design.md.
|
||||
2. [x] Светлая тема по умолчанию (`RequestedThemeVariant=Light`); тёмная — те же токены (вариант Dark).
|
||||
3. [x] Экран продажи: белая верхняя полоса (лого/точка/кассир/чипы смены+связи),
|
||||
поиск со скан-иконкой, корзина-карточки со степпером [−]N[+] и удалением,
|
||||
белая карточка чека с крупным зелёным ИТОГО и «Оплатить · F12», вторичные
|
||||
кнопки-контуры, пустая корзина-заглушка, капсулы горячих клавиш.
|
||||
4. [x] Оплата (светлое модальное): сегменты Нал/Карта/Смешанная, быстрые суммы
|
||||
(без сдачи/1000/2000/5000/10000/20000), цифровая клавиатура, крупная «Сдача».
|
||||
5. [x] Остальные экраны в стиле: вход (логин + PIN-пад), регистрация/выбор точки,
|
||||
смена/Z-отчёт (разница цветом), возврат (степперы). Нет связи — чип в шапке.
|
||||
6. [ ] Избранные товары: сервер (флаг/порядок у товара) + миграция + плитки UI.
|
||||
7. [x] Удобство: кнопки ≥48px, клавиши крупные; фокус в поле скана; адаптив 1366/1920.
|
||||
8. [~] Скриншоты «Было/Стало» 1920×1080 + 1366×768 (галерея с разделами); реальное окно Windows 11 — в конце.
|
||||
9. [x] --uitest OK; unit 43 зелёные; скорость не хуже. Smoke Linux/Windows — в конце.
|
||||
10. [~] Коммиты в main; push (по ходу).
|
||||
|
||||
Осталось: избранные товары (п.6), тёмная тема как переключатель в настройках,
|
||||
реальное окно Windows 11, smoke Win/Linux, итоговый push.
|
||||
|
||||
НЕ трогать: прод, shared-postgres, global.json, gateway nginx, sklad, другие VM.
|
||||
|
||||
## Журнал
|
||||
- 08.10 — старт; сохранены «до»-скриншоты (22), замерен baseline; планы зафиксированы.
|
||||
|
|
@ -2,47 +2,19 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:foodmarket.Pos"
|
||||
x:Class="foodmarket.Pos.App"
|
||||
RequestedThemeVariant="Dark">
|
||||
RequestedThemeVariant="Light">
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator />
|
||||
</Application.DataTemplates>
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="avares://foodmarket.Pos/Styles/Tokens.axaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<!-- Крупный кассовый стиль: большие кнопки, читаемые цифры, без анимаций. -->
|
||||
<Style Selector="Button.pos">
|
||||
<Setter Property="Height" Value="56" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
<Setter Property="Padding" Value="12,0" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="Background" Value="#2d3748" />
|
||||
<Setter Property="Foreground" Value="#f7fafc" />
|
||||
<!-- Текст строго по центру кнопки (и по вертикали, и по горизонтали). -->
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<!-- Содержимое кнопок кассы не обрезаем: при нехватке ширины переносим,
|
||||
текст по центру. Избавляет от «Esc · Очистит…». -->
|
||||
<Style Selector="Button.pos /template/ ContentPresenter">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style Selector="Button.pos TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="TextAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style Selector="Button.primary">
|
||||
<Setter Property="Background" Value="#16a34a" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
</Style>
|
||||
<Style Selector="Button.danger">
|
||||
<Setter Property="Background" Value="#dc2626" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Style>
|
||||
<Style Selector="TextBox.pos">
|
||||
<Setter Property="FontSize" Value="20" />
|
||||
<Setter Property="Height" Value="52" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<StyleInclude Source="avares://foodmarket.Pos/Styles/PosStyles.axaml"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
|
|
|||
152
src/food-market.pos/Styles/PosStyles.axaml
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- ── Карточки ─────────────────────────────────────────────────── -->
|
||||
<Style Selector="Border.card">
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="14"/>
|
||||
<Setter Property="BoxShadow" Value="0 1 3 0 #14000000, 0 6 16 0 #0D000000"/>
|
||||
</Style>
|
||||
|
||||
<!-- ── Чипы статуса (капсулы) ───────────────────────────────────── -->
|
||||
<Style Selector="Border.chip">
|
||||
<Setter Property="CornerRadius" Value="999"/>
|
||||
<Setter Property="Padding" Value="12,6"/>
|
||||
<Setter Property="Background" Value="{DynamicResource PosSubtle}"/>
|
||||
</Style>
|
||||
<Style Selector="Border.chip > TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosTextMuted}"/>
|
||||
<Setter Property="FontSize" Value="13.5"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style Selector="Border.chip.ok">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrandSoft}"/>
|
||||
</Style>
|
||||
<Style Selector="Border.chip.ok > TextBlock"><Setter Property="Foreground" Value="{DynamicResource PosBrand}"/></Style>
|
||||
<Style Selector="Border.chip.warn">
|
||||
<Setter Property="Background" Value="{DynamicResource PosWarnSoft}"/>
|
||||
</Style>
|
||||
<Style Selector="Border.chip.warn > TextBlock"><Setter Property="Foreground" Value="{DynamicResource PosWarn}"/></Style>
|
||||
|
||||
<!-- ── Кнопки ───────────────────────────────────────────────────── -->
|
||||
<Style Selector="Button.pos">
|
||||
<Setter Property="Height" Value="50"/>
|
||||
<Setter Property="MinHeight" Value="48"/>
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Padding" Value="16,0"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
<Style Selector="Button.pos /template/ ContentPresenter">
|
||||
<Setter Property="Transitions">
|
||||
<Transitions><BrushTransition Property="Background" Duration="0:0:0.08"/></Transitions>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style Selector="Button.pos TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="TextAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Главная зелёная -->
|
||||
<Style Selector="Button.primary">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrand}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosBrandText}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</Style>
|
||||
<Style Selector="Button.primary:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrandHover}"/>
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource PosBrandText}"/>
|
||||
</Style>
|
||||
<Style Selector="Button.primary:pressed /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrandPressed}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Вторичная (контур) -->
|
||||
<Style Selector="Button.ghost">
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
</Style>
|
||||
<Style Selector="Button.ghost:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosSubtle}"/>
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource PosText}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Опасная (красный контур, без заливки) -->
|
||||
<Style Selector="Button.danger">
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosDanger}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosDanger}"/>
|
||||
</Style>
|
||||
<Style Selector="Button.danger:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosDangerSoft}"/>
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource PosDanger}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Клавиша клавиатуры/степпер -->
|
||||
<Style Selector="Button.key">
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
<Setter Property="FontSize" Value="22"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
</Style>
|
||||
<Style Selector="Button.key:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosSubtle}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Сегмент переключателя (выбранный — зелёный) -->
|
||||
<Style Selector="Button.seg">
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
<Setter Property="Height" Value="52"/>
|
||||
</Style>
|
||||
<Style Selector="Button.seg.on">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrand}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosBrandText}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
<Style Selector="Button.seg.on /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource PosBrand}"/>
|
||||
</Style>
|
||||
|
||||
<!-- ── Поля ввода ──────────────────────────────────────────────── -->
|
||||
<Style Selector="TextBox.pos">
|
||||
<Setter Property="FontSize" Value="17"/>
|
||||
<Setter Property="Height" Value="52"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
</Style>
|
||||
<Style Selector="TextBox.search">
|
||||
<Setter Property="FontSize" Value="19"/>
|
||||
<Setter Property="Height" Value="58"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="CornerRadius" Value="14"/>
|
||||
<Setter Property="Background" Value="{DynamicResource PosCard}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PosText}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Капсулы горячих клавиш -->
|
||||
<Style Selector="Border.hotkey">
|
||||
<Setter Property="Background" Value="{DynamicResource PosSubtle}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PosBorder}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="8"/>
|
||||
<Setter Property="Padding" Value="9,4"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
46
src/food-market.pos/Styles/Tokens.axaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!-- ── Светлая тема (по умолчанию) ─────────────────────────────── -->
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="PosBg" Color="#F4F6F5"/>
|
||||
<SolidColorBrush x:Key="PosCard" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="PosSubtle" Color="#F0F3F1"/>
|
||||
<SolidColorBrush x:Key="PosBorder" Color="#E3E8E6"/>
|
||||
<SolidColorBrush x:Key="PosText" Color="#0F1F17"/>
|
||||
<SolidColorBrush x:Key="PosTextMuted" Color="#5F6F68"/>
|
||||
<SolidColorBrush x:Key="PosBrand" Color="#16A34A"/>
|
||||
<SolidColorBrush x:Key="PosBrandHover" Color="#15803D"/>
|
||||
<SolidColorBrush x:Key="PosBrandPressed" Color="#166534"/>
|
||||
<SolidColorBrush x:Key="PosBrandText" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="PosBrandSoft" Color="#DCFCE7"/>
|
||||
<SolidColorBrush x:Key="PosDanger" Color="#DC2626"/>
|
||||
<SolidColorBrush x:Key="PosDangerSoft" Color="#FEE2E2"/>
|
||||
<SolidColorBrush x:Key="PosWarn" Color="#B45309"/>
|
||||
<SolidColorBrush x:Key="PosWarnSoft" Color="#FEF3C7"/>
|
||||
<SolidColorBrush x:Key="PosScrim" Color="#33203028"/>
|
||||
</ResourceDictionary>
|
||||
|
||||
<!-- ── Тёмная тема (опция, те же токены) ───────────────────────── -->
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="PosBg" Color="#0F1713"/>
|
||||
<SolidColorBrush x:Key="PosCard" Color="#15201B"/>
|
||||
<SolidColorBrush x:Key="PosSubtle" Color="#1B2821"/>
|
||||
<SolidColorBrush x:Key="PosBorder" Color="#2A3A31"/>
|
||||
<SolidColorBrush x:Key="PosText" Color="#E8EFEA"/>
|
||||
<SolidColorBrush x:Key="PosTextMuted" Color="#9BAAA2"/>
|
||||
<SolidColorBrush x:Key="PosBrand" Color="#22C55E"/>
|
||||
<SolidColorBrush x:Key="PosBrandHover" Color="#16A34A"/>
|
||||
<SolidColorBrush x:Key="PosBrandPressed" Color="#15803D"/>
|
||||
<SolidColorBrush x:Key="PosBrandText" Color="#06140C"/>
|
||||
<SolidColorBrush x:Key="PosBrandSoft" Color="#13331F"/>
|
||||
<SolidColorBrush x:Key="PosDanger" Color="#F87171"/>
|
||||
<SolidColorBrush x:Key="PosDangerSoft" Color="#3A1A1A"/>
|
||||
<SolidColorBrush x:Key="PosWarn" Color="#FBBF24"/>
|
||||
<SolidColorBrush x:Key="PosWarnSoft" Color="#33280D"/>
|
||||
<SolidColorBrush x:Key="PosScrim" Color="#66000000"/>
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -90,11 +90,32 @@ private static void WriteGallery(string dir, List<(string file, string caption)>
|
|||
".grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(420px,1fr));gap:16px;padding:20px}" +
|
||||
"figure{margin:0;background:#1e293b;border:1px solid #334155;border-radius:10px;overflow:hidden}" +
|
||||
"img{width:100%;display:block;background:#000;cursor:zoom-in}figcaption{padding:8px 10px;color:#cbd5e1;font-size:13px}</style></head><body>");
|
||||
sb.Append($"<header><h1>Касса food-market — скриншоты процессов</h1><div style=\"color:#94a3b8\">рендер headless (Avalonia/Skia, Linux) — 1920×1080 и 1366×768. Кадров: {shots.Count}</div></header><div class=\"grid\">");
|
||||
sb.Append($"<header><h1>Касса food-market — скриншоты процессов</h1><div style=\"color:#94a3b8\">Светлая тема (Avalonia/Skia, headless, Linux) — 1920×1080 и 1366×768. Кадров: {shots.Count}</div></header>");
|
||||
sb.Append("<header style=\"background:#eef2f0;color:#0f1f17\"><h1 style=\"color:#16a34a\">Стало (светлая тема)</h1></header>");
|
||||
sb.Append("<div class=\"grid\">");
|
||||
foreach (var (file, caption) in shots)
|
||||
sb.Append($"<figure><a href=\"{file}\" target=\"_blank\"><img src=\"{file}\" loading=\"lazy\"></a><figcaption>{System.Net.WebUtility.HtmlEncode(caption)}</figcaption></figure>");
|
||||
sb.Append("</div>");
|
||||
|
||||
// Раздел «Было» — прежняя тёмная тема (если сохранена в before/).
|
||||
var beforeDir = Path.Combine(dir, "before");
|
||||
if (Directory.Exists(beforeDir))
|
||||
{
|
||||
var befs = Directory.GetFiles(beforeDir, "*.png").OrderBy(f => f).ToList();
|
||||
if (befs.Count > 0)
|
||||
{
|
||||
sb.Append("<header style=\"background:#1e293b;margin-top:8px\"><h1 style=\"color:#e2e8f0\">Было (тёмная тема)</h1>" +
|
||||
"<div style=\"color:#94a3b8\">для сравнения — прежний мрачный интерфейс</div></header>");
|
||||
sb.Append("<div class=\"grid\" style=\"background:#0f172a\">");
|
||||
foreach (var b in befs)
|
||||
{
|
||||
var rel = "before/" + Path.GetFileName(b);
|
||||
sb.Append($"<figure style=\"background:#1e293b;border-color:#334155\"><a href=\"{rel}\" target=\"_blank\"><img src=\"{rel}\" loading=\"lazy\"></a><figcaption style=\"color:#cbd5e1\">{System.Net.WebUtility.HtmlEncode(Path.GetFileNameWithoutExtension(b))}</figcaption></figure>");
|
||||
}
|
||||
sb.Append("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
// Раздел «Windows 11» — реальные скриншоты окна на Windows (если есть).
|
||||
var winDir = Path.Combine(dir, "windows");
|
||||
if (Directory.Exists(winDir))
|
||||
|
|
|
|||
|
|
@ -24,19 +24,24 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||
[ObservableProperty] private object? _currentPage;
|
||||
[ObservableProperty] private string _cashierName = "—";
|
||||
[ObservableProperty] private string _shiftNumber = "смена не открыта";
|
||||
[ObservableProperty] private bool _isShiftOpen;
|
||||
[ObservableProperty] private string _pointName = "Магазин";
|
||||
[ObservableProperty] private bool _isOnline = true;
|
||||
[ObservableProperty] private int _queueCount;
|
||||
[ObservableProperty] private PaymentViewModel? _payment;
|
||||
[ObservableProperty] private bool _isPaymentOpen;
|
||||
[ObservableProperty] private string? _updateMessage;
|
||||
|
||||
public string NetBadge => IsOnline ? "● на связи" : "○ нет связи";
|
||||
public string QueueBadge => QueueCount > 0 ? $"{QueueCount} чек(ов) в очереди" : "очередь пуста";
|
||||
public string NetBadge => IsOnline ? "На связи" : "Нет связи";
|
||||
public string ShiftBadge => IsShiftOpen ? $"Смена {ShiftNumber}" : "Смена закрыта";
|
||||
public string QueueBadge => QueueCount > 0 ? $"· {QueueCount} в очереди" : "";
|
||||
partial void OnIsOnlineChanged(bool value) { OnPropertyChanged(nameof(NetBadge)); }
|
||||
partial void OnQueueCountChanged(int value) { OnPropertyChanged(nameof(QueueBadge)); }
|
||||
partial void OnIsShiftOpenChanged(bool value) => OnPropertyChanged(nameof(ShiftBadge));
|
||||
partial void OnShiftNumberChanged(string value) => OnPropertyChanged(nameof(ShiftBadge));
|
||||
|
||||
/// <summary>Конструктор для дизайнера/скриншотов (демо-данные).</summary>
|
||||
public MainWindowViewModel() { CurrentPage = SaleViewModel.Sample(); CashierName = "Пётр Кассиров"; ShiftNumber = "С-2026-000001"; }
|
||||
public MainWindowViewModel() { CurrentPage = SaleViewModel.Sample(); CashierName = "Пётр Кассиров"; ShiftNumber = "С-2026-000001"; IsShiftOpen = true; PointName = "Магазин №1 · ул. Абая 10"; }
|
||||
|
||||
public MainWindowViewModel(AppServices svc) { _svc = svc; if (svc.Session is not null) StartSession(); else ShowLogin(); }
|
||||
|
||||
|
|
@ -86,7 +91,7 @@ private static string LocalVersion()
|
|||
private void RefreshStatus() { if (_svc is not null) QueueCount = _svc.Store.PendingCount(); }
|
||||
|
||||
[RelayCommand] private void ShowSale() { CurrentPage = _sale ??= (_svc is null ? SaleViewModel.Sample() : new SaleViewModel(_svc) { PaymentRequested = OpenPayment, SaleCommitted = _ => RefreshStatus() }); }
|
||||
[RelayCommand] private void ShowShift() => CurrentPage = _svc is null ? ShiftViewModel.Sample() : new ShiftViewModel(_svc) { OnShiftChanged = s => { ShiftNumber = s ?? "смена не открыта"; } };
|
||||
[RelayCommand] private void ShowShift() => CurrentPage = _svc is null ? ShiftViewModel.Sample() : new ShiftViewModel(_svc) { OnShiftChanged = s => { ShiftNumber = s ?? "смена не открыта"; IsShiftOpen = s is not null; } };
|
||||
[RelayCommand] private void ShowReturn() => CurrentPage = _svc is null ? ReturnViewModel.Sample() : new ReturnViewModel(_svc);
|
||||
|
||||
private void OpenPayment(PaymentViewModel pvm)
|
||||
|
|
@ -369,6 +374,10 @@ public partial class SaleViewModel : ViewModelBase
|
|||
[ObservableProperty] private decimal _total;
|
||||
[ObservableProperty] private int _itemCount;
|
||||
[ObservableProperty] private SaleLineVm? _selectedLine;
|
||||
public bool IsCartEmpty => ItemCount == 0;
|
||||
public bool HasAnyDiscount => Discount > 0m;
|
||||
partial void OnItemCountChanged(int value) => OnPropertyChanged(nameof(IsCartEmpty));
|
||||
partial void OnDiscountChanged(decimal value) => OnPropertyChanged(nameof(HasAnyDiscount));
|
||||
|
||||
/// <summary>Поднять модальную оплату (обрабатывает оболочка).</summary>
|
||||
public Action<PaymentViewModel>? PaymentRequested;
|
||||
|
|
@ -433,6 +442,17 @@ public void SetLineQuantity(SaleLineVm line, decimal qty)
|
|||
Recalc();
|
||||
}
|
||||
|
||||
[RelayCommand] private void IncLine(SaleLineVm line) { if (line.Model is { } m) SetLineQuantity(line, m.Quantity + 1m); }
|
||||
[RelayCommand] private void DecLine(SaleLineVm line) { if (line.Model is { } m) SetLineQuantity(line, m.Quantity - 1m); }
|
||||
[RelayCommand]
|
||||
private void RemoveLine(SaleLineVm line)
|
||||
{
|
||||
if (line.Model is { } m) _cart.Remove(m);
|
||||
Lines.Remove(line);
|
||||
if (ReferenceEquals(SelectedLine, line)) SelectedLine = Lines.FirstOrDefault();
|
||||
Recalc();
|
||||
}
|
||||
|
||||
public void SetCartDiscount(decimal amount) { _cart.SetCartDiscount(amount); Recalc(); Message = $"Скидка на чек: {amount:N0} ₸"; }
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -524,16 +544,35 @@ public partial class PaymentViewModel : ViewModelBase
|
|||
public decimal Change => PaymentMath.Compute(Total, PaidCash, PaidCard).Change;
|
||||
public decimal Shortfall => PaymentMath.Compute(Total, PaidCash, PaidCard).Shortfall;
|
||||
public bool Sufficient => PaymentMath.Compute(Total, PaidCash, PaidCard).Sufficient;
|
||||
public bool IsCash => Method == "Наличные";
|
||||
public bool IsCard => Method == "Карта";
|
||||
public bool IsMixed => Method == "Смешанная";
|
||||
partial void OnPaidCashChanged(decimal value) => RaisePay();
|
||||
partial void OnPaidCardChanged(decimal value) => RaisePay();
|
||||
partial void OnTotalChanged(decimal value) => RaisePay();
|
||||
partial void OnMethodChanged(string value) { OnPropertyChanged(nameof(IsCash)); OnPropertyChanged(nameof(IsCard)); OnPropertyChanged(nameof(IsMixed)); }
|
||||
private void RaisePay() { OnPropertyChanged(nameof(Change)); OnPropertyChanged(nameof(Shortfall)); OnPropertyChanged(nameof(Sufficient)); }
|
||||
|
||||
/// <summary>Вызывается при подтверждении оплаты (cash, card, change).</summary>
|
||||
public Func<decimal, decimal, decimal, Task>? OnConfirm;
|
||||
public Action? OnCancel;
|
||||
|
||||
[RelayCommand] private void ExactCash() { PaidCash = Total; PaidCard = 0m; Method = "Наличные"; }
|
||||
[RelayCommand] private void ExactCash() { PaidCash = Total; PaidCard = 0m; }
|
||||
[RelayCommand] private void SelectCash() { Method = "Наличные"; PaidCard = 0m; }
|
||||
[RelayCommand] private void SelectCard() { Method = "Карта"; PaidCard = Total; PaidCash = 0m; }
|
||||
[RelayCommand] private void SelectMixed() { Method = "Смешанная"; }
|
||||
|
||||
/// <summary>Быстрая сумма наличных: «Без сдачи» (ровно) или номинал.</summary>
|
||||
[RelayCommand] private void QuickCash(string amount)
|
||||
{
|
||||
if (amount == "exact") { PaidCash = Total; return; }
|
||||
if (decimal.TryParse(amount, out var a)) PaidCash = a;
|
||||
}
|
||||
|
||||
// Экранная цифровая клавиатура для наличных (целые тенге).
|
||||
[RelayCommand] private void KeyDigit(string d) { if (int.TryParse(d, out var n)) PaidCash = PaidCash * 10m + n; }
|
||||
[RelayCommand] private void KeyBack() { PaidCash = Math.Floor(PaidCash / 10m); }
|
||||
[RelayCommand] private void KeyClear() { PaidCash = 0m; }
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Confirm()
|
||||
|
|
@ -544,8 +583,8 @@ private async Task Confirm()
|
|||
|
||||
[RelayCommand] private void Cancel() => OnCancel?.Invoke();
|
||||
|
||||
public static PaymentViewModel SampleCash() => new() { Total = 3365m, PaidCash = 5000m, Method = "Наличные" };
|
||||
public static PaymentViewModel SampleMixed() => new() { Total = 3365m, PaidCash = 1365m, PaidCard = 2000m, Method = "Смешанная" };
|
||||
public static PaymentViewModel SampleCash() => new() { Total = 3362m, PaidCash = 5000m, Method = "Наличные" };
|
||||
public static PaymentViewModel SampleMixed() => new() { Total = 3362m, PaidCash = 1362m, PaidCard = 2000m, Method = "Смешанная" };
|
||||
}
|
||||
|
||||
/// <summary>Смена: открыть (размен) / закрыть (факт → должно/факт/разница),
|
||||
|
|
@ -568,9 +607,12 @@ public partial class ShiftViewModel : ViewModelBase
|
|||
|
||||
public decimal ExpectedCash => ShiftMath.ExpectedCash(OpeningFloat, SalesCash, ReturnsCash, DrawerIn, DrawerOut);
|
||||
public decimal Difference => ShiftMath.CashDifference(ActualCash, ExpectedCash);
|
||||
public bool DiffSurplus => Difference > 0m; // излишек
|
||||
public bool DiffShortage => Difference < 0m; // недостача
|
||||
public string DiffLabel => Difference == 0m ? "Сходится" : Difference > 0m ? "Излишек" : "Недостача";
|
||||
partial void OnOpeningFloatChanged(decimal value) => RaiseCalc();
|
||||
partial void OnActualCashChanged(decimal value) => RaiseCalc();
|
||||
private void RaiseCalc() { OnPropertyChanged(nameof(ExpectedCash)); OnPropertyChanged(nameof(Difference)); }
|
||||
private void RaiseCalc() { OnPropertyChanged(nameof(ExpectedCash)); OnPropertyChanged(nameof(Difference)); OnPropertyChanged(nameof(DiffSurplus)); OnPropertyChanged(nameof(DiffShortage)); OnPropertyChanged(nameof(DiffLabel)); }
|
||||
|
||||
/// <summary>Сообщить оболочке новый номер смены (или null — закрыта).</summary>
|
||||
public Action<string?>? OnShiftChanged;
|
||||
|
|
|
|||
|
|
@ -3,63 +3,65 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.LoginView"
|
||||
x:DataType="vm:LoginViewModel">
|
||||
<Grid Background="#0f172a">
|
||||
<Border Background="#1e293b" CornerRadius="12" Width="440" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Text="FOOD MARKET" Foreground="#22c55e" FontWeight="Bold" FontSize="22" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Вход кассира" Foreground="#f8fafc" FontSize="18" HorizontalAlignment="Center"/>
|
||||
|
||||
<!-- Быстрый вход по PIN (зарегистрированное устройство) -->
|
||||
<StackPanel Spacing="10" IsVisible="{Binding PinMode}">
|
||||
<Border Background="#0b1220" CornerRadius="8" Height="56">
|
||||
<TextBlock Text="{Binding PinMasked}" Foreground="#f8fafc" FontSize="30" LetterSpacing="6" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Grid Background="{DynamicResource PosBg}">
|
||||
<Border Classes="card" Width="440" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="18">
|
||||
<StackPanel Spacing="16">
|
||||
<StackPanel Spacing="4" HorizontalAlignment="Center">
|
||||
<Border Background="{DynamicResource PosBrand}" CornerRadius="12" Width="48" Height="48" HorizontalAlignment="Center">
|
||||
<TextBlock Text="FM" Foreground="White" FontWeight="Bold" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="{Binding Error}" Foreground="#fca5a5" TextWrapping="Wrap" HorizontalAlignment="Center" IsVisible="{Binding Error, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<Grid ColumnDefinitions="*,*,*" RowDefinitions="Auto,Auto,Auto,Auto" >
|
||||
<Button Grid.Row="0" Grid.Column="0" Classes="pos" Margin="3" Height="56" FontSize="22" Content="1" Command="{Binding PinDigitCommand}" CommandParameter="1"/>
|
||||
<Button Grid.Row="0" Grid.Column="1" Classes="pos" Margin="3" Height="56" FontSize="22" Content="2" Command="{Binding PinDigitCommand}" CommandParameter="2"/>
|
||||
<Button Grid.Row="0" Grid.Column="2" Classes="pos" Margin="3" Height="56" FontSize="22" Content="3" Command="{Binding PinDigitCommand}" CommandParameter="3"/>
|
||||
<Button Grid.Row="1" Grid.Column="0" Classes="pos" Margin="3" Height="56" FontSize="22" Content="4" Command="{Binding PinDigitCommand}" CommandParameter="4"/>
|
||||
<Button Grid.Row="1" Grid.Column="1" Classes="pos" Margin="3" Height="56" FontSize="22" Content="5" Command="{Binding PinDigitCommand}" CommandParameter="5"/>
|
||||
<Button Grid.Row="1" Grid.Column="2" Classes="pos" Margin="3" Height="56" FontSize="22" Content="6" Command="{Binding PinDigitCommand}" CommandParameter="6"/>
|
||||
<Button Grid.Row="2" Grid.Column="0" Classes="pos" Margin="3" Height="56" FontSize="22" Content="7" Command="{Binding PinDigitCommand}" CommandParameter="7"/>
|
||||
<Button Grid.Row="2" Grid.Column="1" Classes="pos" Margin="3" Height="56" FontSize="22" Content="8" Command="{Binding PinDigitCommand}" CommandParameter="8"/>
|
||||
<Button Grid.Row="2" Grid.Column="2" Classes="pos" Margin="3" Height="56" FontSize="22" Content="9" Command="{Binding PinDigitCommand}" CommandParameter="9"/>
|
||||
<Button Grid.Row="3" Grid.Column="0" Classes="pos" Margin="3" Height="56" FontSize="18" Content="⌫" Command="{Binding PinBackCommand}"/>
|
||||
<Button Grid.Row="3" Grid.Column="1" Classes="pos" Margin="3" Height="56" FontSize="22" Content="0" Command="{Binding PinDigitCommand}" CommandParameter="0"/>
|
||||
<Button Grid.Row="3" Grid.Column="2" Classes="pos primary" Margin="3" Height="56" FontSize="18" Content="Войти" Command="{Binding PinLoginCommand}" IsEnabled="{Binding !Busy}"/>
|
||||
</Grid>
|
||||
<Button Classes="pos" HorizontalAlignment="Stretch" Height="40" FontSize="13" Content="Вход по логину и паролю" Command="{Binding UsePasswordCommand}"/>
|
||||
<TextBlock Text="Food Market · Касса" Foreground="{DynamicResource PosText}" FontWeight="Bold" FontSize="18" HorizontalAlignment="Center" Margin="0,8,0,0"/>
|
||||
<TextBlock Text="Вход кассира" Foreground="{DynamicResource PosTextMuted}" FontSize="14" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- PIN -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding PinMode}">
|
||||
<Border Background="{DynamicResource PosSubtle}" CornerRadius="12" Height="56">
|
||||
<TextBlock Text="{Binding PinMasked}" Foreground="{DynamicResource PosText}" FontSize="30" LetterSpacing="8" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="{Binding Error}" Foreground="{DynamicResource PosDanger}" TextWrapping="Wrap" HorizontalAlignment="Center" IsVisible="{Binding Error, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<Grid ColumnDefinitions="*,*,*" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<Button Grid.Row="0" Grid.Column="0" Classes="key" Margin="4" Height="58" Content="1" Command="{Binding PinDigitCommand}" CommandParameter="1"/>
|
||||
<Button Grid.Row="0" Grid.Column="1" Classes="key" Margin="4" Height="58" Content="2" Command="{Binding PinDigitCommand}" CommandParameter="2"/>
|
||||
<Button Grid.Row="0" Grid.Column="2" Classes="key" Margin="4" Height="58" Content="3" Command="{Binding PinDigitCommand}" CommandParameter="3"/>
|
||||
<Button Grid.Row="1" Grid.Column="0" Classes="key" Margin="4" Height="58" Content="4" Command="{Binding PinDigitCommand}" CommandParameter="4"/>
|
||||
<Button Grid.Row="1" Grid.Column="1" Classes="key" Margin="4" Height="58" Content="5" Command="{Binding PinDigitCommand}" CommandParameter="5"/>
|
||||
<Button Grid.Row="1" Grid.Column="2" Classes="key" Margin="4" Height="58" Content="6" Command="{Binding PinDigitCommand}" CommandParameter="6"/>
|
||||
<Button Grid.Row="2" Grid.Column="0" Classes="key" Margin="4" Height="58" Content="7" Command="{Binding PinDigitCommand}" CommandParameter="7"/>
|
||||
<Button Grid.Row="2" Grid.Column="1" Classes="key" Margin="4" Height="58" Content="8" Command="{Binding PinDigitCommand}" CommandParameter="8"/>
|
||||
<Button Grid.Row="2" Grid.Column="2" Classes="key" Margin="4" Height="58" Content="9" Command="{Binding PinDigitCommand}" CommandParameter="9"/>
|
||||
<Button Grid.Row="3" Grid.Column="0" Classes="key" Margin="4" Height="58" FontSize="18" Content="⌫" Command="{Binding PinBackCommand}"/>
|
||||
<Button Grid.Row="3" Grid.Column="1" Classes="key" Margin="4" Height="58" Content="0" Command="{Binding PinDigitCommand}" CommandParameter="0"/>
|
||||
<Button Grid.Row="3" Grid.Column="2" Classes="key primary" Margin="4" Height="58" FontSize="17" Content="Войти" Command="{Binding PinLoginCommand}" IsEnabled="{Binding !Busy}"/>
|
||||
</Grid>
|
||||
<Button Classes="pos ghost" HorizontalAlignment="Stretch" Height="42" FontSize="13" Content="Вход по логину и паролю" Command="{Binding UsePasswordCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Логин/пароль -->
|
||||
<StackPanel Spacing="14" IsVisible="{Binding !PinMode}">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Адрес сервера" Foreground="#94a3b8"/>
|
||||
<TextBox Classes="pos" Text="{Binding ServerUrl}" FontSize="16"/>
|
||||
<TextBlock Text="Адрес сервера" Foreground="{DynamicResource PosTextMuted}" FontSize="13"/>
|
||||
<TextBox Classes="pos" Text="{Binding ServerUrl}" FontSize="15"/>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Логин кассира" Foreground="#94a3b8"/>
|
||||
<TextBox Classes="pos" Text="{Binding Username}" FontSize="18"/>
|
||||
<TextBlock Text="Логин кассира" Foreground="{DynamicResource PosTextMuted}" FontSize="13"/>
|
||||
<TextBox Classes="pos" Text="{Binding Username}" FontSize="17"/>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="PIN / пароль" Foreground="#94a3b8"/>
|
||||
<TextBox Classes="pos" Text="{Binding Pin}" PasswordChar="●" FontSize="18"/>
|
||||
<TextBlock Text="PIN / пароль" Foreground="{DynamicResource PosTextMuted}" FontSize="13"/>
|
||||
<TextBox Classes="pos" Text="{Binding Pin}" PasswordChar="●" FontSize="17"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Error}" Foreground="#fca5a5" TextWrapping="Wrap" IsVisible="{Binding Error, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="56" FontSize="18" Content="Войти" Command="{Binding LoginCommand}" IsDefault="True" IsEnabled="{Binding !Busy}" IsVisible="{Binding !RequirePointSelection}"/>
|
||||
<TextBlock Text="{Binding Error}" Foreground="{DynamicResource PosDanger}" TextWrapping="Wrap" IsVisible="{Binding Error, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="54" FontSize="18" Content="Войти" Command="{Binding LoginCommand}" IsDefault="True" IsEnabled="{Binding !Busy}" IsVisible="{Binding !RequirePointSelection}"/>
|
||||
|
||||
<!-- Регистрация устройства: выбор точки продаж (только администратор) -->
|
||||
<StackPanel Spacing="8" IsVisible="{Binding RequirePointSelection}">
|
||||
<TextBlock Text="Регистрация кассы — выберите точку продаж:" Foreground="#f8fafc"/>
|
||||
<ComboBox ItemsSource="{Binding Points}" SelectedItem="{Binding SelectedPoint}" HorizontalAlignment="Stretch" FontSize="16"/>
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="52" Content="Зарегистрировать на этой точке" Command="{Binding ConfirmPointCommand}"/>
|
||||
<TextBlock Text="Регистрация кассы — выберите точку продаж:" Foreground="{DynamicResource PosText}" FontSize="14"/>
|
||||
<ComboBox ItemsSource="{Binding Points}" SelectedItem="{Binding SelectedPoint}" HorizontalAlignment="Stretch" FontSize="15"/>
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="50" Content="Зарегистрировать на этой точке" Command="{Binding ConfirmPointCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Button Classes="pos" HorizontalAlignment="Stretch" Height="40" FontSize="13" Content="Быстрый вход по PIN" Command="{Binding UsePinCommand}" IsVisible="{Binding DeviceRegistered}"/>
|
||||
|
||||
<Button Classes="pos" HorizontalAlignment="Stretch" Height="40" FontSize="13" Command="{Binding ResetDeviceCommand}" ToolTip.Tip="Сбросить регистрацию устройства. Новую точку выберет только администратор.">
|
||||
<TextBlock Text="Сменить точку продаж (админ)" TextWrapping="Wrap" TextAlignment="Center"/>
|
||||
</Button>
|
||||
<Button Classes="pos ghost" HorizontalAlignment="Stretch" Height="40" FontSize="13" Content="Быстрый вход по PIN" Command="{Binding UsePinCommand}" IsVisible="{Binding DeviceRegistered}"/>
|
||||
<Button Classes="pos ghost" HorizontalAlignment="Stretch" Height="38" FontSize="12.5" Content="Сменить точку продаж (админ)" Command="{Binding ResetDeviceCommand}" ToolTip.Tip="Сбросить регистрацию устройства. Новую точку выберет только администратор."/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
|
|
|||
|
|
@ -3,43 +3,69 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Width="1280" Height="800" MinWidth="1024" MinHeight="680"
|
||||
Background="#0f172a" Title="Касса food-market"
|
||||
Width="1366" Height="768" MinWidth="1100" MinHeight="680"
|
||||
Background="{DynamicResource PosBg}" Title="Касса food-market"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Panel>
|
||||
<Grid RowDefinitions="54,*,34">
|
||||
<!-- Статус-бар -->
|
||||
<Border Grid.Row="0" Background="#1e293b" Padding="16,0">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto,Auto">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<TextBlock Text="FOOD MARKET · Касса" FontWeight="Bold" Foreground="#22c55e" FontSize="16" VerticalAlignment="Center"/>
|
||||
<Grid RowDefinitions="64,*,46">
|
||||
|
||||
<!-- Верхняя полоса -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource PosCard}" BorderBrush="{DynamicResource PosBorder}" BorderThickness="0,0,0,1" Padding="20,0">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
|
||||
<Border Background="{DynamicResource PosBrand}" CornerRadius="9" Width="34" Height="34">
|
||||
<TextBlock Text="FM" Foreground="White" FontWeight="Bold" FontSize="15" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="Food Market · Касса" Foreground="{DynamicResource PosText}" FontWeight="Bold" FontSize="16"/>
|
||||
<TextBlock Text="{Binding PointName}" Foreground="{DynamicResource PosTextMuted}" FontSize="12.5"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="18" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding CashierName}" Foreground="#cbd5e1" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding ShiftNumber}" Foreground="#93c5fd" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding UpdateMessage}" Foreground="#fde68a" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
IsVisible="{Binding UpdateMessage, Converter={x:Static ObjectConverters.IsNotNull}}" FontSize="12"/>
|
||||
<TextBlock Grid.Column="3" Margin="18,0" Text="{Binding NetBadge}" VerticalAlignment="Center" Foreground="#34d399"/>
|
||||
<TextBlock Grid.Column="4" Text="{Binding QueueBadge}" Foreground="#fbbf24" VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<Border Classes="chip" Classes.ok="{Binding IsShiftOpen}" Classes.warn="{Binding !IsShiftOpen}">
|
||||
<TextBlock Text="{Binding ShiftBadge}"/>
|
||||
</Border>
|
||||
<Border Classes="chip" Classes.ok="{Binding IsOnline}" Classes.warn="{Binding !IsOnline}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock Text="{Binding NetBadge}"/>
|
||||
<TextBlock Text="{Binding QueueBadge}" IsVisible="{Binding QueueBadge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Width="1" Height="26" Background="{DynamicResource PosBorder}" Margin="4,0"/>
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Right">
|
||||
<TextBlock Text="{Binding CashierName}" Foreground="{DynamicResource PosText}" FontSize="14" FontWeight="SemiBold" HorizontalAlignment="Right"/>
|
||||
<TextBlock Text="кассир" Foreground="{DynamicResource PosTextMuted}" FontSize="12" HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding UpdateMessage}" Foreground="{DynamicResource PosWarn}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12.5"
|
||||
IsVisible="{Binding UpdateMessage, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Текущая страница -->
|
||||
<ContentControl Grid.Row="1" Content="{Binding CurrentPage}" Margin="0"/>
|
||||
<ContentControl Grid.Row="1" Content="{Binding CurrentPage}"/>
|
||||
|
||||
<!-- Нижняя панель навигации -->
|
||||
<Border Grid.Row="2" Background="#1e293b">
|
||||
<StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Center">
|
||||
<TextBlock Text="F1 Помощь · F2 Поиск · F7 Внесение/Выплата · F9 Возврат · F10 Смена · F12 Оплата · Esc Отмена"
|
||||
Foreground="#94a3b8" VerticalAlignment="Center" FontSize="12"/>
|
||||
</StackPanel>
|
||||
<!-- Горячие клавиши -->
|
||||
<Border Grid.Row="2" Background="{DynamicResource PosCard}" BorderBrush="{DynamicResource PosBorder}" BorderThickness="0,1,0,0">
|
||||
<ItemsControl VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<ItemsControl.ItemsPanel><ItemsPanelTemplate><StackPanel Orientation="Horizontal" Spacing="8"/></ItemsPanelTemplate></ItemsControl.ItemsPanel>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F1" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Помощь" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F2" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Поиск" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F7" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Отложить" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F9" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Возврат" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F10" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Смена" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="F12" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosBrand}"/><TextBlock Text="Оплата" FontSize="12" Foreground="{DynamicResource PosBrand}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="Del" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Удалить" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
<Border Classes="hotkey"><StackPanel Orientation="Horizontal" Spacing="6"><TextBlock Text="Esc" FontWeight="Bold" FontSize="11.5" Foreground="{DynamicResource PosTextMuted}"/><TextBlock Text="Отмена" FontSize="12" Foreground="{DynamicResource PosTextMuted}"/></StackPanel></Border>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Модальная оплата поверх экрана продажи -->
|
||||
<Border Background="#cc0b1220" IsVisible="{Binding IsPaymentOpen}">
|
||||
<ContentControl Content="{Binding Payment}"/>
|
||||
<!-- Модальная оплата -->
|
||||
<Border Background="{DynamicResource PosScrim}" IsVisible="{Binding IsPaymentOpen}">
|
||||
<ContentControl Content="{Binding Payment}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Panel>
|
||||
</Window>
|
||||
|
|
|
|||
|
|
@ -3,37 +3,74 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.PaymentView"
|
||||
x:DataType="vm:PaymentViewModel">
|
||||
<Grid Background="#0f172a">
|
||||
<Border Background="#1e293b" CornerRadius="12" Width="560" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Text="Оплата чека" FontSize="24" FontWeight="Bold" Foreground="#f8fafc" HorizontalAlignment="Center"/>
|
||||
<Border Classes="card" Width="760" Padding="28" CornerRadius="18">
|
||||
<StackPanel Spacing="16">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="К оплате" Foreground="#94a3b8" FontSize="18" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Total, StringFormat='{}{0:N0} ₸'}" Foreground="#34d399" FontSize="30" FontWeight="Bold"/>
|
||||
<TextBlock Text="Оплата" FontSize="22" FontWeight="Bold" Foreground="{DynamicResource PosText}" VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<TextBlock Text="К оплате" Foreground="{DynamicResource PosTextMuted}" FontSize="16" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Total, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="30" FontWeight="Bold"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Background="#334155"/>
|
||||
<Grid ColumnDefinitions="140,*">
|
||||
<TextBlock Text="Наличные" Foreground="#cbd5e1" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Classes="pos" Text="{Binding PaidCash}" FontSize="22"/>
|
||||
|
||||
<!-- Сегменты способа оплаты -->
|
||||
<Grid ColumnDefinitions="*,*,*">
|
||||
<Button Classes="pos seg" Classes.on="{Binding IsCash}" Margin="0,0,5,0" Content="Наличные" Command="{Binding SelectCashCommand}"/>
|
||||
<Button Grid.Column="1" Classes="pos seg" Classes.on="{Binding IsCard}" Margin="5,0" Content="Карта" Command="{Binding SelectCardCommand}"/>
|
||||
<Button Grid.Column="2" Classes="pos seg" Classes.on="{Binding IsMixed}" Margin="5,0,0,0" Content="Смешанная" Command="{Binding SelectMixedCommand}"/>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="140,*">
|
||||
<TextBlock Text="Картой" Foreground="#cbd5e1" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Classes="pos" Text="{Binding PaidCard}" FontSize="22"/>
|
||||
|
||||
<Grid ColumnDefinitions="*,320">
|
||||
<!-- Левая часть: суммы -->
|
||||
<StackPanel Grid.Column="0" Spacing="12" Margin="0,0,20,0">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Получено наличными" Foreground="{DynamicResource PosTextMuted}" FontSize="14"/>
|
||||
<TextBox Classes="pos" FontSize="24" Text="{Binding PaidCash}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="4" IsVisible="{Binding IsMixed}">
|
||||
<TextBlock Text="Картой" Foreground="{DynamicResource PosTextMuted}" FontSize="14"/>
|
||||
<TextBox Classes="pos" FontSize="24" Text="{Binding PaidCard}"/>
|
||||
</StackPanel>
|
||||
<!-- Быстрые суммы -->
|
||||
<Grid ColumnDefinitions="*,*,*" RowDefinitions="Auto,Auto" IsVisible="{Binding !IsCard}">
|
||||
<Button Classes="pos ghost" Margin="0,0,4,8" Content="Без сдачи" Command="{Binding QuickCashCommand}" CommandParameter="exact"/>
|
||||
<Button Grid.Column="1" Classes="pos ghost" Margin="4,0,4,8" Content="1 000" Command="{Binding QuickCashCommand}" CommandParameter="1000"/>
|
||||
<Button Grid.Column="2" Classes="pos ghost" Margin="4,0,0,8" Content="2 000" Command="{Binding QuickCashCommand}" CommandParameter="2000"/>
|
||||
<Button Grid.Row="1" Classes="pos ghost" Margin="0,0,4,0" Content="5 000" Command="{Binding QuickCashCommand}" CommandParameter="5000"/>
|
||||
<Button Grid.Row="1" Grid.Column="1" Classes="pos ghost" Margin="4,0" Content="10 000" Command="{Binding QuickCashCommand}" CommandParameter="10000"/>
|
||||
<Button Grid.Row="1" Grid.Column="2" Classes="pos ghost" Margin="4,0,0,0" Content="20 000" Command="{Binding QuickCashCommand}" CommandParameter="20000"/>
|
||||
</Grid>
|
||||
<Border Background="#0b1220" CornerRadius="8" Padding="14">
|
||||
|
||||
<!-- Сдача -->
|
||||
<Border Background="{DynamicResource PosBrandSoft}" CornerRadius="12" Padding="16,14">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="СДАЧА" Foreground="#f8fafc" FontSize="20" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Change, StringFormat='{}{0:N0} ₸'}" Foreground="#fbbf24" FontSize="30" FontWeight="Bold"/>
|
||||
<TextBlock Text="СДАЧА" Foreground="{DynamicResource PosText}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Change, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosBrand}" FontSize="36" FontWeight="Bold"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid ColumnDefinitions="*,2*">
|
||||
<Button Classes="pos" Margin="0,0,6,0" HorizontalAlignment="Stretch" Height="64" FontSize="16" Content="Без сдачи" Command="{Binding ExactCashCommand}"/>
|
||||
<Button Grid.Column="1" Classes="pos primary" HorizontalAlignment="Stretch" Height="64" FontSize="19" Command="{Binding ConfirmCommand}" IsDefault="True" IsEnabled="{Binding Sufficient}">
|
||||
<TextBlock Text="Enter · Провести и печать" TextWrapping="Wrap" TextAlignment="Center"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Правая часть: цифровая клавиатура -->
|
||||
<Grid Grid.Column="1" ColumnDefinitions="*,*,*" RowDefinitions="*,*,*,*" IsVisible="{Binding !IsCard}">
|
||||
<Button Grid.Row="0" Grid.Column="0" Classes="key" Margin="3" Height="54" Content="1" Command="{Binding KeyDigitCommand}" CommandParameter="1"/>
|
||||
<Button Grid.Row="0" Grid.Column="1" Classes="key" Margin="3" Height="54" Content="2" Command="{Binding KeyDigitCommand}" CommandParameter="2"/>
|
||||
<Button Grid.Row="0" Grid.Column="2" Classes="key" Margin="3" Height="54" Content="3" Command="{Binding KeyDigitCommand}" CommandParameter="3"/>
|
||||
<Button Grid.Row="1" Grid.Column="0" Classes="key" Margin="3" Height="54" Content="4" Command="{Binding KeyDigitCommand}" CommandParameter="4"/>
|
||||
<Button Grid.Row="1" Grid.Column="1" Classes="key" Margin="3" Height="54" Content="5" Command="{Binding KeyDigitCommand}" CommandParameter="5"/>
|
||||
<Button Grid.Row="1" Grid.Column="2" Classes="key" Margin="3" Height="54" Content="6" Command="{Binding KeyDigitCommand}" CommandParameter="6"/>
|
||||
<Button Grid.Row="2" Grid.Column="0" Classes="key" Margin="3" Height="54" Content="7" Command="{Binding KeyDigitCommand}" CommandParameter="7"/>
|
||||
<Button Grid.Row="2" Grid.Column="1" Classes="key" Margin="3" Height="54" Content="8" Command="{Binding KeyDigitCommand}" CommandParameter="8"/>
|
||||
<Button Grid.Row="2" Grid.Column="2" Classes="key" Margin="3" Height="54" Content="9" Command="{Binding KeyDigitCommand}" CommandParameter="9"/>
|
||||
<Button Grid.Row="3" Grid.Column="0" Classes="key" Margin="3" Height="54" FontSize="15" Content="Сброс" Command="{Binding KeyClearCommand}"/>
|
||||
<Button Grid.Row="3" Grid.Column="1" Classes="key" Margin="3" Height="54" Content="0" Command="{Binding KeyDigitCommand}" CommandParameter="0"/>
|
||||
<Button Grid.Row="3" Grid.Column="2" Classes="key" Margin="3" Height="54" FontSize="18" Content="⌫" Command="{Binding KeyBackCommand}"/>
|
||||
</Grid>
|
||||
<Button Classes="pos" HorizontalAlignment="Stretch" Height="44" Content="Esc · Отмена" Command="{Binding CancelCommand}"/>
|
||||
</Grid>
|
||||
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="66" FontSize="20" Command="{Binding ConfirmCommand}" IsDefault="True" IsEnabled="{Binding Sufficient}">
|
||||
<TextBlock Text="Провести и печатать · Enter" TextAlignment="Center"/>
|
||||
</Button>
|
||||
<Button Classes="pos ghost" HorizontalAlignment="Stretch" Height="44" Content="Esc · Назад" Command="{Binding CancelCommand}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -3,54 +3,57 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.ReturnView"
|
||||
x:DataType="vm:ReturnViewModel">
|
||||
<Grid Background="#0f172a" RowDefinitions="Auto,*,Auto" Margin="16">
|
||||
<StackPanel Grid.Row="0" Spacing="8">
|
||||
<TextBlock Text="Возврат по чеку" FontSize="22" FontWeight="Bold" Foreground="#f8fafc"/>
|
||||
<Grid Background="{DynamicResource PosBg}" RowDefinitions="Auto,*,Auto" Margin="20">
|
||||
<StackPanel Grid.Row="0" Spacing="10">
|
||||
<TextBlock Text="Возврат по чеку" FontSize="22" FontWeight="Bold" Foreground="{DynamicResource PosText}"/>
|
||||
<Border Classes="card" Padding="0">
|
||||
<Grid ColumnDefinitions="*,170">
|
||||
<TextBox Classes="pos" Watermark="Номер чека или скан QR (Enter)" Text="{Binding ReceiptNumber}">
|
||||
<TextBox.KeyBindings>
|
||||
<KeyBinding Gesture="Enter" Command="{Binding LookupCommand}"/>
|
||||
</TextBox.KeyBindings>
|
||||
<TextBox BorderThickness="0" Background="Transparent" FontSize="18" VerticalContentAlignment="Center" Margin="16,0"
|
||||
Watermark="Номер чека или скан QR (Enter)" Text="{Binding ReceiptNumber}">
|
||||
<TextBox.KeyBindings><KeyBinding Gesture="Enter" Command="{Binding LookupCommand}"/></TextBox.KeyBindings>
|
||||
</TextBox>
|
||||
<Button Grid.Column="1" Classes="pos" Margin="8,0,0,0" HorizontalAlignment="Stretch" Content="Найти" Command="{Binding LookupCommand}"/>
|
||||
<Button Grid.Column="1" Classes="pos primary" Margin="6" HorizontalAlignment="Stretch" Content="Найти" Command="{Binding LookupCommand}"/>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding Message}" Foreground="#60a5fa" TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
<TextBlock Text="{Binding Message}" Foreground="{DynamicResource PosTextMuted}" TextWrapping="Wrap" FontSize="14"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border Grid.Row="1" Background="#111827" CornerRadius="10" Margin="0,10">
|
||||
<Border Grid.Row="1" Classes="card" Margin="0,12" Padding="8">
|
||||
<ListBox ItemsSource="{Binding Lines}" Background="Transparent" BorderThickness="0">
|
||||
<ListBox.Styles><Style Selector="ListBoxItem"><Setter Property="Padding" Value="0"/><Setter Property="Margin" Value="0,3"/></Style></ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:ReturnLineVm">
|
||||
<Grid ColumnDefinitions="*,190,110,120" MinHeight="58">
|
||||
<StackPanel VerticalAlignment="Center" Margin="4,6">
|
||||
<TextBlock Text="{Binding Name}" Foreground="#f3f4f6" FontSize="16" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding AvailText}" Foreground="#94a3b8" FontSize="12.5"/>
|
||||
<Border Background="{DynamicResource PosSubtle}" CornerRadius="10" Padding="12,8">
|
||||
<Grid ColumnDefinitions="*,210,110,120">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Name}" Foreground="{DynamicResource PosText}" FontSize="17" FontWeight="Medium" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding AvailText}" Foreground="{DynamicResource PosTextMuted}" FontSize="13"/>
|
||||
</StackPanel>
|
||||
<!-- Степпер количества к возврату -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Classes="pos" Width="44" Height="40" Padding="0" FontSize="20" Content="−"
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Button Classes="key" Width="42" Height="42" Padding="0" FontSize="22" Content="−"
|
||||
Command="{Binding $parent[ItemsControl].((vm:ReturnViewModel)DataContext).DecCommand}" CommandParameter="{Binding}"/>
|
||||
<Border Background="#0b1220" CornerRadius="6" Width="58" Height="40">
|
||||
<TextBlock Text="{Binding QtyText}" Foreground="#f8fafc" FontSize="17" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Button Classes="pos" Width="44" Height="40" Padding="0" FontSize="20" Content="+"
|
||||
<TextBlock Text="{Binding QtyText}" Foreground="{DynamicResource PosText}" FontSize="17" FontWeight="SemiBold" MinWidth="54" TextAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Button Classes="key" Width="42" Height="42" Padding="0" FontSize="20" Content="+"
|
||||
Command="{Binding $parent[ItemsControl].((vm:ReturnViewModel)DataContext).IncCommand}" CommandParameter="{Binding}"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="2" Classes="pos" Height="40" FontSize="13" Margin="6,0,0,0" Content="Всё"
|
||||
<Button Grid.Column="2" Classes="pos ghost" Height="40" FontSize="13" Margin="6,0,0,0" Content="Всё"
|
||||
Command="{Binding $parent[ItemsControl].((vm:ReturnViewModel)DataContext).FullCommand}" CommandParameter="{Binding}"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding LineRefund, StringFormat='{}{0:N0} ₸'}" Foreground="#ffffff" FontWeight="SemiBold" FontSize="17" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding LineRefund, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontWeight="SemiBold" FontSize="18" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="К возврату" Foreground="#cbd5e1" FontSize="18" VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="16">
|
||||
<TextBlock Text="{Binding RefundTotal, StringFormat='{}{0:N0} ₸'}" Foreground="#fca5a5" FontSize="26" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<Button Classes="pos danger" Height="56" Width="230" Content="Оформить возврат" Command="{Binding SubmitCommand}" IsEnabled="{Binding Found}"/>
|
||||
<Border Grid.Row="2" Classes="card" Padding="16,12">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="К возврату" Foreground="{DynamicResource PosTextMuted}" FontSize="14"/>
|
||||
<TextBlock Text="{Binding RefundTotal, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosDanger}" FontSize="28" FontWeight="Bold"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Classes="pos danger" Height="56" Width="240" FontSize="17" Content="Оформить возврат" Command="{Binding SubmitCommand}" IsEnabled="{Binding Found}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -3,74 +3,107 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.SaleView"
|
||||
x:DataType="vm:SaleViewModel">
|
||||
<Grid ColumnDefinitions="*,380" Background="#0f172a">
|
||||
<!-- Левая часть: чек -->
|
||||
<Grid Grid.Column="0" RowDefinitions="Auto,*,Auto" Margin="16,12,8,12">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
|
||||
<TextBox Grid.Column="0" Classes="pos" Watermark="Штрихкод / поиск товара (Enter)"
|
||||
Text="{Binding ScanInput}" FontSize="22">
|
||||
<TextBox.KeyBindings>
|
||||
<KeyBinding Gesture="Enter" Command="{Binding ScanCommand}"/>
|
||||
</TextBox.KeyBindings>
|
||||
<Grid ColumnDefinitions="*,400" Background="{DynamicResource PosBg}">
|
||||
|
||||
<!-- ЛЕВО: поиск + корзина -->
|
||||
<Grid Grid.Column="0" RowDefinitions="Auto,*,Auto" Margin="20,18,10,18">
|
||||
|
||||
<!-- Поиск / скан -->
|
||||
<Border Grid.Row="0" Classes="card" Padding="0" Margin="0,0,0,14">
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<PathIcon Grid.Column="0" Width="22" Height="22" Margin="18,0,8,0" Foreground="{DynamicResource PosTextMuted}"
|
||||
Data="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
||||
<TextBox Grid.Column="1" BorderThickness="0" FontSize="19" Background="Transparent" VerticalContentAlignment="Center"
|
||||
Watermark="Отсканируйте товар или начните вводить название" Text="{Binding ScanInput}">
|
||||
<TextBox.KeyBindings><KeyBinding Gesture="Enter" Command="{Binding ScanCommand}"/></TextBox.KeyBindings>
|
||||
</TextBox>
|
||||
<Button Grid.Column="1" Classes="pos" Margin="8,0,0,0" Content="Отложить" Command="{Binding ParkCommand}"/>
|
||||
<Button Grid.Column="2" Classes="pos" Margin="6,0,0,0" Content="Вернуть" Command="{Binding ResumeCommand}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Корзина -->
|
||||
<Border Grid.Row="1" Classes="card" Padding="0">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,170,120,44" Height="40" Margin="18,6,10,0">
|
||||
<TextBlock Text="Товар" Foreground="{DynamicResource PosTextMuted}" FontSize="13" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="Количество" Foreground="{DynamicResource PosTextMuted}" FontSize="13" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="2" Text="Сумма" Foreground="{DynamicResource PosTextMuted}" FontSize="13" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1" Background="#111827" CornerRadius="10" Padding="0">
|
||||
<DockPanel>
|
||||
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,110,110,120" Background="#1f2937" Height="38">
|
||||
<TextBlock Text=" Товар" Foreground="#9ca3af" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="Кол-во" Foreground="#9ca3af" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBlock Grid.Column="2" Text="Цена" Foreground="#9ca3af" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBlock Grid.Column="3" Text="Сумма " Foreground="#9ca3af" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
<ListBox ItemsSource="{Binding Lines}" Background="Transparent" BorderThickness="0">
|
||||
<!-- Пустая корзина -->
|
||||
<StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10" IsVisible="{Binding IsCartEmpty}">
|
||||
<Border Background="{DynamicResource PosSubtle}" CornerRadius="999" Width="72" Height="72">
|
||||
<PathIcon Width="34" Height="34" Foreground="{DynamicResource PosTextMuted}"
|
||||
Data="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
|
||||
</Border>
|
||||
<TextBlock Text="Отсканируйте первый товар" Foreground="{DynamicResource PosTextMuted}" FontSize="16"/>
|
||||
</StackPanel>
|
||||
|
||||
<ListBox Grid.Row="1" ItemsSource="{Binding Lines}" SelectedItem="{Binding SelectedLine}"
|
||||
Background="Transparent" BorderThickness="0" Padding="8,4" IsVisible="{Binding !IsCartEmpty}">
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem"><Setter Property="Padding" Value="0"/><Setter Property="Margin" Value="0,3"/><Setter Property="CornerRadius" Value="10"/></Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:SaleLineVm">
|
||||
<Grid ColumnDefinitions="*,110,110,120" MinHeight="46">
|
||||
<StackPanel VerticalAlignment="Center" Margin="0,4">
|
||||
<TextBlock Text="{Binding Name}" Foreground="#f3f4f6" FontSize="16" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding DiscountText}" Foreground="#fca5a5" FontSize="12.5"
|
||||
IsVisible="{Binding HasDiscount}"/>
|
||||
<Border Background="{DynamicResource PosSubtle}" CornerRadius="10" Padding="12,8">
|
||||
<Grid ColumnDefinitions="*,170,120,40">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Name}" Foreground="{DynamicResource PosText}" FontSize="17" FontWeight="Medium" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding DiscountText}" Foreground="{DynamicResource PosBrand}" FontSize="13" IsVisible="{Binding HasDiscount}"/>
|
||||
<TextBlock Text="{Binding Price, StringFormat='{}{0:N0} ₸ / ед.'}" Foreground="{DynamicResource PosTextMuted}" FontSize="13" IsVisible="{Binding !HasDiscount}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding QtyText}" Foreground="#e5e7eb" FontSize="16" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="2" Text="{Binding Price, StringFormat='{}{0:N0} ₸'}" Foreground="#e5e7eb" FontSize="16" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding LineTotal, StringFormat='{}{0:N0} ₸'}" Foreground="#ffffff" FontSize="17" FontWeight="SemiBold" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<!-- Степпер -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Button Classes="key" Width="40" Height="40" Padding="0" FontSize="22" Content="−"
|
||||
Command="{Binding $parent[ItemsControl].((vm:SaleViewModel)DataContext).DecLineCommand}" CommandParameter="{Binding}"/>
|
||||
<TextBlock Text="{Binding QtyText}" Foreground="{DynamicResource PosText}" FontSize="16" FontWeight="SemiBold" MinWidth="58" TextAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Button Classes="key" Width="40" Height="40" Padding="0" FontSize="20" Content="+"
|
||||
Command="{Binding $parent[ItemsControl].((vm:SaleViewModel)DataContext).IncLineCommand}" CommandParameter="{Binding}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="2" Text="{Binding LineTotal, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="19" FontWeight="SemiBold" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="3" Classes="key" Width="34" Height="34" Padding="0" Background="Transparent" BorderThickness="0" VerticalAlignment="Center"
|
||||
Command="{Binding $parent[ItemsControl].((vm:SaleViewModel)DataContext).RemoveLineCommand}" CommandParameter="{Binding}" ToolTip.Tip="Удалить">
|
||||
<PathIcon Width="18" Height="18" Foreground="{DynamicResource PosDanger}"
|
||||
Data="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="{Binding Message}" Foreground="#60a5fa" Margin="4,8,0,0" FontSize="14"/>
|
||||
<TextBlock Grid.Row="2" Text="{Binding Message}" Foreground="{DynamicResource PosTextMuted}" Margin="4,10,0,0" FontSize="13.5"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Правая часть: итоги + оплата -->
|
||||
<Border Grid.Column="1" Background="#1e293b" Margin="8,12,16,12" CornerRadius="10" Padding="18">
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto">
|
||||
<StackPanel Grid.Row="0" Spacing="6">
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Позиций" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding ItemCount}" Foreground="#e5e7eb"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Подытог" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding Subtotal, StringFormat='{}{0:N0} ₸'}" Foreground="#e5e7eb"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Скидка" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding Discount, StringFormat='-{0:N0} ₸'}" Foreground="#fca5a5"/></Grid>
|
||||
<!-- ПРАВО: чек + оплата -->
|
||||
<Border Grid.Column="1" Classes="card" Margin="10,18,20,18" Padding="20">
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
<StackPanel Grid.Row="0" Spacing="10">
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Позиций" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding ItemCount}" Foreground="{DynamicResource PosText}" FontSize="15" FontWeight="Medium"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Подытог" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding Subtotal, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto" IsVisible="{Binding HasAnyDiscount}"><TextBlock Text="Скидка" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding Discount, StringFormat='−{0:N0} ₸'}" Foreground="{DynamicResource PosBrand}" FontSize="15" FontWeight="Medium"/></Grid>
|
||||
</StackPanel>
|
||||
<Separator Grid.Row="1" Margin="0,12" Background="#334155"/>
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="ИТОГО" Foreground="#f8fafc" FontSize="22" FontWeight="Bold" VerticalAlignment="Bottom"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Total, StringFormat='{}{0:N0} ₸'}" Foreground="#34d399" FontSize="34" FontWeight="Bold" HorizontalAlignment="Right"/>
|
||||
|
||||
<Border Grid.Row="1" Background="{DynamicResource PosBrandSoft}" CornerRadius="12" Padding="16,14" Margin="0,16,0,0">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="ИТОГО" Foreground="{DynamicResource PosText}" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Total, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosBrand}" FontSize="44" FontWeight="Bold"/>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="4" Spacing="8">
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="68" FontSize="22" Command="{Binding PayCommand}">
|
||||
<TextBlock Text="F12 · ОПЛАТА" TextAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="3" Spacing="10">
|
||||
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="70" FontSize="22" Command="{Binding PayCommand}">
|
||||
<TextBlock Text="Оплатить · F12" TextAlignment="Center"/>
|
||||
</Button>
|
||||
<Grid ColumnDefinitions="*,*">
|
||||
<Button Classes="pos" Margin="0,0,4,0" FontSize="16" HorizontalAlignment="Stretch">
|
||||
<TextBlock Text="Скидка" TextAlignment="Center"/>
|
||||
</Button>
|
||||
<Button Grid.Column="1" Classes="pos danger" Margin="4,0,0,0" FontSize="16" HorizontalAlignment="Stretch" Command="{Binding ClearCommand}" ToolTip.Tip="Очистить чек (Esc)">
|
||||
<TextBlock Text="Очистить" TextWrapping="Wrap" TextAlignment="Center"/>
|
||||
</Button>
|
||||
<Button Classes="pos ghost" Margin="0,0,5,0" Content="Скидка"/>
|
||||
<Button Grid.Column="1" Margin="5,0,0,0" Classes="pos ghost" Content="Отложить" Command="{Binding ParkCommand}"/>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,*">
|
||||
<Button Classes="pos ghost" Margin="0,0,5,0" Content="Отложенные" Command="{Binding ResumeCommand}"/>
|
||||
<Button Grid.Column="1" Margin="5,0,0,0" Classes="pos danger" Content="Очистить" Command="{Binding ClearCommand}" ToolTip.Tip="Очистить чек (Esc)"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -3,33 +3,56 @@
|
|||
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
|
||||
x:Class="foodmarket.Pos.Views.ShiftView"
|
||||
x:DataType="vm:ShiftViewModel">
|
||||
<Grid Background="#0f172a">
|
||||
<Border Background="#1e293b" CornerRadius="12" Width="620" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Grid Background="{DynamicResource PosBg}">
|
||||
<Border Classes="card" Width="640" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<StackPanel Spacing="12">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Text="Z-отчёт смены" FontSize="24" FontWeight="Bold" Foreground="#f8fafc"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Number}" Foreground="#93c5fd" FontSize="18" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<Separator Background="#334155"/>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Размен (на открытие)" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding OpeningFloat, StringFormat='{}{0:N0} ₸'}" Foreground="#e5e7eb"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Продажи наличными" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding SalesCash, StringFormat='{}{0:N0} ₸'}" Foreground="#e5e7eb"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Продажи картой" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding SalesCard, StringFormat='{}{0:N0} ₸'}" Foreground="#e5e7eb"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Возвраты наличными" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding ReturnsCash, StringFormat='-{0:N0} ₸'}" Foreground="#fca5a5"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Внесения" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding DrawerIn, StringFormat='+{0:N0} ₸'}" Foreground="#86efac"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Выплаты" Foreground="#94a3b8"/><TextBlock Grid.Column="1" Text="{Binding DrawerOut, StringFormat='-{0:N0} ₸'}" Foreground="#fca5a5"/></Grid>
|
||||
<Separator Background="#334155"/>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Должно быть в кассе" Foreground="#cbd5e1" FontSize="18"/><TextBlock Grid.Column="1" Text="{Binding ExpectedCash, StringFormat='{}{0:N0} ₸'}" Foreground="#f8fafc" FontSize="20" FontWeight="SemiBold"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Факт (пересчёт)" Foreground="#cbd5e1" FontSize="18"/><TextBlock Grid.Column="1" Text="{Binding ActualCash, StringFormat='{}{0:N0} ₸'}" Foreground="#f8fafc" FontSize="20" FontWeight="SemiBold"/></Grid>
|
||||
<Border Background="#0b1220" CornerRadius="8" Padding="12">
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Разница" Foreground="#f8fafc" FontSize="18"/><TextBlock Grid.Column="1" Text="{Binding Difference, StringFormat='{}{0:+#,0;-#,0;0} ₸'}" Foreground="#fbbf24" FontSize="24" FontWeight="Bold"/></Grid>
|
||||
<TextBlock Text="Смена — Z-отчёт" FontSize="22" FontWeight="Bold" Foreground="{DynamicResource PosText}"/>
|
||||
<Border Grid.Column="1" Classes="chip" Classes.ok="{Binding IsOpen}" Classes.warn="{Binding !IsOpen}" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Number}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border Height="1" Background="{DynamicResource PosBorder}" Margin="0,4"/>
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Размен (на открытие)" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding OpeningFloat, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Продажи наличными" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding SalesCash, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Продажи картой" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding SalesCard, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Возвраты наличными" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding ReturnsCash, StringFormat='−{0:N0} ₸'}" Foreground="{DynamicResource PosDanger}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Внесения" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding DrawerIn, StringFormat='+{0:N0} ₸'}" Foreground="{DynamicResource PosBrand}" FontSize="15"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Выплаты" Foreground="{DynamicResource PosTextMuted}" FontSize="15"/><TextBlock Grid.Column="1" Text="{Binding DrawerOut, StringFormat='−{0:N0} ₸'}" Foreground="{DynamicResource PosDanger}" FontSize="15"/></Grid>
|
||||
|
||||
<Border Height="1" Background="{DynamicResource PosBorder}" Margin="0,4"/>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Должно быть в кассе" Foreground="{DynamicResource PosText}" FontSize="16" FontWeight="SemiBold"/><TextBlock Grid.Column="1" Text="{Binding ExpectedCash, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="18" FontWeight="SemiBold"/></Grid>
|
||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Факт (пересчёт)" Foreground="{DynamicResource PosText}" FontSize="16"/><TextBlock Grid.Column="1" Text="{Binding ActualCash, StringFormat='{}{0:N0} ₸'}" Foreground="{DynamicResource PosText}" FontSize="18" FontWeight="SemiBold"/></Grid>
|
||||
|
||||
<Border CornerRadius="12" Padding="16,12" Background="{DynamicResource PosSubtle}"
|
||||
Classes.bad="{Binding DiffShortage}">
|
||||
<Border.Styles>
|
||||
<Style Selector="Border.bad"><Setter Property="Background" Value="{DynamicResource PosDangerSoft}"/></Style>
|
||||
</Border.Styles>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="Разница" Foreground="{DynamicResource PosText}" FontSize="16" FontWeight="SemiBold"/>
|
||||
<TextBlock Text="{Binding DiffLabel}" Foreground="{DynamicResource PosTextMuted}" FontSize="13"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Difference, StringFormat='{}{0:+#,0;-#,0;0} ₸'}" FontSize="26" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PosTextMuted}"
|
||||
Classes.surplus="{Binding DiffSurplus}" Classes.shortage="{Binding DiffShortage}">
|
||||
<TextBlock.Styles>
|
||||
<Style Selector="TextBlock.surplus"><Setter Property="Foreground" Value="{DynamicResource PosBrand}"/></Style>
|
||||
<Style Selector="TextBlock.shortage"><Setter Property="Foreground" Value="{DynamicResource PosDanger}"/></Style>
|
||||
</TextBlock.Styles>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid ColumnDefinitions="*,*,*">
|
||||
<Button Classes="pos" Margin="0,0,4,0" HorizontalAlignment="Stretch" Content="Открыть смену" Command="{Binding OpenShiftCommand}" IsEnabled="{Binding !IsOpen}"/>
|
||||
<Button Grid.Column="1" Classes="pos" Margin="4,0" HorizontalAlignment="Stretch" Content="Внести" Command="{Binding DepositCashCommand}" IsEnabled="{Binding IsOpen}"/>
|
||||
<Button Grid.Column="2" Classes="pos" Margin="4,0,0,0" HorizontalAlignment="Stretch" Content="Выплата" Command="{Binding WithdrawCashCommand}" IsEnabled="{Binding IsOpen}"/>
|
||||
<Button Classes="pos ghost" Margin="0,0,5,0" Content="Открыть смену" Command="{Binding OpenShiftCommand}" IsEnabled="{Binding !IsOpen}"/>
|
||||
<Button Grid.Column="1" Classes="pos ghost" Margin="5,0" Content="Внести" Command="{Binding DepositCashCommand}" IsEnabled="{Binding IsOpen}"/>
|
||||
<Button Grid.Column="2" Classes="pos ghost" Margin="5,0,0,0" Content="Выплата" Command="{Binding WithdrawCashCommand}" IsEnabled="{Binding IsOpen}"/>
|
||||
</Grid>
|
||||
<Button Classes="pos danger" HorizontalAlignment="Stretch" Height="52" Content="Закрыть смену (Z-отчёт)" Command="{Binding CloseShiftCommand}" IsEnabled="{Binding IsOpen}"/>
|
||||
<TextBlock Text="{Binding Message}" Foreground="#60a5fa" IsVisible="{Binding Message, Converter={x:Static ObjectConverters.IsNotNull}}" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding Message}" Foreground="{DynamicResource PosTextMuted}" TextWrapping="Wrap" IsVisible="{Binding Message, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
|
|
|||