849bc73290
BillingView 416→114 (+ BalanceCard 155 + TransactionsTable 113 + InvoicesTable 90 + billingFormatters 51 composable: formatPlain/formatCost/statusChipColor/ statusLabel/formatLabel/formatIcon/txAmountClass). SecurityTab 354→39 (+ ChangePasswordCard 17 + TwoFactorCard 218 + RecoveryCodesCard 104 + SessionsTable 66; auth-store читается напрямую в каждом sub-component). RemindersView 345→183 (+ RemindersFilters 51 + RemindersList 173; ReminderDialog уже отдельный с прошлой фазы — служит как ReminderForm). State (`activeTab`, `editingReminder`, `deletingReminderId` в RemindersView) остаётся в parent ради единого reload-flow + confirm-dialog'ов. Auth-store читается напрямую в TwoFactorCard/RecoveryCodesCard через useAuthStore() — без prop-drilling. Reminders-store читается напрямую в RemindersFilters/ RemindersList. Все sub-components <250 строк (acceptance threshold). 3 view-shells: 114/39/183. Регрессия: ESLint 0 + vue-tsc 0 + Vitest 416/416 + build OK 968 ms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
2.2 KiB
Vue
91 lines
2.2 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* InvoicesTable — список счетов и УПД (PDF / 1С 8.3 XML).
|
||
* Sprint 4 Phase B/2 — split BillingView.
|
||
*/
|
||
import { MOCK_INVOICES } from '../../composables/mockBilling';
|
||
import { formatIcon, formatLabel, formatPlain } from '../../composables/billingFormatters';
|
||
</script>
|
||
|
||
<template>
|
||
<v-card variant="outlined" class="mt-4 panel">
|
||
<div class="panel-h pa-4">
|
||
<h2 class="text-h6 panel-title ma-0">Счета и УПД</h2>
|
||
<v-btn variant="outlined" size="small" prepend-icon="mdi-download">Реестр XLSX</v-btn>
|
||
</div>
|
||
<v-divider />
|
||
<ul class="invoices-list pa-2 ma-0">
|
||
<li v-for="inv in MOCK_INVOICES" :key="inv.id" class="inv-row">
|
||
<span class="inv-when num">{{ inv.when }}</span>
|
||
<span class="inv-name">
|
||
{{ inv.title }}
|
||
<span class="sub">{{ inv.sub }}</span>
|
||
</span>
|
||
<span class="inv-amount num">{{ formatPlain(inv.amountRub) }}</span>
|
||
<v-btn variant="text" size="small" :prepend-icon="formatIcon(inv.format)">
|
||
{{ formatLabel(inv.format) }}
|
||
</v-btn>
|
||
</li>
|
||
</ul>
|
||
</v-card>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.num {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
font-feature-settings: 'tnum';
|
||
font-weight: 500;
|
||
}
|
||
|
||
.panel {
|
||
background: #fff;
|
||
}
|
||
.panel-h {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
.panel-title {
|
||
font-variation-settings: 'opsz' 18;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
.invoices-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
.inv-row {
|
||
display: grid;
|
||
grid-template-columns: 110px 1fr auto auto;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 16px;
|
||
border-bottom: 1px solid #f0ede4;
|
||
}
|
||
.inv-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.inv-when {
|
||
font-size: 12px;
|
||
color: #66635c;
|
||
}
|
||
.inv-name {
|
||
display: flex;
|
||
flex-direction: column;
|
||
font-weight: 500;
|
||
color: #081319;
|
||
}
|
||
.inv-name .sub {
|
||
font-weight: 400;
|
||
color: #66635c;
|
||
font-size: 12px;
|
||
}
|
||
.inv-amount {
|
||
font-weight: 500;
|
||
color: #081319;
|
||
}
|
||
</style>
|