修正
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
.account-search-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.account-search-input {
|
||||
@@ -52,6 +53,7 @@
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.account-search-input:focus {
|
||||
@@ -84,6 +86,9 @@
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.account-option:hover,
|
||||
@@ -94,11 +99,14 @@
|
||||
.account-option-strong {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: inline;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.account-option-light {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -241,43 +249,81 @@
|
||||
const container = inputElement.closest(".account-search-container");
|
||||
const dropdown = container.querySelector(".account-dropdown");
|
||||
|
||||
inputElement.addEventListener("input", (e) => {
|
||||
const searchText = e.target.value.toLowerCase().trim();
|
||||
// 科目選択履歴を保存(journal-entry.htmlと共通のlocalStorageキー)
|
||||
function saveAccountToRecentEdit(accountId) {
|
||||
if (!accountId) return;
|
||||
let recent = JSON.parse(localStorage.getItem("recentAccounts") || "[]");
|
||||
recent = recent.filter((id) => id !== accountId);
|
||||
recent.unshift(accountId);
|
||||
if (recent.length > 20) recent = recent.slice(0, 20);
|
||||
localStorage.setItem("recentAccounts", JSON.stringify(recent));
|
||||
}
|
||||
|
||||
if (!searchText) {
|
||||
dropdown.classList.remove("active");
|
||||
return;
|
||||
}
|
||||
function makeLbl(text) {
|
||||
const lbl = document.createElement("div");
|
||||
lbl.style.cssText = "padding:3px 12px;font-size:11px;color:#888;background:#f5f5f5;font-weight:bold;border-bottom:1px solid #e0e0e0;";
|
||||
lbl.textContent = text;
|
||||
return lbl;
|
||||
}
|
||||
|
||||
// 过滤账户
|
||||
const filtered = accounts.filter((acc) => {
|
||||
const code = acc.account_code.toLowerCase();
|
||||
const name = acc.account_name.toLowerCase();
|
||||
return code.includes(searchText) || name.includes(searchText);
|
||||
});
|
||||
|
||||
// 生成下拉建议
|
||||
dropdown.innerHTML = "";
|
||||
if (filtered.length === 0) {
|
||||
dropdown.innerHTML = `<div style="padding: 8px 12px; color: #999;">該当する科目がありません</div>`;
|
||||
} else {
|
||||
filtered.slice(0, 20).forEach((acc) => {
|
||||
const option = document.createElement("div");
|
||||
option.className = "account-option";
|
||||
option.innerHTML = `
|
||||
<span class="account-option-strong">${acc.account_code}</span>
|
||||
<span class="account-option-light">${acc.account_name}</span>
|
||||
`;
|
||||
option.addEventListener("click", () => {
|
||||
inputElement.value = `${acc.account_code} ${acc.account_name}`;
|
||||
inputElement.dataset.accountId = acc.account_id;
|
||||
dropdown.classList.remove("active");
|
||||
});
|
||||
dropdown.appendChild(option);
|
||||
// 显示科目下拉列表的函数
|
||||
function showAccountList(searchText = "") {
|
||||
const isFiltered = searchText.length > 0;
|
||||
let filtered = accounts;
|
||||
if (isFiltered) {
|
||||
filtered = accounts.filter((acc) => {
|
||||
const code = acc.account_code.toLowerCase();
|
||||
const name = acc.account_name.toLowerCase();
|
||||
return code.includes(searchText) || name.includes(searchText);
|
||||
});
|
||||
}
|
||||
|
||||
dropdown.innerHTML = "";
|
||||
|
||||
function appendOpt(acc) {
|
||||
const option = document.createElement("div");
|
||||
option.className = "account-option";
|
||||
option.innerHTML = `<span class="account-option-strong">${acc.account_code}</span><span class="account-option-light">${acc.account_name}</span>`;
|
||||
option.addEventListener("click", () => {
|
||||
inputElement.value = `${acc.account_code} ${acc.account_name}`;
|
||||
inputElement.dataset.accountId = acc.account_id;
|
||||
dropdown.classList.remove("active");
|
||||
saveAccountToRecentEdit(acc.account_id);
|
||||
});
|
||||
dropdown.appendChild(option);
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
dropdown.innerHTML = `<div style="padding: 8px 12px; color: #999;">該当する科目がありません</div>`;
|
||||
} else if (isFiltered) {
|
||||
const sorted = filtered.slice().sort((a, b) => a.account_code.localeCompare(b.account_code));
|
||||
sorted.slice(0, 50).forEach(appendOpt);
|
||||
} else {
|
||||
const recentIds = JSON.parse(localStorage.getItem("recentAccounts") || "[]");
|
||||
const recentItems = recentIds.map((id) => filtered.find((a) => a.account_id === id)).filter(Boolean);
|
||||
const recentSet = new Set(recentIds);
|
||||
const otherItems = filtered
|
||||
.filter((a) => !recentSet.has(a.account_id))
|
||||
.sort((a, b) => a.account_code.localeCompare(b.account_code));
|
||||
|
||||
if (recentItems.length > 0) {
|
||||
dropdown.appendChild(makeLbl("⭐ 最近使った科目"));
|
||||
recentItems.forEach(appendOpt);
|
||||
const sep = document.createElement("div");
|
||||
sep.style.cssText = "border-top:1px solid #e8e8e8;margin:2px 0;";
|
||||
dropdown.appendChild(sep);
|
||||
}
|
||||
dropdown.appendChild(makeLbl("📋 すべての科目"));
|
||||
otherItems.slice(0, 50).forEach(appendOpt);
|
||||
}
|
||||
|
||||
dropdown.classList.add("active");
|
||||
}
|
||||
|
||||
// 入力時:フィルター
|
||||
inputElement.addEventListener("input", (e) => {
|
||||
const searchText = e.target.value.toLowerCase().trim();
|
||||
showAccountList(searchText);
|
||||
});
|
||||
|
||||
// クリック以外の場所のドロップダウンを非表示
|
||||
@@ -287,11 +333,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
// フォーカス時にドロップダウンを表示(既存値がある場合)
|
||||
// フォーカス時:すべての科目を表示
|
||||
inputElement.addEventListener("focus", () => {
|
||||
if (inputElement.value) {
|
||||
dropdown.classList.add("active");
|
||||
}
|
||||
showAccountList(inputElement.value.toLowerCase().trim());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -386,13 +430,20 @@
|
||||
|
||||
if (line) {
|
||||
// Find the account and populate the input
|
||||
const account = accounts.find(a => a.account_id === line.account_id);
|
||||
const account = accounts.find(
|
||||
(a) => a.account_id === line.account_id,
|
||||
);
|
||||
if (account) {
|
||||
accountInput.value = `${account.account_code} ${account.account_name}`;
|
||||
accountInput.dataset.accountId = account.account_id;
|
||||
}
|
||||
tr.querySelectorAll("input")[1].value = line.debit || "";
|
||||
tr.querySelectorAll("input")[2].value = line.credit || "";
|
||||
// type="number" のみを取得
|
||||
const numberInputs = tr.querySelectorAll("input[type='number']");
|
||||
// Decimal 値を数値に変換してから代入
|
||||
const debitVal = Number(line.debit) || 0;
|
||||
const creditVal = Number(line.credit) || 0;
|
||||
numberInputs[0].value = debitVal > 0 ? String(debitVal) : "";
|
||||
numberInputs[1].value = creditVal > 0 ? String(creditVal) : "";
|
||||
}
|
||||
|
||||
// 入力時に合計を自動更新
|
||||
@@ -414,8 +465,26 @@
|
||||
let d = 0,
|
||||
c = 0;
|
||||
document.querySelectorAll("#linesTable tbody tr").forEach((tr) => {
|
||||
d += Number(tr.querySelectorAll("input")[0].value || 0);
|
||||
c += Number(tr.querySelectorAll("input")[1].value || 0);
|
||||
// type="number" のみを取得(科目検索入力框を除外)
|
||||
const numberInputs = tr.querySelectorAll("input[type='number']");
|
||||
if (numberInputs.length >= 2) {
|
||||
const debitStr = numberInputs[0]?.value;
|
||||
const creditStr = numberInputs[1]?.value;
|
||||
|
||||
// 空または 0 を含む場合は 0、そうでなければ数値に変換
|
||||
const debitVal =
|
||||
debitStr && debitStr.trim()
|
||||
? Number(debitStr.toString().replace(/,/g, ""))
|
||||
: 0;
|
||||
const creditVal =
|
||||
creditStr && creditStr.trim()
|
||||
? Number(creditStr.toString().replace(/,/g, ""))
|
||||
: 0;
|
||||
|
||||
// NaN チェック
|
||||
if (!isNaN(debitVal)) d += debitVal;
|
||||
if (!isNaN(creditVal)) c += creditVal;
|
||||
}
|
||||
});
|
||||
document.getElementById("debitTotal").textContent = d.toLocaleString();
|
||||
document.getElementById("creditTotal").textContent = c.toLocaleString();
|
||||
@@ -429,13 +498,28 @@
|
||||
result.textContent = "";
|
||||
result.className = "";
|
||||
|
||||
const entryDate = document.getElementById("entryDate").value;
|
||||
const entryDateInput = document.getElementById("entryDate");
|
||||
const entryDate = entryDateInput.value;
|
||||
const desc = document.getElementById("description").value;
|
||||
|
||||
if (!entryDate || !desc) {
|
||||
if (entryDateInput.validity.badInput) {
|
||||
showError(
|
||||
"日付が正しくありません。存在しない日付が入力されています(例:11月31日は無効です)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!entryDate && !desc) {
|
||||
showError("日付と摘要は必須です");
|
||||
return;
|
||||
}
|
||||
if (!entryDate) {
|
||||
showError("日付を入力してください");
|
||||
return;
|
||||
}
|
||||
if (!desc) {
|
||||
showError("摘要を入力してください");
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
let d = 0,
|
||||
@@ -444,8 +528,9 @@
|
||||
document.querySelectorAll("#linesTable tbody tr").forEach((tr) => {
|
||||
const accountInput = tr.querySelector(".account-search-input");
|
||||
const accountId = Number(accountInput.dataset.accountId || 0);
|
||||
const debit = Number(tr.querySelectorAll("input")[1].value || 0);
|
||||
const credit = Number(tr.querySelectorAll("input")[2].value || 0);
|
||||
const numberInputs = tr.querySelectorAll("input[type='number']");
|
||||
const debit = Number(numberInputs[0]?.value || 0);
|
||||
const credit = Number(numberInputs[1]?.value || 0);
|
||||
if (debit === 0 && credit === 0) return;
|
||||
lines.push({ account_id: accountId, debit, credit });
|
||||
d += debit;
|
||||
|
||||
Reference in New Issue
Block a user