junk/entemu/islands/Calendar.tsx

161 lines
6 KiB
TypeScript

import { useComputed, type Signal } from "@preact/signals";
import { addplan, bplan, IPlan, Plan, storage_keys } from "../common/plan.ts";
import { datefmt, datefrom } from "../common/date.ts";
export interface CalendarProps {
date: Signal<number>;
plans: Signal<Plan[]>;
}
const casht_a = -800;
const casht_b = -950;
const calendar_span = 30;
const calendar_days = new Array(calendar_span).fill(0).map((_, i) => i);
export default function Calendar({ date, plans }: CalendarProps) {
const _calendar = useComputed(() => {
// cal
let e: number[] = [];
const cal: Record<number, IPlan[]> = {};
for (const p of plans.value) {
e = e.concat(addplan(cal, p));
}
e = Array.from(new Set(e)).sort((a, b) => a - b);
let c = 600;
let current = 600;
let alert = -1;
let die = -1;
/*
const cal_ex: Record<number, IPlan[]> = {};
const e_ex: number[] = [];
*/
// cash
for (const d of e) {
for (const p of cal[d]) {
c -= p.cost;
p.current = c;
if (alert === -1 && c < casht_a && d < datefrom(6, 1, 1))
alert = d;
if (die === -1 && c < casht_b && d < datefrom(6, 1, 1))
die = d;
/*
if (c < 0) {
e_ex.push(...(addplan(cal_ex, bplan({
name: "借款",
start: d,
price: -p.cost,
})).concat(addplan(cal_ex, bplan({
name: "还贷",
start: d + 360,
price: p.cost * 1.05,
})))));
}
*/
}
if (d < date.value + 30) {
current = c;
}
}
/*
for (const k in cal_ex) {
if (cal[k])
cal[k].push(...cal_ex[k]);
else
cal[k] = cal_ex[k];
}
e.push(...e_ex);
*/
return {
cal: cal,
e: e,
cash: current,
alert: alert,
die: die,
};
});
const calendar = useComputed(() => _calendar.value.cal);
const event_days = useComputed(() => _calendar.value.e);
const cash = useComputed(() => _calendar.value.cash);
const date_alert = useComputed(() => _calendar.value.alert);
const date_die = useComputed(() => _calendar.value.die);
const storage = useComputed(() => {
const st: Record<string, number> = {};
for (const d of event_days.value) {
if (d >= date.value + 30)
break;
for (const p of calendar.value[d]) {
if (!p.plan.role) continue;
for (let role of p.plan.role.split(",")) {
let amount = p.plan.amount;
// if (!role) continue;
if (role[0] === "-") {
amount = -amount;
role = role.slice(1);
}
if (st[role] === undefined)
st[role] = 0;
st[role] += amount;
}
}
}
return st;
});
let last_date = -1;
return <div class="calendar">
<div class="calendar__calendar">
<div class="calender__cash">{cash.value}</div>
<div class="calendar__head">
<button onClick={() => date.value -= 360}>&lt;&lt;&lt;</button>
<button onClick={() => date.value -= 90}>&lt;&lt;</button>
<button onClick={() => date.value -= 30}>&lt;</button>
<span class="calendar__date">{datefmt(date.value)}</span>
<button onClick={() => date.value += 30}>&gt;</button>
<button onClick={() => date.value += 90}>&gt;&gt;</button>
<button onClick={() => date.value += 360}>&gt;&gt;&gt;</button>
</div>
<div class="calendar__days">
{calendar_days.map(d => <span class={calendar.value[d + date.value] ? "p" : ""}>{d + 1}</span>)}
</div>
</div>
<div class="calendar__alert" hidden>
<p>{date_alert.value === -1 ? "" : "资金周转风险 (现金 < " + casht_a + "): " + datefmt(date_alert.value)}</p>
<p>{date_die.value === -1 ? "" : "资金链断裂 (现金 < " + casht_b + "): " + datefmt(date_die.value)}</p>
</div>
<table class="calendar__storage">
<thead>
<tr><td></td><td></td></tr>
</thead>
<tbody>
{Object.entries(storage.value).filter(st => storage_keys[st[0]] && st[1]).map(st => <tr><td>{storage_keys[st[0]]}</td><td>{st[1]}</td></tr>)}
</tbody>
</table>
<table class="calendar__events" cellSpacing="0">
<thead>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</thead>
<tbody>
{calendar_days.map(d => d + date.value).filter(d => calendar.value[d]).map(d => calendar.value[d].map(p => {
const e = <tr>
<td>{last_date === p.date ? "" : datefmt(p.date)}</td>
<td>{p.plan.name}</td>
<td>{p.plan.amount}</td>
<td>{p.plan.times === 1 ? "1" : "" + (p.order + 1) + " / " + p.plan.times}</td>
<td>{p.cost !== 0 ? "¥" + (-p.cost) : ""}</td>
<td>{p.current}</td>
</tr>
last_date = p.date;
return e;
}))}
</tbody>
</table>
</div>;
}