Improve ledger monthly header
This commit is contained in:
parent
7747323588
commit
8627d8404a
|
|
@ -73,7 +73,7 @@ input:focus-visible {
|
|||
.ledger-header {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-height: 210px;
|
||||
min-height: 180px;
|
||||
padding: max(18px, env(safe-area-inset-top)) 18px 20px;
|
||||
background: #087f72;
|
||||
color: #ffffff;
|
||||
|
|
@ -211,8 +211,7 @@ input:focus-visible {
|
|||
background: #edf7f4;
|
||||
}
|
||||
|
||||
.ledger-switcher,
|
||||
.month-switcher {
|
||||
.ledger-switcher {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -228,19 +227,12 @@ input:focus-visible {
|
|||
font-weight: 780;
|
||||
}
|
||||
|
||||
.month-switcher {
|
||||
margin-top: 22px;
|
||||
padding: 0;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.monthly-summary {
|
||||
display: grid;
|
||||
grid-template-columns: 1.35fr 1fr 1fr;
|
||||
align-items: end;
|
||||
gap: 12px;
|
||||
margin-top: 9px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(132px, .72fr);
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.summary-main,
|
||||
|
|
@ -255,11 +247,14 @@ input:focus-visible {
|
|||
}
|
||||
|
||||
.monthly-summary span {
|
||||
margin-bottom: 3px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.summary-main span {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.summary-main strong {
|
||||
overflow: hidden;
|
||||
font-size: 23px;
|
||||
|
|
@ -267,17 +262,38 @@ input:focus-visible {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.summary-stat {
|
||||
.summary-main strong.compact {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.summary-secondary {
|
||||
min-width: 0;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.22);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.summary-stat {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.summary-stat + .summary-stat {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.summary-stat strong {
|
||||
overflow: hidden;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.summary-stat strong.compact {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.income-stat strong {
|
||||
color: #d9ffe7;
|
||||
}
|
||||
|
|
@ -287,8 +303,8 @@ input:focus-visible {
|
|||
}
|
||||
|
||||
.ledger-content {
|
||||
height: calc(100vh - 210px);
|
||||
height: calc(100dvh - 210px);
|
||||
height: calc(100vh - 180px);
|
||||
height: calc(100dvh - 180px);
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 0 16px 112px;
|
||||
|
|
@ -346,10 +362,29 @@ input:focus-visible {
|
|||
|
||||
.list-toolbar-heading {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.list-month-switcher {
|
||||
min-height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: #26342f;
|
||||
}
|
||||
|
||||
.native-month-input {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.list-toolbar strong {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
|
@ -1368,14 +1403,15 @@ input:focus-visible {
|
|||
}
|
||||
|
||||
.monthly-summary {
|
||||
gap: 8px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(126px, .72fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.summary-main strong {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.summary-stat {
|
||||
.summary-secondary {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,15 +35,23 @@ const generatingInvitation = ref(false);
|
|||
const visibleCount = ref(ENTRY_PAGE_SIZE);
|
||||
const ledgerContent = ref<HTMLElement | null>(null);
|
||||
const loadMoreTrigger = ref<HTMLElement | null>(null);
|
||||
const monthInput = ref<HTMLInputElement | null>(null);
|
||||
const selectedMonth = ref(toMonthValue(new Date()));
|
||||
let loadMoreObserver: IntersectionObserver | null = null;
|
||||
|
||||
const selectedMonthParts = computed(() => {
|
||||
const match = /^(\d{4})-(\d{2})$/.exec(selectedMonth.value);
|
||||
if (!match) return null;
|
||||
return { year: Number(match[1]), month: Number(match[2]) - 1 };
|
||||
});
|
||||
const monthlyEntries = computed(() => {
|
||||
const current = new Date();
|
||||
const selected = selectedMonthParts.value;
|
||||
if (!selected) return [];
|
||||
return store.entries.filter((entry) => {
|
||||
const occurredAt = new Date(entry.occurredAt);
|
||||
return entry.ledgerIds.includes(ledgerStore.currentLedgerId)
|
||||
&& occurredAt.getFullYear() === current.getFullYear()
|
||||
&& occurredAt.getMonth() === current.getMonth();
|
||||
&& occurredAt.getFullYear() === selected.year
|
||||
&& occurredAt.getMonth() === selected.month;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -57,6 +65,9 @@ const totals = computed(() => {
|
|||
{ income: 0, expense: 0 },
|
||||
);
|
||||
});
|
||||
const balanceText = computed(() => formatMoney(totals.value.income - totals.value.expense));
|
||||
const incomeText = computed(() => formatMoney(totals.value.income));
|
||||
const expenseText = computed(() => formatMoney(totals.value.expense));
|
||||
|
||||
const visibleMonthlyEntries = computed(() => monthlyEntries.value.slice(0, visibleCount.value));
|
||||
const hasMoreEntries = computed(() => visibleCount.value < monthlyEntries.value.length);
|
||||
|
|
@ -91,12 +102,8 @@ const groupedEntries = computed(() => {
|
|||
});
|
||||
|
||||
const monthTitle = computed(() => {
|
||||
const date = new Date();
|
||||
return `${date.getFullYear()}年 ${date.getMonth() + 1}月`;
|
||||
});
|
||||
const monthListTitle = computed(() => {
|
||||
const date = new Date();
|
||||
return `${date.getFullYear()}年${date.getMonth() + 1}月流水`;
|
||||
const selected = selectedMonthParts.value;
|
||||
return selected ? `${selected.year}年${selected.month + 1}月` : "选择月份";
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -129,6 +136,17 @@ function localDateKey(value: string) {
|
|||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
function toMonthValue(date: Date) {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
function openMonthPicker() {
|
||||
const input = monthInput.value;
|
||||
if (!input) return;
|
||||
if (typeof input.showPicker === "function") input.showPicker();
|
||||
else input.click();
|
||||
}
|
||||
|
||||
function dateHeading(value: string) {
|
||||
const date = new Date(`${value}T00:00:00`);
|
||||
const today = new Date();
|
||||
|
|
@ -261,23 +279,20 @@ async function shareLedger() {
|
|||
</div>
|
||||
</Transition>
|
||||
|
||||
<button class="month-switcher" type="button">
|
||||
{{ monthTitle }}
|
||||
<ChevronDown :size="14" />
|
||||
</button>
|
||||
|
||||
<section class="monthly-summary" aria-label="本月收支">
|
||||
<section class="monthly-summary" aria-label="月度收支">
|
||||
<div class="summary-main">
|
||||
<span>本月结余</span>
|
||||
<strong>¥ {{ formatMoney(totals.income - totals.expense) }}</strong>
|
||||
<span>结余</span>
|
||||
<strong :class="{ compact: balanceText.length > 12 }">¥ {{ balanceText }}</strong>
|
||||
</div>
|
||||
<div class="summary-stat income-stat">
|
||||
<span>收入</span>
|
||||
<strong>{{ formatMoney(totals.income) }}</strong>
|
||||
</div>
|
||||
<div class="summary-stat expense-stat">
|
||||
<span>支出</span>
|
||||
<strong>{{ formatMoney(totals.expense) }}</strong>
|
||||
<div class="summary-secondary">
|
||||
<div class="summary-stat income-stat">
|
||||
<span>收入</span>
|
||||
<strong :class="{ compact: incomeText.length > 10 }">{{ incomeText }}</strong>
|
||||
</div>
|
||||
<div class="summary-stat expense-stat">
|
||||
<span>支出</span>
|
||||
<strong :class="{ compact: expenseText.length > 10 }">{{ expenseText }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</header>
|
||||
|
|
@ -292,7 +307,11 @@ async function shareLedger() {
|
|||
<div class="list-toolbar">
|
||||
<div class="list-toolbar-copy">
|
||||
<div class="list-toolbar-heading">
|
||||
<strong>{{ monthListTitle }}</strong>
|
||||
<button class="list-month-switcher" type="button" @click="openMonthPicker">
|
||||
<strong>{{ monthTitle }}</strong>
|
||||
<ChevronDown :size="14" />
|
||||
</button>
|
||||
<input ref="monthInput" v-model="selectedMonth" class="native-month-input" type="month" aria-label="选择流水月份" tabindex="-1" />
|
||||
<span>{{ monthlyEntries.length }} 笔</span>
|
||||
</div>
|
||||
<div class="list-toolbar-totals">
|
||||
|
|
|
|||
Loading…
Reference in New Issue