This commit is contained in:
admin
2026-02-21 10:51:25 +09:00
parent 584530937b
commit 3b028b8fc0
25 changed files with 1852 additions and 244 deletions

View File

@@ -39,6 +39,67 @@
color: #090;
margin-top: 8px;
}
/* 账户搜索下拉框相关样式 */
.account-search-container {
position: relative;
width: 100%;
}
.account-search-input {
width: 100%;
padding: 6px !important;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.account-search-input:focus {
outline: none;
border-color: #4caf50;
box-shadow: 0 0 4px rgba(76, 175, 80, 0.3);
}
.account-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ccc;
border-top: none;
border-radius: 0 0 4px 4px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.account-dropdown.active {
display: block;
}
.account-option {
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
font-size: 14px;
}
.account-option:hover,
.account-option.selected {
background-color: #e3f2fd;
}
.account-option-strong {
font-weight: bold;
color: #333;
}
.account-option-light {
color: #999;
font-size: 12px;
}
</style>
</head>
<body>
@@ -84,6 +145,187 @@
const API = "";
let accounts = [];
// 科目グループ定義(仕訳入力と同期)
const ACCOUNT_GROUPS = [
// 資産の部
{
label: "現金・預金",
codes: ["101", "102", "103", "104", "105"],
},
{
label: "売上債権",
codes: ["201", "202"],
},
{
label: "有価証券",
codes: ["301"],
},
{
label: "棚卸資産",
codes: ["210"],
},
{
label: "他流動資産",
codes: ["203", "204", "205", "206", "207", "208"],
},
{
label: "有形固定資産",
codes: ["401", "402", "403", "404", "405", "406", "407"],
},
{
label: "無形固定資産",
codes: ["408"],
},
{
label: "投資その他資産",
codes: ["410"],
},
// 負債の部
{
label: "仕入債務",
codes: ["501"],
},
{
label: "その他流動負債",
codes: ["502", "503", "504", "505", "506", "507", "508"],
},
{
label: "固定負債",
codes: ["509"],
},
// 資本の部
{
label: "資本金",
codes: ["601"],
},
{
label: "利益剰余金",
codes: ["602"],
},
// 収益・費用
{
label: "収益",
codes: ["701", "702", "703", "704", "705"],
},
{
label: "費用",
codes: [
"801",
"802",
"803",
"804",
"805",
"806",
"807",
"808",
"809",
"810",
"811",
"812",
"813",
"814",
"815",
"816",
"817",
"818",
"819",
"820",
],
},
];
// ========================================
// 账户搜索功能
// ========================================
function setupAccountSearch(inputElement, row) {
const container = inputElement.closest(".account-search-container");
const dropdown = container.querySelector(".account-dropdown");
inputElement.addEventListener("input", (e) => {
const searchText = e.target.value.toLowerCase().trim();
if (!searchText) {
dropdown.classList.remove("active");
return;
}
// 过滤账户
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);
});
}
dropdown.classList.add("active");
});
// クリック以外の場所のドロップダウンを非表示
document.addEventListener("click", (e) => {
if (!container.contains(e.target)) {
dropdown.classList.remove("active");
}
});
// フォーカス時にドロップダウンを表示(既存値がある場合)
inputElement.addEventListener("focus", () => {
if (inputElement.value) {
dropdown.classList.add("active");
}
});
}
// 科目選択肢をグループ分けして構築
function buildAccountOptions() {
let html = "";
ACCOUNT_GROUPS.forEach((group) => {
html += `<optgroup label="${group.label}">`;
accounts
.filter((a) => group.codes.includes(a.account_code))
.forEach((a) => {
html += `<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`;
});
html += `</optgroup>`;
});
// 未分組の科目を「その他」グループに追加
const groupedCodes = ACCOUNT_GROUPS.flatMap((g) => g.codes);
const others = accounts.filter(
(a) => !groupedCodes.includes(a.account_code),
);
if (others.length > 0) {
html += `<optgroup label="その他">`;
others.forEach((a) => {
html += `<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`;
});
html += `</optgroup>`;
}
return html;
}
// ------------------------------------
// 初期化
// ------------------------------------
@@ -122,14 +364,14 @@
tr.innerHTML = `
<td>
<select>
${accounts
.map(
(a) =>
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`,
)
.join("")}
</select>
<div class="account-search-container">
<input
type="text"
class="account-search-input account"
placeholder="科目を入力・検索"
autocomplete="off" />
<div class="account-dropdown"></div>
</div>
</td>
<td><input type="number" min="0"></td>
<td><input type="number" min="0"></td>
@@ -138,10 +380,19 @@
tbody.appendChild(tr);
// Setup account search for this row
const accountInput = tr.querySelector(".account-search-input");
setupAccountSearch(accountInput, tr);
if (line) {
tr.querySelector("select").value = line.account_id;
tr.querySelectorAll("input")[0].value = line.debit || "";
tr.querySelectorAll("input")[1].value = line.credit || "";
// Find the account and populate the input
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 || "";
}
// 入力時に合計を自動更新
@@ -191,9 +442,10 @@
c = 0;
document.querySelectorAll("#linesTable tbody tr").forEach((tr) => {
const accountId = Number(tr.querySelector("select").value);
const debit = Number(tr.querySelectorAll("input")[0].value || 0);
const credit = Number(tr.querySelectorAll("input")[1].value || 0);
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);
if (debit === 0 && credit === 0) return;
lines.push({ account_id: accountId, debit, credit });
d += debit;