HEL-269: 完成月结锁账、撤演示数据与代码收尾

锁账后写保护与闭期补录留痕;重开须审批并按版本链再结;列表/导出改走真实 API。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
总工
2026-08-30 21:42:22 +08:00
co-authored by Cursor multica-agent
parent f5e0915f63
commit e5e326514d
26 changed files with 3455 additions and 487 deletions
+220 -45
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="金牛集团管理端" />
<title>管理端 · 金牛集团</title>
<link rel="stylesheet" href="design-system.css?v=9" />
<link rel="stylesheet" href="design-system.css?v=10" />
</head>
<body data-portal="admin">
<a class="skip-link" href="#main-content">跳到主要内容</a>
@@ -25,6 +25,7 @@
<div class="nav-group">基础与结账</div>
<a data-view="companies" href="#companies"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M4 21V8l8-5 8 5v13"/><path d="M9 21v-6h6v6"/></svg><span class="nav-label">公司与账号</span></a>
<a data-view="settings" href="#settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="8.5"/><path d="M12 7v5l3.5 2"/></svg><span class="nav-label">结账与期初</span></a>
<a data-view="period-audit" href="#period-audit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 8v5l3 1.5"/><circle cx="12" cy="12" r="8.5"/></svg><span class="nav-label">审计记录</span></a>
<a data-view="reminders" href="#reminders"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M6 9a6 6 0 1 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10 19a2 2 0 0 0 4 0"/></svg><span class="nav-label">提醒管理</span></a>
</nav>
<div class="side-foot">
@@ -241,7 +242,7 @@
<div class="notice warn" id="pairNotice" style="display: none; margin-bottom: 14px;">
<div>
<div class="n-title">该公司组合暂无归集数据</div>
<div class="n-body">所选组合在正式环境中将按相同口径实时归集,当前仅内置演示数据组合</div>
<div class="n-body">所选组合暂无已确认往来,列表将显示「暂无数据」</div>
</div>
</div>
@@ -336,6 +337,7 @@
<button data-audit-filter="起算" data-label="起算区间校准" aria-pressed="false">起算区间校准<span class="tab-count">0</span></button>
<button data-audit-filter="账户" data-label="银行账户登记" aria-pressed="false">银行账户登记<span class="tab-count">0</span></button>
<button data-audit-filter="手工" data-label="公司手工记录" aria-pressed="false">公司手工记录<span class="tab-count">0</span></button>
<button data-audit-filter="reopen" data-label="重开审批" aria-pressed="false">重开审批<span class="tab-count" id="reopenTabCount">0</span></button>
</div>
<div class="field" style="min-width: 180px;">
<label for="auditCompany">公司</label>
@@ -343,7 +345,7 @@
</div>
</div>
<div class="table-wrap" style="margin-top: 14px;">
<div class="table-wrap" style="margin-top: 14px;" id="auditTableWrap">
<table class="ds-table audit-table">
<thead>
<tr>
@@ -364,6 +366,19 @@
<span>审核人:系统管理员 · 操作实时写入审核日志</span>
</div>
</div>
<div id="reopenQueue" hidden style="margin-top: 14px;">
<div class="card">
<div class="card-head">
<span class="card-title">重开审批<span class="sub">待审批申请需管理员确认后才解除锁定</span></span>
</div>
<div id="reopenQueueList"></div>
<div class="empty" id="reopenQueueEmpty">
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><circle cx="12" cy="12" r="8.5"/><path d="M12 8v5l3 1.5"/></svg>
<div class="e-title">暂无重开申请</div>
锁定账期的重开申请会显示在这里。
</div>
</div>
</div>
</section>
<section class="app-view" data-page="flows">
@@ -426,9 +441,14 @@
</thead>
<tbody></tbody>
</table>
<div class="empty" id="flowEmpty" hidden>
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="4" y="5" width="16" height="14" rx="2"/><path d="M4 10h16"/></svg>
<div class="e-title">暂无数据</div>
当前筛选条件下没有已确认工作表的银行流水。
</div>
<div class="table-foot">
<span id="flowCount">共 0 笔</span>
<span class="meta" id="flowsRangeMeta">数据范围:加载中…</span>
<span class="num" id="flowSum"></span>
</div>
</div>
</section>
@@ -491,15 +511,8 @@
<div class="card-head">
<span class="card-title">账期时间轴<span class="sub" id="timelineSub">全局起算日 2026-01-01 起,每月 5 日结账</span></span>
</div>
<div class="timeline">
<div class="tl-cell closed"><div class="tl-month">2026-01</div><div class="tl-state">已结账</div></div>
<div class="tl-cell closed"><div class="tl-month">2026-02</div><div class="tl-state">已结账</div></div>
<div class="tl-cell closed"><div class="tl-month">2026-03</div><div class="tl-state">已结账</div></div>
<div class="tl-cell closed"><div class="tl-month">2026-04</div><div class="tl-state">已结账</div></div>
<div class="tl-cell closed"><div class="tl-month">2026-05</div><div class="tl-state">已结账</div></div>
<div class="tl-cell closed"><div class="tl-month">2026-06</div><div class="tl-state">已结账</div></div>
<div class="tl-cell current" id="tl-current"><div class="tl-month">2026-07</div><div class="tl-state" id="tl-current-state">进行中</div></div>
<div class="tl-cell open"><div class="tl-month">2026-08</div><div class="tl-state">归集中</div></div>
<div class="timeline" id="periodTimeline">
<div class="tl-cell open"><div class="tl-month"></div><div class="tl-state">加载中</div></div>
</div>
</div>
@@ -579,54 +592,146 @@
<div class="card" id="closingPanel">
<div class="card-head">
<span class="card-title">月度结账<span class="sub" id="closingDescription">2026 年 7 月 · 当前未达到结账条件</span></span>
<span class="pill pill-danger" id="closingStatus">已阻断</span>
<span class="card-title">月度结账<span class="sub" id="closingDescription">加载账期状态…</span></span>
<span class="pill pill-muted" id="closingStatus"></span>
</div>
<div id="closingBody">
<div class="closing-check-list">
<div class="list-row">
<span class="pill pill-success" data-closing-check>公司流水提交</span>
<div class="lr-main"><div class="lr-title">流水提交</div><div class="lr-sub" data-closing-sub>5 / 6 家已完成</div></div>
<div class="lr-side"><span class="pill pill-warn" data-closing-state>待处理</span></div>
<div class="list-row" data-closing-key="submissions">
<span class="pill pill-muted" data-closing-check>流水提交</span>
<div class="lr-main"><div class="lr-title">流水提交</div><div class="lr-sub" data-closing-sub></div></div>
<div class="lr-side"><span class="pill pill-muted" data-closing-state></span></div>
</div>
<div class="list-row">
<span class="pill pill-success" data-closing-check>账户连续</span>
<div class="lr-main"><div class="lr-title">账户连续</div><div class="lr-sub" data-closing-sub>2 个账户存在断档</div></div>
<div class="lr-side"><span class="pill pill-danger" data-closing-state>阻断</span></div>
<div class="list-row" data-closing-key="coverage">
<span class="pill pill-muted" data-closing-check>账户连续</span>
<div class="lr-main"><div class="lr-title">账户连续</div><div class="lr-sub" data-closing-sub></div></div>
<div class="lr-side"><span class="pill pill-muted" data-closing-state></span></div>
</div>
<div class="list-row">
<span class="pill pill-success" data-closing-check>审核事项</span>
<div class="lr-main"><div class="lr-title">审核完成</div><div class="lr-sub" data-closing-sub>0 项尚未处理</div></div>
<div class="lr-side"><span class="pill pill-danger" data-closing-state>阻断</span></div>
<div class="list-row" data-closing-key="reviews">
<span class="pill pill-muted" data-closing-check>审核完成</span>
<div class="lr-main"><div class="lr-title">审核完成</div><div class="lr-sub" data-closing-sub></div></div>
<div class="lr-side"><span class="pill pill-muted" data-closing-state></span></div>
</div>
<div class="list-row">
<span class="pill pill-success" data-closing-check>期初余额</span>
<div class="lr-main"><div class="lr-title">期初锁定</div><div class="lr-sub" data-closing-sub>已锁定 2026.01.01</div></div>
<div class="lr-side"><span class="pill pill-success" data-closing-state>已通过</span></div>
<div class="list-row" data-closing-key="opening">
<span class="pill pill-muted" data-closing-check>期初锁定</span>
<div class="lr-main"><div class="lr-title">期初锁定</div><div class="lr-sub" data-closing-sub></div></div>
<div class="lr-side"><span class="pill pill-muted" data-closing-state></span></div>
</div>
</div>
<div class="grid grid-4" id="closingSummary" style="margin-top: 16px;">
<div><div class="meta">账期</div><div class="num" id="ckMonth"></div></div>
<div><div class="meta">期末归集</div><div class="num" id="ckNet"></div></div>
<div><div class="meta">结转去向</div><div class="num" id="ckCarry"></div></div>
<div><div class="meta">月报编号</div><div class="num" id="ckReport">尚未生成</div></div>
</div>
<div class="notice danger" id="block-notice" style="margin-top: 16px; display: none;">
<div>
<div class="n-title">存在阻断项,暂不能执行 2026-07 月度结账</div>
<div class="n-title" id="block-notice-title">存在阻断项,暂不能执行月度结账</div>
<div class="n-body" id="block-notice-body">请先处理待审核事项与账户断档。</div>
</div>
</div>
<div class="notice danger" id="failed-notice" style="margin-top: 16px; display: none;">
<div>
<div class="n-title">结账失败</div>
<div class="n-body" id="failed-notice-body">未改动任何数据,可重新执行结账。</div>
</div>
</div>
<div class="notice info" id="closed-notice" style="margin-top: 16px; display: none;">
<div>
<div class="n-title">2026-07 已结账</div>
<div class="n-body" id="closed-notice-body">结账后各公司 7 月往来数据已锁定,期初数将结转至 2026-08 账期。</div>
<div class="n-title" id="closed-notice-title">账期已锁定</div>
<div class="n-body" id="closed-notice-body">结账后各公司往来数据已锁定,期初数将结转至下一账期。</div>
</div>
</div>
<div class="notice warn" id="reopened-notice" style="margin-top: 16px; display: none;">
<div>
<div class="n-title">账期已重开</div>
<div class="n-body" id="reopened-notice-body">窗口内可更正并留痕,到期自动恢复锁定。</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 16px; justify-content: flex-end; flex-wrap: wrap;">
<span class="meta" id="closingHistory" style="margin-right: auto;">最近结账:2026 年 6 月 · 系统管理员 · 2026.07.05 18:10</span>
<div class="row" style="margin-top: 16px; justify-content: flex-end; flex-wrap: wrap; gap: 8px;">
<span class="meta" id="closingHistory" style="margin-right: auto;"></span>
<span class="loading-inline" id="closingBusy" hidden>正在锁定账期并生成月报…请勿关闭页面</span>
<button class="btn" id="downloadMonthReport" hidden>下载月报</button>
<button class="btn" id="runClosingCheck">重新检查</button>
<button class="btn btn-primary" id="executeClosing" disabled title="请先处理全部阻断事项">执行 2026-07 月度结账</button>
<button class="btn btn-primary" id="executeClosing" disabled title="请先处理全部阻断事项">执行月度结账</button>
</div>
</div>
</div>
</section>
<section class="app-view" data-page="period-audit">
<div class="page-head">
<div>
<h1>审计记录</h1>
<p class="page-sub">谁、什么时间、改了什么、为什么。记录只增不改,可倒查至银行原始流水。</p>
</div>
</div>
<div class="filters">
<div class="field">
<label for="paSince">起始日期</label>
<input class="input" id="paSince" type="date" />
</div>
<div class="field">
<label for="paUntil">结束日期</label>
<input class="input" id="paUntil" type="date" />
</div>
<div class="field">
<label for="paAction">动作类型</label>
<select class="select" id="paAction">
<option value="">全部动作</option>
<option value="close_execute">月结</option>
<option value="reopen_request">重开申请</option>
<option value="reopen_approve">重开审批通过</option>
<option value="reopen_reject">重开驳回</option>
<option value="reopen_expire">重开到期恢复</option>
<option value="close_fail">月结失败</option>
</select>
</div>
<div class="field">
<label for="paCompany">涉及公司</label>
<input class="input" id="paCompany" type="text" placeholder="公司名 / 月报号" />
</div>
<button class="btn" id="paApply" type="button">查询</button>
<button class="btn btn-ghost" id="paReset" type="button">清除筛选条件</button>
</div>
<div class="notice danger" id="paError" style="display: none; margin-bottom: 14px;">
<div>
<div class="n-title">审计记录加载失败</div>
<div class="n-body">已加载的数据未被改动 · 请稍后重试</div>
</div>
<button class="btn btn-primary btn-sm" type="button" id="paReload">重新加载</button>
</div>
<div class="table-wrap">
<table class="ds-table">
<thead>
<tr>
<th>时间</th>
<th>操作人</th>
<th>动作</th>
<th class="wrap">对象与原因</th>
<th>前后变化</th>
<th>凭证</th>
</tr>
</thead>
<tbody id="periodAuditRows"></tbody>
</table>
<div class="table-foot">
<span id="periodAuditFoot">共 0 条</span>
<span>审计记录只增不改 · 永久保留</span>
</div>
</div>
<div class="audit-event-cards" id="periodAuditCards"></div>
<div class="empty" id="periodAuditEmpty" hidden>
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><circle cx="12" cy="12" r="8.5"/><path d="M12 8v5l3 1.5"/></svg>
<div class="e-title">没有符合条件的审计记录</div>
调整筛选条件后再查询,或清除筛选查看全部留痕。
</div>
</section>
<section class="app-view" data-page="reminders">
<div class="page-head">
<div>
@@ -913,29 +1018,99 @@
<div class="modal-backdrop" id="closingDialog">
<div class="modal">
<div class="modal-head">
<span class="modal-title">确认执行 2026-07 月度结账</span>
<span class="modal-title" id="closingDialogTitle">确认执行月度结账</span>
<button type="button" class="modal-close" data-close-closing aria-label="关闭">×</button>
</div>
<p class="modal-sub">结账后成员公司的 7 月流水与往来确认将锁定,不可再修改。</p>
<p class="modal-sub" id="closingDialogSub">结账后该月流水与往来确认将锁定,不可再直接修改。</p>
<form id="closingForm" novalidate>
<div class="notice warn" style="margin-bottom: 14px;">
<div>
<div class="n-title">请确认后果</div>
<div class="n-body" id="closingConsequences">① 该账期往来结果锁定,普通写入将被拒绝。<br />② 生成不可静默改写的月报并结转期末。<br />③ 确需更正须申请重开,经审批后留痕修改。</div>
</div>
</div>
<div class="detail-box">
<dl class="kv">
<dt>结账账期</dt><dd>2026-07</dd>
<dt>检查通过</dt><dd>全部前置检查已通过</dd>
<dt>期末归集</dt><dd>往来净额将结转至 2026-08 期初</dd>
<dt>结账账期</dt><dd id="cdMonth"></dd>
<dt>检查通过</dt><dd id="cdChecks"></dd>
<dt>期末归集</dt><dd id="cdCarry">往来净额将结转至下一账期期初</dd>
</dl>
</div>
<label class="row" style="gap: 6px; font-size: 13px; margin-top: 14px; cursor: pointer;">
<input type="checkbox" name="confirm" required style="width: auto;" />我已复核 2026 年 7 月结账结果
<input type="checkbox" name="confirm" id="closingConfirmBox" required style="width: auto;" />我已复核结账结果并知晓锁定后
</label>
<div class="modal-actions">
<button type="button" class="btn" data-close-closing>再想想</button>
<button type="submit" class="btn btn-primary">确认结账</button>
<button type="submit" class="btn btn-primary" id="closingConfirmBtn">确认结账并锁定</button>
</div>
</form>
</div>
</div>
<div class="modal-backdrop" id="reopenRequestDialog">
<div class="modal">
<div class="modal-head">
<span class="modal-title" id="reopenRequestTitle">申请重开账期</span>
<button type="button" class="modal-close" data-close-reopen-req aria-label="关闭">×</button>
</div>
<p class="modal-sub">重开解除锁定但保留原月报;更正后须重新结账。</p>
<form id="reopenRequestForm" novalidate>
<div class="notice warn" style="margin-bottom: 14px;">
<div>
<div class="n-title">请确认后果</div>
<div class="n-body">① 批准后该账期暂时解除锁定,所有更正留痕。<br />② 原月报作废,窗口结束后或提前结束须重新结账。<br />③ 申请人、原因、时间与前后变化可倒查。</div>
</div>
</div>
<div class="field">
<label for="reopenReason">重开原因 *</label>
<textarea class="textarea" id="reopenReason" minlength="10" required placeholder="不少于 10 个字,将写入审计记录"></textarea>
<span class="hint">原因会进入审计记录,不可事后删改。</span>
</div>
<div class="field" style="margin-top: 12px;">
<label for="reopenCompanies">涉及公司与事项</label>
<input class="input" id="reopenCompanies" placeholder="例如:金牛煤业 ↔ 金牛物流 运输费补录" />
</div>
<div class="field" style="margin-top: 12px;">
<label for="reopenDays">申请窗口(天)</label>
<input class="input num-input" id="reopenDays" type="number" min="1" max="30" value="3" />
<span class="hint">到期自动恢复锁定;默认 3 天。</span>
</div>
<div class="modal-actions">
<button type="button" class="btn" data-close-reopen-req>取消</button>
<button type="submit" class="btn btn-warn">提交重开申请</button>
</div>
</form>
</div>
</div>
<div class="modal-backdrop" id="reopenDecideDialog">
<div class="modal wide">
<div class="modal-head">
<span class="modal-title" id="reopenDecideTitle">重开审批</span>
<button type="button" class="modal-close" data-close-reopen-dec aria-label="关闭">×</button>
</div>
<p class="modal-sub" id="reopenDecideSub"></p>
<div class="detail-box">
<dl class="kv" id="reopenDecideKv"></dl>
</div>
<div class="diff-grid" id="reopenDiff" style="margin-top: 16px;"></div>
<div class="notice info" style="margin-top: 14px;">
<div>
<div class="n-title">凭证与入账方式</div>
<div class="n-body">批准后按审批窗口解除锁定;银行原始流水永不改写,更正通过新决定与留痕完成。</div>
</div>
</div>
<div class="field" style="margin-top: 14px;">
<label for="reopenComment">审批意见(驳回必填)</label>
<textarea class="textarea" id="reopenComment" placeholder="通过可写备注;驳回须说明原因"></textarea>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-danger" id="reopenRejectBtn">驳回申请</button>
<button type="button" class="btn btn-warn" id="reopenApproveBtn">同意重开</button>
</div>
</div>
</div>
<!-- 往来穿透弹窗 -->
<div class="modal-backdrop" id="traceModal">
<div class="modal wide">
@@ -1033,6 +1208,6 @@
</div>
</div>
<div class="toast-region" id="toastRegion" aria-live="polite"></div>
<script src="app.js?v=14"></script>
<script src="app.js?v=15"></script>
</body>
</html>