HEL-122: 第3批——管理端剩余6页 + 对应弹窗换原型壳
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
+769
-62
@@ -260,89 +260,796 @@
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="pair">
|
||||
<header class="page-heading"><div><h1>往来查询</h1><p>按公司对查询双方口径、科目和逐笔凭证</p></div></header>
|
||||
<section class="query-band">
|
||||
<form class="pair-query-form" data-pair-form>
|
||||
<label class="field"><span>本方公司</span><select name="from"><option>A公司</option><option>B公司</option><option>C公司</option><option>D公司</option><option>E公司</option><option>F公司</option></select></label>
|
||||
<button class="swap-button" type="button" data-swap aria-label="交换公司" title="交换公司"><svg><use href="icons.svg#arrow-left-right"/></svg></button>
|
||||
<label class="field"><span>对方公司</span><select name="to"><option>B公司</option><option>C公司</option><option>D公司</option><option>E公司</option><option>F公司</option><option>A公司</option></select></label>
|
||||
<label class="field"><span>统计截止</span><input name="end" type="date" value="2026-07-31" /></label>
|
||||
<button class="button primary" type="submit"><svg><use href="icons.svg#search"/></svg>查询</button>
|
||||
</form>
|
||||
</section>
|
||||
<section class="pair-report" id="pairReport">
|
||||
<header class="pair-report-heading"><div><h2><span data-pair-from>A公司</span> 与 <span data-pair-to>B公司</span></h2><p id="pairPeriod">统计口径 2026.01.01—2026.07.31</p></div><span class="status warning" id="pairReviewStatus">含 1 笔待审核</span></header>
|
||||
<div class="pair-balance-line"><div><span>期初余额</span><strong id="pairOpening">0.00</strong></div><div><span>本期借方</span><strong id="pairDebit">2,416.00</strong></div><div><span>本期贷方</span><strong id="pairCredit">1,136.00</strong></div><div class="pair-final"><span>期末结果</span><strong id="pairFinal">应收 1,280.00<small>万元</small></strong></div></div>
|
||||
<div class="subject-strip"><button class="is-active" data-subject-filter="all" aria-pressed="true"><span>全部往来</span><strong data-subject-total="all">5 笔</strong></button><button data-subject-filter="应收" aria-pressed="false"><span>应收</span><strong data-subject-total="应收">1,280.00</strong></button><button data-subject-filter="其他应收" aria-pressed="false"><span>其他应收</span><strong data-subject-total="其他应收">640.00</strong></button><button data-subject-filter="应付" aria-pressed="false"><span>应付</span><strong data-subject-total="应付">728.00</strong></button><button data-subject-filter="其他应付" aria-pressed="false"><span>其他应付</span><strong data-subject-total="其他应付">408.00</strong></button></div>
|
||||
<div class="table-scroll"><table class="data-table" id="pairTransactions"><thead><tr><th>交易日期</th><th>方向</th><th>科目</th><th>本方账户</th><th>对方账户</th><th>摘要</th><th>匹配</th><th class="number">金额(万元)</th></tr></thead><tbody><tr data-subject="应收"><td>2026.07.18</td><td>转出</td><td>应收</td><td>中信 · 5316</td><td>工行 · 9481</td><td>往来款</td><td><span class="status success">双边匹配</span></td><td class="number">1,000.00</td></tr><tr data-subject="应收"><td>2026.07.06</td><td>转入</td><td>应收</td><td>中信 · 5316</td><td>工行 · 9481</td><td>归还往来款</td><td><span class="status success">双边匹配</span></td><td class="number">−320.00</td></tr><tr data-subject="其他应收"><td>2026.06.27</td><td>转出</td><td>其他应收</td><td>建行 · 0845</td><td>工行 · 9481</td><td>资金调拨</td><td><span class="status warning">单边待核</span></td><td class="number">600.00</td></tr></tbody></table></div>
|
||||
</section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>往来查询</h1>
|
||||
<p class="page-sub">按公司组合查询起算日 2026-01-01 起至统计截止日的逐笔往来余额,支持穿透至银行原始流水链路。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="filters" data-pair-form novalidate>
|
||||
<div class="field">
|
||||
<label for="pairFrom">本方公司</label>
|
||||
<select class="select" name="from" id="pairFrom"></select>
|
||||
</div>
|
||||
<button class="btn" type="button" data-swap aria-label="交换公司" title="交换公司" style="align-self: flex-end;">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="width: 15px; height: 15px;"><path d="M7 8h13l-3.5-3.5M17 16H4l3.5 3.5"/></svg>
|
||||
</button>
|
||||
<div class="field">
|
||||
<label for="pairTo">对方公司</label>
|
||||
<select class="select" name="to" id="pairTo"></select>
|
||||
<span class="hint error" id="pairError" style="display: none;">本方公司与对方公司不能为同一家</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="pairEnd">统计截止日</label>
|
||||
<input class="input" name="end" id="pairEnd" type="date" value="2026-07-31" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="width: 15px; height: 15px;"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.8-3.8"/></svg>
|
||||
查询
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="notice warn" id="pairNotice" style="display: none; margin-bottom: 14px;">
|
||||
<div>
|
||||
<div class="n-title">该公司组合暂无归集数据</div>
|
||||
<div class="n-body">所选组合在正式环境中将按相同口径实时归集,当前仅内置演示数据组合。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pairReport">
|
||||
<div class="row-between" style="margin-bottom: 12px;">
|
||||
<span class="card-title"><span data-pair-from>—</span> ↔ <span data-pair-to>—</span> · 往来余额</span>
|
||||
<span class="meta" id="pairPeriod">统计口径 2026.01.01—2026.07.31</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-4">
|
||||
<div class="card stat-card">
|
||||
<div class="stat-label"><span class="stat-dot info"></span>期初余额(2026-01-01)</div>
|
||||
<div class="stat-value" id="pairOpening">0.00</div>
|
||||
<div class="stat-foot">起算日前结转</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-label"><span class="stat-dot success"></span>本期借方发生</div>
|
||||
<div class="stat-value" id="pairDebit">0.00</div>
|
||||
<div class="stat-foot">应收增加 + 应付偿付</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-label"><span class="stat-dot warn"></span>本期贷方发生</div>
|
||||
<div class="stat-value" id="pairCredit">0.00</div>
|
||||
<div class="stat-foot">收回款项 + 应付计提</div>
|
||||
</div>
|
||||
<div class="card stat-card">
|
||||
<div class="stat-label"><span class="stat-dot success"></span>期末结果</div>
|
||||
<div class="stat-value" id="pairFinal">—</div>
|
||||
<div class="stat-foot"><span id="pairReviewStatus">含 1 笔待审核</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top: 14px;">
|
||||
<div class="tabs" id="subjectTabs">
|
||||
<button type="button" class="active" data-subject-filter="all" aria-pressed="true">全部往来<span class="tab-count" data-subject-total="all">0</span></button>
|
||||
<button type="button" data-subject-filter="应收" aria-pressed="false">应收<span class="tab-count" data-subject-total="应收">0</span></button>
|
||||
<button type="button" data-subject-filter="其他应收" aria-pressed="false">其他应收<span class="tab-count" data-subject-total="其他应收">0</span></button>
|
||||
<button type="button" data-subject-filter="应付" aria-pressed="false">应付<span class="tab-count" data-subject-total="应付">0</span></button>
|
||||
<button type="button" data-subject-filter="其他应付" aria-pressed="false">其他应付<span class="tab-count" data-subject-total="其他应付">0</span></button>
|
||||
</div>
|
||||
<div class="table-wrap" style="border: 0;">
|
||||
<table class="ds-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>交易日期</th>
|
||||
<th>方向</th>
|
||||
<th>科目</th>
|
||||
<th>本方账户</th>
|
||||
<th>对方账户</th>
|
||||
<th class="wrap">摘要</th>
|
||||
<th>匹配</th>
|
||||
<th class="num-col">金额(万元)</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pairTransactions"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-foot" style="margin: 0 -20px -18px; border-radius: 0 0 12px 12px;">
|
||||
<span id="pairFoot">点击「穿透」查看银行原始流水链路</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="audit">
|
||||
<header class="page-heading"><div><h1>审核中心</h1><p>统一处理核算异常、账户登记与公司手工记录</p></div></header>
|
||||
<section class="filter-bar"><div class="segmented" role="group" aria-label="审核类型"><button class="is-active" data-audit-filter="all" data-label="全部" aria-pressed="true">全部 6</button><button data-audit-filter="断档" data-label="流水断档" aria-pressed="false">流水断档 1</button><button data-audit-filter="单边" data-label="单边匹配" aria-pressed="false">单边匹配 1</button><button data-audit-filter="科目" data-label="科目待确认" aria-pressed="false">科目待确认 1</button><button data-audit-filter="起算" data-label="起算校准" aria-pressed="false">起算校准 1</button><button data-audit-filter="账户" data-label="账户登记" aria-pressed="false">账户登记 1</button><button data-audit-filter="手工" data-label="手工记录" aria-pressed="false">手工记录 1</button></div><label class="field compact"><span>公司</span><select id="auditCompany"><option>全部公司</option><option>A公司</option><option>B公司</option><option>E公司</option><option>F公司</option></select></label></section>
|
||||
<section class="panel"><div class="table-scroll"><table class="data-table audit-table"><thead><tr><th>风险</th><th>公司 / 事项</th><th>类型</th><th>涉及期间</th><th>影响</th><th>状态</th><th>操作</th></tr></thead><tbody id="auditRows"><tr data-audit-type="断档" data-company="A公司"><td><span class="task-level danger">高</span></td><td><strong>A公司 · 工商银行 9481</strong><small>上期截止 06.30,下期始于 07.22</small></td><td>流水断档</td><td>07.01—07.21</td><td>7 月结账</td><td><span class="status danger">已逾期</span></td><td><button class="text-button" data-audit-action>审核</button></td></tr><tr data-audit-type="单边" data-company="B公司"><td><span class="task-level warning">中</span></td><td><strong>B公司 ↔ D公司</strong><small>金额与日期存在 2 个候选</small></td><td>单边匹配</td><td>07.18</td><td>312.00 万元</td><td><span class="status warning">待审核</span></td><td><button class="text-button" data-audit-action>匹配</button></td></tr><tr data-audit-type="科目" data-company="E公司"><td><span class="task-level warning">中</span></td><td><strong>E公司 ↔ F公司</strong><small>规则无法区分应收与其他应收</small></td><td>科目待确认</td><td>07.09</td><td>88.00 万元</td><td><span class="status warning">待确认</span></td><td><button class="text-button" data-audit-action>确认</button></td></tr><tr data-audit-type="起算" data-company="F公司"><td><span class="task-level warning">中</span></td><td><strong>F公司 · 农业银行 3650</strong><small>公司已提交无业务说明</small></td><td>起算校准</td><td>01.01—01.16</td><td>期初口径</td><td><span class="status neutral">待复核</span></td><td><button class="text-button" data-audit-action>复核</button></td></tr><tr data-audit-type="手工" data-company="A公司" data-record-kind="manual" data-evidence="公司手工记录、关联银行流水号、证明附件与提交说明"><td><span class="task-level warning">中</span></td><td><strong>A公司 · MR-DEMO-001</strong><small>付款 80,000.00 元 · B公司 · 其他应收</small></td><td>手工记录</td><td>2026.07.29</td><td>待确认往来 8.00 万元</td><td><span class="status warning">待复核</span></td><td><button class="text-button" data-audit-action>复核</button></td></tr></tbody></table></div></section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>审核中心</h1>
|
||||
<p class="page-sub">集中处理流水断档、单边匹配、科目确认等待审事项。审核结论将同步至结账检查与各公司往来台账。</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<a class="btn" data-view-link="reminders">提醒管理</a>
|
||||
<a class="btn btn-primary" data-view-link="settings">查看结账影响</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notice warn">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="width: 18px; height: 18px; flex: none; margin-top: 1px;"><path d="M12 4 3 19h18L12 4z"/><path d="M12 10v4"/><circle cx="12" cy="16.6" r="0.4"/></svg>
|
||||
<div>
|
||||
<div class="n-title">当前 <span id="pending-count">0</span> 项待审核</div>
|
||||
<div class="n-body">全部待审事项须在顺延后的结账日前处理完毕,逾期将阻断 7 月结账。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-top: 18px;">
|
||||
<div class="tabs" id="auditTabs" style="margin-bottom: 0; flex: 1 1 260px; min-width: 0;">
|
||||
<button class="active" data-audit-filter="all" data-label="全部" aria-pressed="true">全部<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="科目" 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="手工" data-label="公司手工记录" aria-pressed="false">公司手工记录<span class="tab-count">0</span></button>
|
||||
</div>
|
||||
<div class="field" style="min-width: 180px;">
|
||||
<label for="auditCompany">公司</label>
|
||||
<select class="select" id="auditCompany"><option>全部公司</option></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap" style="margin-top: 14px;">
|
||||
<table class="ds-table audit-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>风险等级</th>
|
||||
<th>公司 / 事项</th>
|
||||
<th>类型</th>
|
||||
<th>期间</th>
|
||||
<th class="wrap">影响</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="auditRows">
|
||||
<tr data-audit-type="断档" data-company="金牛置业">
|
||||
<td><span class="pill pill-danger">高</span></td>
|
||||
<td><span class="cell-main">金牛置业 · 中行尾号 8821</span><span class="cell-sub">07-06 至 07-16 无流水</span></td>
|
||||
<td>流水断档</td>
|
||||
<td>2026-07</td>
|
||||
<td class="wrap">11 天无流水,与金牛煤业 320 万煤炭采购款往来无法归集,阻断 7 月结账</td>
|
||||
<td><span class="pill pill-info">待审核</span></td>
|
||||
<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div></td>
|
||||
</tr>
|
||||
<tr data-audit-type="单边" data-company="金牛新能源">
|
||||
<td><span class="pill pill-warn">中</span></td>
|
||||
<td><span class="cell-main">金牛新能源 ↔ 金牛贸易</span><span class="cell-sub">3 笔单边合计 486 万</span></td>
|
||||
<td>单边匹配</td>
|
||||
<td>2026-07</td>
|
||||
<td class="wrap">单边支付无对方对应流水,往来差异挂账,待对方选择流水佐证</td>
|
||||
<td><span class="pill pill-info">待审核</span></td>
|
||||
<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div></td>
|
||||
</tr>
|
||||
<tr data-audit-type="科目" data-company="金牛煤业">
|
||||
<td><span class="pill pill-warn">中</span></td>
|
||||
<td><span class="cell-main">金牛煤业 · 手工记录</span><span class="cell-sub">场地押金 42 万</span></td>
|
||||
<td>科目确认</td>
|
||||
<td>2026-07</td>
|
||||
<td class="wrap">「应收 / 其他应收」科目待判定,影响科目汇总口径</td>
|
||||
<td><span class="pill pill-info">待审核</span></td>
|
||||
<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div></td>
|
||||
</tr>
|
||||
<tr data-audit-type="起算" data-company="金牛物流">
|
||||
<td><span class="pill pill-danger">高</span></td>
|
||||
<td><span class="cell-main">金牛物流 · 工行基本户</span><span class="cell-sub">期初与上年结转差 28.6 万</span></td>
|
||||
<td>起算区间校准</td>
|
||||
<td>2026-01 起算</td>
|
||||
<td class="wrap">期初余额与上年结转不衔接,全年累计数失真,需校准起算区间</td>
|
||||
<td><span class="pill pill-info">待审核</span></td>
|
||||
<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div></td>
|
||||
</tr>
|
||||
<tr data-audit-type="手工" data-company="金牛贸易">
|
||||
<td><span class="pill pill-warn">中</span></td>
|
||||
<td><span class="cell-main">金牛贸易 · 手工补录</span><span class="cell-sub">现金缴存 64 万</span></td>
|
||||
<td>公司手工记录</td>
|
||||
<td>2026-07</td>
|
||||
<td class="wrap">无银行回单附件,需人工核实后方可入账</td>
|
||||
<td><span class="pill pill-info">待审核</span></td>
|
||||
<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-foot">
|
||||
<span id="auditFoot">共 0 项 · 待审核 0 项</span>
|
||||
<span>审核人:系统管理员 · 操作实时写入审核日志</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="flows">
|
||||
<header class="page-heading"><div><h1>流水管理</h1><p>查询全部公司的银行原始流水与归集状态</p></div><button class="button secondary" id="exportFlows"><svg><use href="icons.svg#download"/></svg>导出当前结果</button></header>
|
||||
<section class="filter-grid flow-filters"><label class="field"><span>公司</span><select id="flowCompany"><option>全部公司</option><option>A公司</option><option>B公司</option><option>C公司</option></select></label><label class="field"><span>银行</span><select id="flowBank"><option>全部银行</option><option>中信银行</option><option>工商银行</option><option>建设银行</option></select></label><label class="field"><span>账户</span><select id="flowAccount"><option>全部账户</option><option>5316</option><option>9481</option><option>0845</option><option>6120</option></select></label><label class="field"><span>开始日期</span><input id="flowStart" type="date" value="2026-07-01" /></label><label class="field"><span>结束日期</span><input id="flowEnd" type="date" value="2026-07-31" /></label><label class="field grow"><span>摘要 / 对方户名 / 银行流水号</span><input id="flowKeyword" placeholder="输入关键词" /></label><button class="button primary" id="applyFlowFilters"><svg><use href="icons.svg#search"/></svg>查询</button></section>
|
||||
<section class="panel"><div class="table-summary"><span>当前结果 <strong id="flowCount">4</strong> 笔</span><span>导出保留银行、账户、流水号、批次与源行定位</span></div><div class="table-scroll"><table class="data-table" id="flowTable"><thead><tr><th>日期</th><th>公司 / 银行账户</th><th>方向</th><th>对方户名</th><th>摘要</th><th>银行流水号</th><th>归集状态</th><th class="number">金额</th></tr></thead><tbody><tr data-company="A公司" data-bank="中信银行"><td>2026.07.18</td><td><strong>A公司</strong><small>中信银行 · 5316</small></td><td>转出</td><td>B公司</td><td>往来款</td><td>CIT260718018</td><td><span class="status success">双边匹配</span></td><td class="number">10,000,000.00</td></tr><tr data-company="B公司" data-bank="工商银行"><td>2026.07.18</td><td><strong>B公司</strong><small>工商银行 · 9481</small></td><td>转入</td><td>A公司</td><td>往来款</td><td>ICB260718109</td><td><span class="status success">双边匹配</span></td><td class="number">10,000,000.00</td></tr><tr data-company="A公司" data-bank="建设银行"><td>2026.07.12</td><td><strong>A公司</strong><small>建设银行 · 0845</small></td><td>转出</td><td>A公司</td><td>同名账户调拨</td><td>CCB260712031</td><td><span class="status neutral">同公司调拨</span></td><td class="number">2,000,000.00</td></tr><tr data-company="C公司" data-bank="建设银行"><td>2026.07.09</td><td><strong>C公司</strong><small>建设银行 · 6120</small></td><td>转出</td><td>E公司</td><td>资金调拨</td><td>CCB260709088</td><td><span class="status warning">单边待核</span></td><td class="number">880,000.00</td></tr></tbody></table></div></section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>流水管理</h1>
|
||||
<p class="page-sub">六家成员公司上报的银行原始流水总表,含归集状态。点击任意一行可穿透查看原始流水要素与归集链路。</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-primary" id="exportFlows">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="width: 15px; height: 15px;"><path d="M12 4v11m0 0l-4-4m4 4l4-4"/><path d="M4 17v2.5A1.5 1.5 0 0 0 5.5 21h13a1.5 1.5 0 0 0 1.5-1.5V17"/></svg>
|
||||
导出 CSV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<div class="field">
|
||||
<label for="flowCompany">公司</label>
|
||||
<select class="select" id="flowCompany"><option>全部公司</option></select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="flowBank">银行</label>
|
||||
<select class="select" id="flowBank"><option>全部银行</option><option>中行</option><option>工行</option><option>建行</option><option>农行</option><option>交行</option></select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="flowAccount">账户</label>
|
||||
<select class="select" id="flowAccount"><option>全部账户</option><option>3305</option><option>9916</option><option>7710</option><option>5562</option><option>2208</option><option>8821</option><option>6649</option></select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="flowStart">日期起</label>
|
||||
<input class="input" id="flowStart" type="date" value="2026-07-01" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="flowEnd">日期止</label>
|
||||
<input class="input" id="flowEnd" type="date" value="2026-07-31" />
|
||||
</div>
|
||||
<div class="field" style="min-width: 200px;">
|
||||
<label for="flowKeyword">关键词</label>
|
||||
<input class="input" id="flowKeyword" type="text" placeholder="摘要 / 对方 / 流水号" />
|
||||
</div>
|
||||
<button class="btn" id="applyFlowFilters">查询</button>
|
||||
<button class="btn btn-ghost" id="resetFlowFilters">重置</button>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table class="ds-table" id="flowTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日期</th>
|
||||
<th>公司</th>
|
||||
<th>银行账户</th>
|
||||
<th>方向</th>
|
||||
<th class="wrap">对方</th>
|
||||
<th class="wrap">摘要</th>
|
||||
<th>银行流水号</th>
|
||||
<th>归集状态</th>
|
||||
<th class="num-col">金额</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<div class="table-foot">
|
||||
<span id="flowCount">共 0 笔</span>
|
||||
<span class="meta">数据范围:2026-01-01 起算 · 每月 5 日结账(7 月顺延至 08-29)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="companies">
|
||||
<header class="page-heading"><div><h1>公司与账号</h1><p>维护公司主档、银行账户和一对一公司账号</p></div><button class="button primary" id="openCompanyDialog"><svg><use href="icons.svg#plus"/></svg>新增公司</button></header>
|
||||
<section class="panel"><div class="table-scroll"><table class="data-table" id="companyTable"><thead><tr><th>公司全称</th><th>统一社会信用代码</th><th>银行账户</th><th>公司账号</th><th>出纳人员</th><th>状态</th><th>操作</th></tr></thead><tbody></tbody></table></div></section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>公司与账号</h1>
|
||||
<p class="page-sub">集团成员公司的档案、银行账户与登录账号管理。公司新登记的银行账户须经审核中心确认后方可参与流水归集。</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-primary" id="openCompanyDialog">新增公司</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notice info" id="notice-pending-account" style="margin-bottom: 14px; display: none;">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--info);"><circle cx="12" cy="12" r="8.5"/><path d="M12 8v5M12 16h.01"/></svg>
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div class="n-title"><span id="pending-account-count">0</span> 个新账户登记待审核</div>
|
||||
<div class="n-body" id="pending-account-body">成员公司提交的新银行账户登记,等待审核通过后纳入账期流水归集范围。</div>
|
||||
</div>
|
||||
<a class="btn btn-sm" data-view-link="audit" style="flex: none;">去审核</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">成员公司<span class="sub">数据截至 2026-08-20</span></span>
|
||||
</div>
|
||||
<div class="table-wrap" style="border: 0;">
|
||||
<table class="ds-table" id="companyTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>公司名称</th>
|
||||
<th class="num-col">银行账户数</th>
|
||||
<th>登录账号</th>
|
||||
<th>出纳</th>
|
||||
<th>启用状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-foot" style="border: 1px solid var(--border); border-radius: 0 0 var(--radius-lg) var(--radius-lg);">
|
||||
<span id="companyFoot">共 0 家公司</span>
|
||||
<span class="meta">登录账号初始密码由集团总账统一发放</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="settings">
|
||||
<header class="page-heading"><div><h1>结账与期初</h1><p>统一管理计算窗口、月结规则和公司期初余额</p></div></header>
|
||||
<section class="settings-layout">
|
||||
<form class="panel settings-form" id="systemSettings"><div class="panel-heading"><div><h2>系统计算口径</h2><p>修改后影响全部公司</p></div></div><div class="form-body"><label class="field"><span>全局起算日</span><input name="startDate" type="date" value="2026-01-01" /><small>早于此日的流水保留但不参与计算</small></label><label class="field"><span>每月结账日</span><span class="input-suffix"><input name="closingDay" type="number" min="1" max="28" value="5" /><b>次月日</b></span></label><label class="check-field"><input type="checkbox" checked />结账前 3 天自动提醒未完成公司</label></div><footer><button class="button primary" type="submit">保存系统口径</button></footer></form>
|
||||
<section class="panel opening-panel"><div class="panel-heading"><div><h2>公司期初余额</h2><p>按公司对录入,修改保留审计记录</p></div><button class="button secondary small" id="openOpeningDialog"><svg><use href="icons.svg#plus"/></svg>新增</button></div><div class="table-scroll"><table class="data-table"><thead><tr><th>本方</th><th>对方</th><th>科目</th><th>方向</th><th class="number">金额(万元)</th><th>生效日</th><th>状态</th></tr></thead><tbody id="openingRows"><tr><td>A公司</td><td>B公司</td><td>应收</td><td>借方</td><td class="number">0.00</td><td>2026.01.01</td><td><span class="status success">已确认</span></td></tr><tr><td>C公司</td><td>E公司</td><td>其他应收</td><td>借方</td><td class="number">120.00</td><td>2026.01.01</td><td><span class="status warning">待复核</span></td></tr></tbody></table></div></section>
|
||||
<section class="panel closing-panel" id="closingPanel"><div class="panel-heading"><div><h2>月度结账</h2><p id="closingDescription">2026 年 7 月 · 当前未达到结账条件</p></div><span class="status danger" id="closingStatus">已阻断</span></div><div class="closing-checks"><article><svg><use href="icons.svg#circle-check"/></svg><span><strong>公司流水提交</strong><small>5 / 6 家已完成</small></span></article><article class="is-blocked"><svg><use href="icons.svg#circle-alert"/></svg><span><strong>账户连续性</strong><small>2 个账户存在断档</small></span></article><article class="is-blocked"><svg><use href="icons.svg#circle-alert"/></svg><span><strong>审核事项</strong><small>7 项尚未处理</small></span></article><article><svg><use href="icons.svg#circle-check"/></svg><span><strong>期初余额</strong><small>已锁定 2026.01.01</small></span></article></div><footer class="closing-footer"><span id="closingHistory">最近结账:2026 年 6 月 · 系统管理员 · 2026.07.05 18:10</span><button class="button secondary" id="runClosingCheck">重新检查</button><button class="button primary" id="executeClosing" disabled title="请先处理全部阻断事项">执行 7 月结账</button></footer></section>
|
||||
</section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>结账与期初</h1>
|
||||
<p class="page-sub">维护全局结账参数与各公司期初往来余额,并在月度检查全部通过后执行结账。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">账期时间轴<span class="sub">全局起算日 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>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-1-2" style="margin-top: 14px;">
|
||||
<div class="stack">
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">结账设置<span class="sub">对全部成员公司生效</span></span>
|
||||
</div>
|
||||
<form id="systemSettings" novalidate>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="cs-start">全局起算日</label>
|
||||
<input class="input num-input" type="date" name="startDate" id="cs-start" value="2026-01-01" />
|
||||
<span class="hint">期初余额以此日前一日的期末数为准</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="cs-day">每月结账日</label>
|
||||
<input class="input num-input" type="number" name="closingDay" id="cs-day" min="1" max="28" value="5" />
|
||||
<span class="hint">每月几号执行上月结账 · 2026-07 因故顺延至 08-29</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 16px;">
|
||||
<label>自动提醒</label>
|
||||
<div class="row" style="flex-wrap: wrap;">
|
||||
<label style="display: inline-flex; align-items: center; gap: 8px; cursor: pointer;">
|
||||
<input type="checkbox" id="cs-remind" checked style="position: absolute; opacity: 0; width: 0; height: 0;" />
|
||||
<span id="cs-switch-track" style="width: 36px; height: 20px; border-radius: 999px; background: var(--accent); position: relative; flex: none; transition: background .15s ease;">
|
||||
<span id="cs-switch-thumb" style="position: absolute; top: 2px; left: 18px; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); transition: left .15s ease;"></span>
|
||||
</span>
|
||||
<span style="font-size: 13px;">结账日前自动提醒各公司出纳</span>
|
||||
</label>
|
||||
<select class="select" id="cs-remind-days" style="width: 110px;">
|
||||
<option value="1">提前 1 天</option>
|
||||
<option value="3" selected>提前 3 天</option>
|
||||
<option value="5">提前 5 天</option>
|
||||
</select>
|
||||
</div>
|
||||
<span class="hint">提醒对象为当月尚未提交流水的公司联系人</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="btn btn-primary" type="submit">保存设置</button>
|
||||
<span class="meta" id="cs-save-tip" style="display: none; color: var(--success);">已保存 · 立即生效</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">期初余额管理<span class="sub">2026-01-01 起算的公司间往来期初数</span></span>
|
||||
<button class="btn btn-sm" id="openOpeningDialog">新增</button>
|
||||
</div>
|
||||
<div class="table-wrap" style="border: 0;">
|
||||
<table class="ds-table">
|
||||
<thead>
|
||||
<tr><th>本方公司</th><th>对方公司</th><th>科目</th><th>方向</th><th class="num-col">金额(万元)</th><th>生效日</th><th>状态</th></tr>
|
||||
</thead>
|
||||
<tbody id="openingRows"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
</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-body" id="block-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>
|
||||
</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>
|
||||
<button class="btn" id="runClosingCheck">重新检查</button>
|
||||
<button class="btn btn-primary" id="executeClosing" disabled title="请先处理全部阻断事项">执行 2026-07 月度结账</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="app-view" data-page="reminders">
|
||||
<header class="page-heading"><div><h1>提醒管理</h1><p>查看自动提醒,并向公司出纳发送站内提醒</p></div></header>
|
||||
<div class="reminder-layout">
|
||||
<form class="panel reminder-form" id="reminderForm"><div class="panel-heading"><div><h2>发送手动提醒</h2><p>当前仅支持系统内通知</p></div></div><div class="form-body"><label class="field"><span>接收公司</span><select name="company" required><option value="">请选择公司</option><option>A公司</option><option>B公司</option><option>C公司</option></select></label><label class="field"><span>处理期限</span><input name="dueDate" type="date" value="2026-08-08" required /></label><label class="field"><span>提醒内容</span><textarea name="message" rows="4" placeholder="请填写需要处理的事项" required></textarea></label></div><footer><button class="button primary" type="submit"><svg><use href="icons.svg#send"/></svg>发送提醒</button></footer></form>
|
||||
<section class="panel"><div class="panel-heading"><div><h2>提醒记录</h2><p>自动与手动提醒统一留痕</p></div></div><div class="notification-list" id="adminReminderList"><article><span class="notification-icon danger"><svg><use href="icons.svg#circle-alert"/></svg></span><span><strong>A公司 7 月流水缺失</strong><small>自动提醒 · 截止 08.05 · 已逾期</small></span><em class="status danger">未处理</em></article><article><span class="notification-icon warning"><svg><use href="icons.svg#bell"/></svg></span><span><strong>B公司单边流水待核验</strong><small>手动提醒 · 管理员 · 今天 09:30</small></span><em class="status warning">处理中</em></article><article><span class="notification-icon success"><svg><use href="icons.svg#circle-check"/></svg></span><span><strong>C公司 7 月流水已确认</strong><small>自动提醒 · 昨天 16:42</small></span><em class="status success">已完成</em></article></div></section>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>提醒管理</h1>
|
||||
<p class="page-sub">向成员公司发送处理提醒,并跟踪系统提醒与人工提醒的触达与处理状态。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-1-2">
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">发送处理提醒<span class="sub">将以人工提醒形式送达所选公司出纳与财务负责人</span></span>
|
||||
</div>
|
||||
<form id="reminderForm" novalidate>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label>接收公司(可多选)</label>
|
||||
<div class="row" id="reminderCompanyList" style="flex-wrap: wrap; gap: 8px 16px;"></div>
|
||||
<span class="hint error" id="company-error" style="display: none;">请至少选择一家接收公司</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="reminder-type">提醒类型</label>
|
||||
<select class="select" id="reminder-type">
|
||||
<option selected>流水未提交</option>
|
||||
<option>单边待确认</option>
|
||||
<option>科目待确认</option>
|
||||
<option>账户登记</option>
|
||||
<option>其他</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="reminder-content">提醒内容</label>
|
||||
<textarea class="textarea" id="reminder-content">请于截止日期前完成 2026 年 7 月银行流水上传与待确认事项处理。</textarea>
|
||||
<span class="hint error" id="content-error" style="display: none;">提醒内容不能为空</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 18px;">
|
||||
<label for="reminder-deadline">截止日期</label>
|
||||
<input class="input" type="date" id="reminder-deadline" value="2026-08-29" min="2026-08-20" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="send-btn" style="width: 100%;">发送提醒</button>
|
||||
<p class="hint" id="send-hint" style="margin-top: 10px; display: none;"></p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">提醒历史<span class="sub">含系统自动触发与人工发送的全部提醒记录</span></span>
|
||||
</div>
|
||||
<div class="tabs" id="reminder-tabs">
|
||||
<button type="button" class="active" data-filter="all">全部<span class="tab-count" id="count-all">0</span></button>
|
||||
<button type="button" data-filter="system">系统提醒<span class="tab-count" id="count-system">0</span></button>
|
||||
<button type="button" data-filter="manual">人工提醒<span class="tab-count" id="count-manual">0</span></button>
|
||||
</div>
|
||||
<div class="table-wrap" style="border: 0;">
|
||||
<table class="ds-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>公司</th>
|
||||
<th>类型</th>
|
||||
<th class="wrap">内容摘要</th>
|
||||
<th>发送时间</th>
|
||||
<th>截止日期</th>
|
||||
<th>处理状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reminder-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-foot">
|
||||
<span id="table-foot-count">共 0 条提醒记录</span>
|
||||
<span id="table-foot-state">未读 0 · 处理中 0 · 已完成 0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dialog id="companyDialog" class="dialog">
|
||||
<form method="dialog" id="companyForm">
|
||||
<header><div><h2>新增公司</h2><p>公司主档与公司账号可一次创建</p></div><button class="icon-button" value="cancel" aria-label="关闭" title="关闭"><svg><use href="icons.svg#x"/></svg></button></header>
|
||||
<div class="dialog-body"><label class="field"><span>公司全称</span><input name="companyName" required /></label><label class="field"><span>统一社会信用代码</span><input name="creditCode" /></label><label class="check-field"><input type="checkbox" name="createUser" checked />同时创建公司账号</label><div class="form-grid"><label class="field"><span>登录账号</span><input name="loginName" required /></label><label class="field"><span>出纳人员</span><input name="cashier" required /></label></div><p class="form-callout"><svg><use href="icons.svg#key-round"/></svg>创建后生成随机初始密码,仅显示一次,首次登录必须修改。</p></div>
|
||||
<footer><button class="button secondary" value="cancel">取消</button><button class="button primary" value="default">创建公司与账号</button></footer>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="openingDialog" class="dialog">
|
||||
<form id="openingForm">
|
||||
<header><div><h2>新增期初余额</h2><p>按本方视角记录公司间期初方向</p></div><button type="button" class="icon-button" data-close-opening aria-label="关闭" title="关闭"><svg><use href="icons.svg#x"/></svg></button></header>
|
||||
<div class="dialog-body"><div class="form-grid"><label class="field"><span>本方公司</span><select name="from" required><option>A公司</option><option>B公司</option><option>C公司</option></select></label><label class="field"><span>对方公司</span><select name="to" required><option>B公司</option><option>C公司</option><option>D公司</option></select></label><label class="field"><span>科目</span><select name="subject" required><option>应收</option><option>应付</option><option>其他应收</option><option>其他应付</option></select></label><label class="field"><span>方向</span><select name="direction" required><option>借方</option><option>贷方</option></select></label><label class="field"><span>金额(万元)</span><input name="amount" type="number" min="0" step="0.01" required /></label><label class="field"><span>生效日</span><input name="effectiveDate" type="date" value="2026-01-01" required /></label></div><p class="form-callout"><svg><use href="icons.svg#circle-alert"/></svg>提交后进入复核,不能直接修改已确认期初。</p></div>
|
||||
<footer><button type="button" class="button secondary" data-close-opening>取消</button><button class="button primary" type="submit">提交复核</button></footer>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="auditDialog" class="dialog">
|
||||
<form id="auditForm">
|
||||
<header><div><h2 id="auditDialogTitle">审核事项</h2><p id="auditDialogMeta">原始证据与处理决定将一并留痕</p></div><button type="button" class="icon-button" data-close-audit aria-label="关闭" title="关闭"><svg><use href="icons.svg#x"/></svg></button></header>
|
||||
<div class="dialog-body"><div class="evidence-block" id="auditEvidence"></div><label class="field"><span>处理决定</span><select name="decision" required><option value="">请选择</option><option>确认并纳入计算</option><option>退回公司补充材料</option><option>转为异常待后续处理</option></select></label><label class="field"><span>处理依据</span><textarea name="reason" rows="3" required placeholder="填写核验账号、摘要、回单或说明"></textarea></label></div>
|
||||
<footer><button type="button" class="button secondary" data-close-audit>取消</button><button class="button primary" type="submit">提交审核结果</button></footer>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="closingDialog" class="dialog">
|
||||
<form id="closingForm">
|
||||
<header><div><h2>执行 2026 年 7 月结账</h2><p>结账后本期结果将锁定,补录流水需走重开流程</p></div><button type="button" class="icon-button" data-close-closing aria-label="关闭" title="关闭"><svg><use href="icons.svg#x"/></svg></button></header>
|
||||
<div class="dialog-body"><div class="evidence-block"><strong>结账检查已通过</strong><small>公司提交、账户连续性、审核事项和期初余额均符合当前口径。</small></div><label class="check-field"><input type="checkbox" name="confirm" required />我已复核 2026 年 7 月结账结果</label></div>
|
||||
<footer><button type="button" class="button secondary" data-close-closing>取消</button><button class="button primary" type="submit">确认执行结账</button></footer>
|
||||
</form>
|
||||
</dialog>
|
||||
<!-- 新增公司弹窗 -->
|
||||
<div class="modal-backdrop" id="companyDialog">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">新增成员公司</span>
|
||||
<button type="button" class="modal-close" data-close="companyDialog" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">登记后系统将生成公司端登录账号,初始密码由集团总账统一发放。</p>
|
||||
<form id="companyForm" novalidate>
|
||||
<div class="field" style="margin-bottom: 12px;">
|
||||
<label for="f-name">公司全称</label>
|
||||
<input class="input" id="f-name" name="companyName" type="text" placeholder="如:河南金牛XX有限公司" required />
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 12px;">
|
||||
<label for="f-code">统一社会信用代码</label>
|
||||
<input class="input num-input" id="f-code" name="creditCode" type="text" maxlength="18" placeholder="18 位,如 91410100MA9K2X417A" />
|
||||
</div>
|
||||
<label class="row" style="gap: 6px; font-size: 13px; margin-bottom: 12px; cursor: pointer;">
|
||||
<input type="checkbox" name="createUser" checked style="width: auto;" />同时创建公司账号
|
||||
</label>
|
||||
<div class="grid grid-2">
|
||||
<div class="field">
|
||||
<label for="f-login">登录账号</label>
|
||||
<input class="input" id="f-login" name="loginName" type="text" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="f-cashier">出纳人员</label>
|
||||
<input class="input" id="f-cashier" name="cashier" type="text" required />
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top: 10px;">创建后生成随机初始密码,仅显示一次,首次登录必须修改。</p>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close="companyDialog">取消</button>
|
||||
<button type="submit" class="btn btn-primary">创建公司与账号</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 公司详情弹窗 -->
|
||||
<div class="modal-backdrop" id="modal-detail">
|
||||
<div class="modal wide">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title" id="d-title">公司详情</span>
|
||||
<button type="button" class="modal-close" data-close="modal-detail" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub" id="d-sub"></p>
|
||||
<div class="detail-box" style="margin-bottom: 14px;">
|
||||
<dl class="kv" id="d-kv"></dl>
|
||||
</div>
|
||||
<div class="card-head" style="margin-bottom: 10px;">
|
||||
<span class="card-title">登录账号</span>
|
||||
</div>
|
||||
<div class="detail-box">
|
||||
<div class="row-between">
|
||||
<div>
|
||||
<div class="num" id="d-login-account" style="font-weight: 650;"></div>
|
||||
<div class="meta" id="d-login-meta" style="margin-top: 3px;"></div>
|
||||
</div>
|
||||
<button class="btn btn-sm" id="btn-reset-pwd">重置密码</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增期初余额弹窗 -->
|
||||
<div class="modal-backdrop" id="openingDialog">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">新增期初余额</span>
|
||||
<button type="button" class="modal-close" data-close-opening aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">按本方视角记录公司间期初方向,提交后进入复核。</p>
|
||||
<form id="openingForm" novalidate>
|
||||
<div class="grid grid-2">
|
||||
<div class="field">
|
||||
<label for="ob-from">本方公司</label>
|
||||
<select class="select" name="from" id="ob-from" required></select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="ob-to">对方公司</label>
|
||||
<select class="select" name="to" id="ob-to" required></select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="ob-subject">科目</label>
|
||||
<select class="select" name="subject" id="ob-subject" required>
|
||||
<option>应收</option>
|
||||
<option>应付</option>
|
||||
<option>其他应收</option>
|
||||
<option>其他应付</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="ob-direction">方向</label>
|
||||
<select class="select" name="direction" id="ob-direction" required>
|
||||
<option>借方</option>
|
||||
<option>贷方</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="ob-amount">金额(万元)</label>
|
||||
<input class="input num-input" name="amount" id="ob-amount" type="number" min="0" step="0.01" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="ob-effective">生效日</label>
|
||||
<input class="input" name="effectiveDate" id="ob-effective" type="date" value="2026-01-01" required />
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top: 10px;">提交后进入复核,不能直接修改已确认期初。</p>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close-opening>取消</button>
|
||||
<button type="submit" class="btn btn-primary">提交复核</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审核通过弹窗 -->
|
||||
<div class="modal-backdrop" id="modal-approve">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">确认通过</span>
|
||||
<button type="button" class="modal-close" data-close="modal-approve">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">通过后该事项将标记为「已通过」并写入审核日志,不可撤销。</p>
|
||||
<div class="detail-box">
|
||||
<dl class="kv">
|
||||
<dt>公司 / 事项</dt><dd id="approve-company">—</dd>
|
||||
<dt>期间</dt><dd id="approve-period">—</dd>
|
||||
<dt>类型</dt><dd id="approve-type">—</dd>
|
||||
<dt>审核依据</dt><dd id="approve-basis" style="font-family: var(--font-body);">—</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close="modal-approve">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="approve-confirm">确认通过</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审核驳回弹窗 -->
|
||||
<div class="modal-backdrop" id="modal-reject">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">驳回事项</span>
|
||||
<button type="button" class="modal-close" data-close="modal-reject">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">驳回后系统将自动向该公司发送整改通知,请填写明确的驳回原因。</p>
|
||||
<div class="field">
|
||||
<label for="reject-reason">驳回原因</label>
|
||||
<textarea class="textarea" id="reject-reason" placeholder="例如:补传的银行回单金额与流水不一致,请重新核对后再提交。"></textarea>
|
||||
<span class="hint error" id="reject-hint" style="display: none;">请填写驳回原因(不少于 5 个字)</span>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close="modal-reject">取消</button>
|
||||
<button type="button" class="btn btn-danger" id="reject-confirm">确认驳回</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结账确认弹窗 -->
|
||||
<div class="modal-backdrop" id="closingDialog">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">确认执行 2026-07 月度结账</span>
|
||||
<button type="button" class="modal-close" data-close-closing aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub">结账后成员公司的 7 月流水与往来确认将锁定,不可再修改。</p>
|
||||
<form id="closingForm" novalidate>
|
||||
<div class="detail-box">
|
||||
<dl class="kv">
|
||||
<dt>结账账期</dt><dd>2026-07</dd>
|
||||
<dt>检查通过</dt><dd>全部前置检查已通过</dd>
|
||||
<dt>期末归集</dt><dd>往来净额将结转至 2026-08 期初</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 月结账结果
|
||||
</label>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" data-close-closing>再想想</button>
|
||||
<button type="submit" class="btn btn-primary">确认结账</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 往来穿透弹窗 -->
|
||||
<div class="modal-backdrop" id="traceModal">
|
||||
<div class="modal wide">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">往来穿透 · 银行原始流水链路</span>
|
||||
<button type="button" class="modal-close" id="traceClose" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub" id="traceSub">—</p>
|
||||
<div class="detail-box">
|
||||
<dl class="kv">
|
||||
<dt>本方银行流水号</dt><dd id="kvOwnTx">—</dd>
|
||||
<dt>本方账户</dt><dd id="kvOwnAcct">—</dd>
|
||||
<dt>对方公司</dt><dd id="kvPeerCo">—</dd>
|
||||
<dt>对方账户</dt><dd id="kvPeerAcct">—</dd>
|
||||
<dt>交易时间</dt><dd id="kvTime">—</dd>
|
||||
<dt>金额</dt><dd id="kvAmt">—</dd>
|
||||
<dt>归集批次号</dt><dd id="kvBatch">—</dd>
|
||||
<dt>对方证据流水号</dt><dd id="kvEvidence">—</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" id="traceOk">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 流水穿透详情弹窗 -->
|
||||
<div class="modal-backdrop" id="tx-modal">
|
||||
<div class="modal wide">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title">流水穿透详情</span>
|
||||
<button type="button" class="modal-close" id="tx-modal-close" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub" id="tx-modal-sub"></p>
|
||||
<div class="detail-box">
|
||||
<div class="card-title" style="margin-bottom: 10px;">原始流水</div>
|
||||
<dl class="kv">
|
||||
<dt>银行流水号</dt><dd id="d-serial"></dd>
|
||||
<dt>银行</dt><dd id="d-bank"></dd>
|
||||
<dt>账户</dt><dd id="d-account"></dd>
|
||||
<dt>交易时间</dt><dd id="d-time"></dd>
|
||||
<dt>对方户名</dt><dd id="d-peer"></dd>
|
||||
<dt>对方账号</dt><dd id="d-peer-acct"></dd>
|
||||
<dt>金额</dt><dd id="d-amount"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="detail-box" style="margin-top: 12px;">
|
||||
<div class="card-title" style="margin-bottom: 10px;">归集链路</div>
|
||||
<dl class="kv">
|
||||
<dt>归集状态</dt><dd id="d-status"></dd>
|
||||
<dt>归集往来</dt><dd id="d-pair"></dd>
|
||||
<dt>本方科目</dt><dd id="d-subject"></dd>
|
||||
<dt>归集批次</dt><dd id="d-batch"></dd>
|
||||
<dt>说明</dt><dd id="d-note" style="font-family: var(--font-body);"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" id="tx-modal-ok">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提醒历史弹窗 -->
|
||||
<div class="modal-backdrop" id="history-modal">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<span class="modal-title" id="history-title">提醒历史</span>
|
||||
<button type="button" class="modal-close" id="history-close" aria-label="关闭">×</button>
|
||||
</div>
|
||||
<p class="modal-sub" id="history-sub"></p>
|
||||
<div id="history-list"></div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" id="history-ok">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast-region" id="toastRegion" aria-live="polite"></div>
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
|
||||
+511
-164
@@ -126,6 +126,10 @@ function accountStatusLabel(status) {
|
||||
return accountStatusLabels[status] || "待复核";
|
||||
}
|
||||
|
||||
function pillClass(statusClass) {
|
||||
return { success: "pill-success", danger: "pill-danger", warning: "pill-warn", neutral: "pill-muted", info: "pill-info" }[statusClass] || "pill-muted";
|
||||
}
|
||||
|
||||
function accountTail(masked) {
|
||||
return String(masked || "").replace(/^\*+/, "");
|
||||
}
|
||||
@@ -541,14 +545,16 @@ function pairData(from, to, endDate) {
|
||||
};
|
||||
}
|
||||
|
||||
const pairSubjectOrder = ["应收", "其他应收", "应付", "其他应付"];
|
||||
|
||||
function setPair(from, to, endDate = "2026-07-31") {
|
||||
$$('[data-pair-from]').forEach((item) => { item.textContent = from; });
|
||||
$$('[data-pair-to]').forEach((item) => { item.textContent = to; });
|
||||
$$("[data-pair-form]").forEach((form) => {
|
||||
const fromSelect = $('[name="from"]', form);
|
||||
const toSelect = $('[name="to"]', form);
|
||||
if ([...fromSelect.options].some((option) => option.value === from)) fromSelect.value = from;
|
||||
if ([...toSelect.options].some((option) => option.value === to)) toSelect.value = to;
|
||||
if (fromSelect && [...fromSelect.options].some((option) => option.value === from)) fromSelect.value = from;
|
||||
if (toSelect && [...toSelect.options].some((option) => option.value === to)) toSelect.value = to;
|
||||
const endInput = $('[name="end"]', form);
|
||||
if (endInput) endInput.value = endDate;
|
||||
});
|
||||
@@ -556,20 +562,56 @@ function setPair(from, to, endDate = "2026-07-31") {
|
||||
const data = pairData(from, to, endDate);
|
||||
const format = (value) => value.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
$("#pairPeriod").textContent = `统计口径 2026.01.01—${endDate}`;
|
||||
$("#pairOpening").textContent = format(data.opening);
|
||||
$("#pairDebit").textContent = format(data.debit);
|
||||
$("#pairCredit").textContent = format(data.credit);
|
||||
$("#pairFinal").innerHTML = `${data.final >= 0 ? "应收" : "应付"} ${format(Math.abs(data.final))}<small>万元</small>`;
|
||||
$("#pairOpening").innerHTML = `${format(data.opening)}<span class="unit">万元</span>`;
|
||||
$("#pairDebit").innerHTML = `${format(data.debit)}<span class="unit">万元</span>`;
|
||||
$("#pairCredit").innerHTML = `${format(data.credit)}<span class="unit">万元</span>`;
|
||||
$("#pairFinal").innerHTML = `${data.final >= 0 ? "应收" : "应付"} ${format(Math.abs(data.final))}<span class="unit">万元</span>`;
|
||||
$("#pairReviewStatus").textContent = "含 1 笔待审核";
|
||||
const quickResult = $(".quick-result");
|
||||
if (quickResult) {
|
||||
$("span", quickResult).textContent = `${from}对${to}`;
|
||||
$("strong", quickResult).innerHTML = `${data.final >= 0 ? "应收" : "应付"} ${format(Math.abs(data.final))}<small>万元</small>`;
|
||||
$("p", quickResult).textContent = `${data.rows.length - 1} 笔已确认 · 1 笔待审核`;
|
||||
}
|
||||
$('[data-subject-total="all"]').textContent = `${data.rows.length} 笔`;
|
||||
Object.entries(data.totals).forEach(([subject, value]) => { $(`[data-subject-total="${subject}"]`).textContent = format(value); });
|
||||
$("#pairTransactions tbody").innerHTML = data.rows.map(([date, direction, subject, ownAccount, counterparty, summary, match, amount]) => `<tr data-subject="${subject}"><td>${date}</td><td>${direction}</td><td>${subject}</td><td>${ownAccount}</td><td>${counterparty}</td><td>${summary}</td><td><span class="status ${match === "双边匹配" ? "success" : "warning"}">${match}</span></td><td class="number">${format(amount)}</td></tr>`).join("");
|
||||
Object.entries(data.totals).forEach(([subject, value]) => { $(`[data-subject-total="${subject}"]`)?.replaceChildren(document.createTextNode(format(value))); });
|
||||
state.pairContext = { from, to, endDate };
|
||||
state.pairRows = data.rows.map(([date, direction, subject, ownAccount, counterparty, summary, match, amount]) => ({ date, direction, subject, ownAccount, counterparty, summary, match, amount }));
|
||||
renderPairRows();
|
||||
}
|
||||
|
||||
function renderPairRows() {
|
||||
const tbody = $("#pairTransactions");
|
||||
if (!tbody) return;
|
||||
const format = (value) => Number(value).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
tbody.innerHTML = state.pairRows.map((row, index) => {
|
||||
const matched = row.match === "双边匹配";
|
||||
return `<tr data-subject="${row.subject}" data-pair-idx="${index}">
|
||||
<td class="num">${row.date}</td>
|
||||
<td>${row.direction}</td>
|
||||
<td>${row.subject}</td>
|
||||
<td>${row.ownAccount}</td>
|
||||
<td>${row.counterparty}</td>
|
||||
<td class="wrap">${row.summary}</td>
|
||||
<td><span class="pill ${matched ? "pill-success" : "pill-warn"}">${row.match}</span></td>
|
||||
<td class="num-col ${row.direction === "转出" ? "amt-out" : "amt-in"}">${format(row.amount)}</td>
|
||||
<td><button type="button" class="btn btn-sm" data-trace="${index}">穿透</button></td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function openTrace(index) {
|
||||
const modal = $("#traceModal");
|
||||
if (!modal) return;
|
||||
const row = state.pairRows?.[index];
|
||||
if (!row) return;
|
||||
const ctx = state.pairContext || { from: "—", to: "—" };
|
||||
$("#traceSub").textContent = `${ctx.from} ↔ ${ctx.to} · ${row.subject} · ${row.date} · ${row.direction} ${formatCurrency(row.amount)} 万元`;
|
||||
$("#kvOwnTx").textContent = "—(演示数据,未关联银行流水)";
|
||||
$("#kvOwnAcct").textContent = row.ownAccount;
|
||||
$("#kvPeerCo").textContent = ctx.to;
|
||||
$("#kvPeerAcct").textContent = row.counterparty;
|
||||
$("#kvTime").textContent = row.date;
|
||||
$("#kvAmt").textContent = `${formatCurrency(row.amount)} 万元`;
|
||||
$("#kvBatch").textContent = "—";
|
||||
const evidence = $("#kvEvidence");
|
||||
if (row.match === "双边匹配") { evidence.textContent = "—"; evidence.style.color = ""; }
|
||||
else { evidence.textContent = "待对方提供"; evidence.style.color = "var(--warn)"; }
|
||||
modal.classList.add("open");
|
||||
}
|
||||
|
||||
function initPairQueries() {
|
||||
@@ -586,13 +628,17 @@ function initPairQueries() {
|
||||
const from = $('[name="from"]', form).value;
|
||||
const to = $('[name="to"]', form).value;
|
||||
const endDate = $('[name="end"]', form)?.value || "2026-07-31";
|
||||
const error = $("#pairError");
|
||||
if (from === to) {
|
||||
if (error) { error.style.display = ""; }
|
||||
showToast("请选择两个不同的公司", "同公司账户调拨不进入公司间往来查询");
|
||||
return;
|
||||
}
|
||||
if (error) error.style.display = "none";
|
||||
const notice = $("#pairNotice");
|
||||
if (notice) notice.style.display = "none";
|
||||
setPair(from, to, endDate);
|
||||
showView("pair");
|
||||
showToast("查询结果已更新", `${from} 与 ${to} · 截至 ${endDate}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -606,11 +652,19 @@ function initPairQueries() {
|
||||
const subject = button.dataset.subjectFilter;
|
||||
$$("[data-subject-filter]").forEach((item) => {
|
||||
const active = item === button;
|
||||
item.classList.toggle("is-active", active);
|
||||
item.classList.toggle("active", active);
|
||||
item.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
$$("#pairTransactions tbody tr").forEach((row) => { row.hidden = subject !== "all" && row.dataset.subject !== subject; });
|
||||
$$("#pairTransactions tr").forEach((row) => { row.hidden = subject !== "all" && row.dataset.subject !== subject; });
|
||||
}));
|
||||
|
||||
$("#pairTransactions")?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-trace]");
|
||||
if (button) openTrace(Number(button.dataset.trace));
|
||||
});
|
||||
$("#traceClose")?.addEventListener("click", () => $("#traceModal")?.classList.remove("open"));
|
||||
$("#traceOk")?.addEventListener("click", () => $("#traceModal")?.classList.remove("open"));
|
||||
$("#traceModal")?.addEventListener("click", (event) => { if (event.target === event.currentTarget) event.currentTarget.classList.remove("open"); });
|
||||
}
|
||||
|
||||
function renderCompanyManualRecords() {
|
||||
@@ -712,8 +766,6 @@ function appendAdminReviewRow(record, kind) {
|
||||
const tbody = $("#auditRows");
|
||||
if (!tbody) return;
|
||||
const isAccount = kind === "account";
|
||||
// Account rows come from the server (full number visible only in this
|
||||
// authorized admin view); manual records are still browser-local demo data.
|
||||
const statusLabel = isAccount ? accountStatusLabel(record.status) : record.status;
|
||||
const row = document.createElement("tr");
|
||||
row.dataset.storedReview = record.id;
|
||||
@@ -726,48 +778,52 @@ function appendAdminReviewRow(record, kind) {
|
||||
? "公司提交资料、开户行、账号、账户类型与启用日期"
|
||||
: "公司手工记录、关联银行流水号、证明附件与提交说明";
|
||||
if (statusLabel !== "待复核" && statusLabel !== "待总账复核") row.dataset.resolved = "true";
|
||||
if (isAccount) row.dataset.accountStatus = record.status;
|
||||
|
||||
const riskCell = document.createElement("td");
|
||||
const risk = document.createElement("span"); risk.className = "task-level warning"; risk.textContent = "中"; riskCell.append(risk);
|
||||
riskCell.innerHTML = '<span class="pill pill-warn">中</span>';
|
||||
|
||||
const identityCell = document.createElement("td");
|
||||
const identity = document.createElement("strong");
|
||||
const detail = document.createElement("small");
|
||||
const typeCell = document.createElement("td");
|
||||
const periodCell = document.createElement("td");
|
||||
const impactCell = document.createElement("td");
|
||||
const identity = document.createElement("span"); identity.className = "cell-main";
|
||||
const detail = document.createElement("span"); detail.className = "cell-sub";
|
||||
if (isAccount) {
|
||||
identity.textContent = `${record.company_name} · ${record.bank_name} ${String(record.account_number).slice(-4)}`;
|
||||
detail.textContent = `${record.account_type} · 完整账号 ${record.account_number}`;
|
||||
typeCell.textContent = "账户登记";
|
||||
periodCell.textContent = record.effective_from || "待审核确定";
|
||||
impactCell.textContent = "账户识别与流水上传";
|
||||
} else {
|
||||
identity.textContent = `${record.company} · ${record.id}`;
|
||||
detail.textContent = `${record.direction} ${formatCurrency(record.amount)} 元 · ${record.counterparty} · ${record.subject}`;
|
||||
typeCell.textContent = "手工记录";
|
||||
periodCell.textContent = record.transactionDate;
|
||||
impactCell.textContent = `待确认往来 ${(Number(record.amount) / 10000).toFixed(2)} 万元`;
|
||||
}
|
||||
identityCell.append(identity, detail);
|
||||
|
||||
const typeCell = document.createElement("td");
|
||||
typeCell.textContent = isAccount ? "账户登记" : "手工记录";
|
||||
|
||||
const periodCell = document.createElement("td");
|
||||
periodCell.textContent = isAccount ? (record.effective_from || "待审核确定") : record.transactionDate;
|
||||
|
||||
const impactCell = document.createElement("td");
|
||||
impactCell.className = "wrap";
|
||||
impactCell.textContent = isAccount ? "账户识别与流水上传" : `待确认往来 ${(Number(record.amount) / 10000).toFixed(2)} 万元`;
|
||||
|
||||
const statusCell = document.createElement("td");
|
||||
const status = recordStatus(statusLabel);
|
||||
const badge = document.createElement("span"); badge.className = `status ${status.className}`; badge.textContent = status.label; statusCell.append(badge);
|
||||
statusCell.innerHTML = `<span class="pill ${pillClass(status.className)}">${status.label}</span>`;
|
||||
|
||||
const actionCell = document.createElement("td");
|
||||
const action = document.createElement("button"); action.className = "text-button"; action.dataset.auditAction = "";
|
||||
if (isAccount) row.dataset.accountStatus = record.status;
|
||||
action.textContent = row.dataset.resolved
|
||||
? (isAccount && record.status === "active" ? "管理" : "查看记录")
|
||||
: "复核";
|
||||
if (row.dataset.resolved) {
|
||||
const decisionLabels = { active: "复核通过并启用账户", returned: "退回公司修改", disabled: "停用并驳回" };
|
||||
const decisionLabel = isAccount ? decisionLabels[record.status] || statusLabel : record.decision || record.status;
|
||||
const reasonText = (isAccount ? record.review_reason : record.reviewReason) || "已留痕";
|
||||
action.dataset.record = `${decisionLabel} · ${reasonText}`;
|
||||
action.dataset.decision = decisionLabel;
|
||||
action.dataset.reason = reasonText;
|
||||
action.dataset.processedAt = (isAccount ? record.reviewed_at : record.reviewedAt) || "时间未记录";
|
||||
const isActiveAccount = isAccount && record.status === "active";
|
||||
if (isActiveAccount) {
|
||||
actionCell.innerHTML = '<button type="button" class="btn btn-sm btn-danger" data-audit-action="disable">停用</button>';
|
||||
} else {
|
||||
const resolvedLabel = isAccount
|
||||
? ({ returned: "已退回", disabled: "已停用" })[record.status] || "已通过"
|
||||
: (statusLabel === "已确认" ? "已通过" : "已驳回");
|
||||
actionCell.innerHTML = `<span class="meta">${resolvedLabel} · 系统管理员</span>`;
|
||||
}
|
||||
} else {
|
||||
actionCell.innerHTML = '<div class="row" style="gap:6px;"><button type="button" class="btn btn-sm btn-primary" data-audit-action="approve">通过</button><button type="button" class="btn btn-sm btn-danger" data-audit-action="reject">驳回</button></div>';
|
||||
}
|
||||
actionCell.append(action);
|
||||
|
||||
row.append(riskCell, identityCell, typeCell, periodCell, impactCell, statusCell, actionCell);
|
||||
tbody.append(row);
|
||||
}
|
||||
@@ -781,6 +837,7 @@ async function renderAdminAccountReviews() {
|
||||
const result = await response.json().catch(() => null);
|
||||
(result?.accounts || []).forEach((account) => appendAdminReviewRow(account, "account"));
|
||||
updateAuditCounts();
|
||||
updatePendingAccountNotice();
|
||||
}
|
||||
|
||||
function renderStoredAdminReviews() {
|
||||
@@ -805,10 +862,18 @@ function updateAuditCounts() {
|
||||
$$('[data-audit-filter]').forEach((button) => {
|
||||
const type = button.dataset.auditFilter;
|
||||
const count = unresolved.filter((row) => type === "all" || row.dataset.auditType === type).length;
|
||||
button.textContent = `${button.dataset.label} ${count}`;
|
||||
const badge = $(".tab-count", button);
|
||||
if (badge) badge.textContent = count;
|
||||
});
|
||||
const badge = $('.side-nav a[data-view="audit"] .nav-badge');
|
||||
if (badge) badge.textContent = unresolved.length;
|
||||
if (badge) {
|
||||
badge.textContent = unresolved.length;
|
||||
badge.style.display = unresolved.length ? "" : "none";
|
||||
}
|
||||
const pending = $("#pending-count");
|
||||
if (pending) pending.textContent = unresolved.length;
|
||||
const foot = $("#auditFoot");
|
||||
if (foot) foot.textContent = `共 ${rows.length} 项 · 待审核 ${unresolved.length} 项`;
|
||||
}
|
||||
|
||||
function companyStatusBadge(status) {
|
||||
@@ -820,24 +885,42 @@ function companyStatusBadge(status) {
|
||||
function renderAdminCompanyTable(companies) {
|
||||
const tbody = $("#companyTable tbody");
|
||||
if (!tbody) return;
|
||||
state.companies = companies;
|
||||
tbody.replaceChildren(...companies.map((company) => {
|
||||
const row = document.createElement("tr");
|
||||
row.dataset.companyId = company.id;
|
||||
const nameCell = document.createElement("td");
|
||||
const name = document.createElement("strong"); name.textContent = company.name;
|
||||
const code = document.createElement("small"); code.textContent = `COMP-${String(company.id).padStart(3, "0")}`;
|
||||
const name = document.createElement("span"); name.className = "cell-main"; name.textContent = company.name;
|
||||
const code = document.createElement("span"); code.className = "cell-sub"; code.textContent = company.credit_code || "统一社会信用代码待补充";
|
||||
nameCell.append(name, code);
|
||||
const credit = document.createElement("td"); credit.textContent = company.credit_code || "待补充";
|
||||
const accounts = document.createElement("td"); accounts.textContent = `${company.account_count ?? 0} 个`;
|
||||
const usernames = document.createElement("td"); usernames.textContent = company.usernames || "未创建";
|
||||
const accounts = document.createElement("td"); accounts.className = "num-col"; accounts.textContent = `${company.account_count ?? 0}`;
|
||||
const usernames = document.createElement("td"); usernames.className = "num"; usernames.textContent = company.usernames || "未创建";
|
||||
const cashier = document.createElement("td"); cashier.textContent = company.cashier_name || "未指定";
|
||||
const statusCell = document.createElement("td");
|
||||
const badge = companyStatusBadge(company.status);
|
||||
statusCell.innerHTML = `<span class="status ${badge.className}">${badge.label}</span>`;
|
||||
statusCell.innerHTML = `<span class="pill ${pillClass(badge.className)}">${badge.label}</span>`;
|
||||
const actionCell = document.createElement("td");
|
||||
actionCell.innerHTML = `<button class="text-button" data-toast="已打开 ${company.name} 主档">管理</button>`;
|
||||
row.append(nameCell, credit, accounts, usernames, cashier, statusCell, actionCell);
|
||||
actionCell.innerHTML = `<button type="button" class="btn btn-sm" data-company-view="${company.id}">查看</button>`;
|
||||
row.append(nameCell, accounts, usernames, cashier, statusCell, actionCell);
|
||||
return row;
|
||||
}));
|
||||
const foot = $("#companyFoot");
|
||||
if (foot) foot.textContent = `共 ${companies.length} 家公司`;
|
||||
}
|
||||
|
||||
function updatePendingAccountNotice() {
|
||||
const rows = $$('#auditRows tr[data-record-kind="account"]');
|
||||
const pending = rows.filter((r) => r.dataset.resolved !== "true");
|
||||
const notice = $("#notice-pending-account");
|
||||
if (!notice) return;
|
||||
if (pending.length) {
|
||||
notice.style.display = "";
|
||||
$("#pending-account-count").textContent = pending.length;
|
||||
const names = [...new Set(pending.map((r) => r.dataset.company))].join("、");
|
||||
$("#pending-account-body").textContent = (names || "成员公司") + " 提交了新银行账户登记,等待审核通过后纳入账期流水归集范围。";
|
||||
} else {
|
||||
notice.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectOptions(select, names, { keepFirst = false } = {}) {
|
||||
@@ -858,7 +941,21 @@ function fillCompanySelects(names) {
|
||||
});
|
||||
setSelectOptions($("#auditCompany"), names, { keepFirst: true });
|
||||
setSelectOptions($("#flowCompany"), names, { keepFirst: true });
|
||||
setSelectOptions($('#reminderForm [name="company"]'), names, { keepFirst: true });
|
||||
const reminderList = $("#reminderCompanyList");
|
||||
if (reminderList) {
|
||||
reminderList.replaceChildren(...names.map((name) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "row";
|
||||
label.style.cssText = "gap:6px;font-size:13px;";
|
||||
const input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.name = "company";
|
||||
input.value = name;
|
||||
input.style.width = "auto";
|
||||
label.append(input, document.createTextNode(name));
|
||||
return label;
|
||||
}));
|
||||
}
|
||||
setSelectOptions($('#openingDialog [name="from"]'), names);
|
||||
setSelectOptions($('#openingDialog [name="to"]'), names);
|
||||
}
|
||||
@@ -870,6 +967,7 @@ async function loadAdminCompanies() {
|
||||
const companies = result?.companies || [];
|
||||
renderAdminCompanyTable(companies);
|
||||
fillCompanySelects(companies.map((company) => company.name));
|
||||
if (companies.length >= 2) setPair(companies[0].name, companies[1].name);
|
||||
}
|
||||
|
||||
function initAdmin() {
|
||||
@@ -895,63 +993,23 @@ function initAdmin() {
|
||||
activeAuditType = button.dataset.auditFilter;
|
||||
$$("[data-audit-filter]").forEach((item) => {
|
||||
const active = item === button;
|
||||
item.classList.toggle("is-active", active);
|
||||
item.classList.toggle("active", active);
|
||||
item.setAttribute("aria-pressed", String(active));
|
||||
});
|
||||
filterAuditRows();
|
||||
}));
|
||||
$("#auditCompany")?.addEventListener("change", filterAuditRows);
|
||||
|
||||
const auditDialog = $("#auditDialog");
|
||||
$$('[data-close-audit]').forEach((button) => button.addEventListener("click", () => auditDialog.close()));
|
||||
// Delegated: account review rows arrive asynchronously from the API.
|
||||
$("#auditRows")?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-audit-action]");
|
||||
if (!button) return;
|
||||
const row = button.closest("tr");
|
||||
state.auditRow = row;
|
||||
const cells = $$('td', row);
|
||||
const form = $("#auditForm");
|
||||
const decision = $('[name="decision"]', form);
|
||||
const reason = $('[name="reason"]', form);
|
||||
const submit = $('button[type="submit"]', form);
|
||||
form.reset();
|
||||
const isActiveAccount = row.dataset.recordKind === "account" && row.dataset.accountStatus === "active";
|
||||
const decisions = row.dataset.recordKind === "account"
|
||||
? (isActiveAccount ? ["停用并驳回"] : ["复核通过并启用账户", "退回公司修改", "停用并驳回"])
|
||||
: ["确认并纳入计算", "退回公司补充材料", "转为异常待后续处理"];
|
||||
decision.replaceChildren(new Option("请选择", ""), ...decisions.map((item) => new Option(item, item)));
|
||||
decision.disabled = false;
|
||||
reason.disabled = false;
|
||||
submit.hidden = false;
|
||||
$("#auditDialogTitle").textContent = `${cells[2].innerText.trim()} · ${cells[1].querySelector("strong").textContent}`;
|
||||
$("#auditDialogMeta").textContent = `${cells[3].innerText.trim()} · 影响 ${cells[4].innerText.trim()}`;
|
||||
const evidence = $("#auditEvidence");
|
||||
evidence.replaceChildren();
|
||||
const heading = document.createElement("strong"); heading.textContent = cells[1].querySelector("strong").textContent;
|
||||
const detail = document.createElement("small"); detail.textContent = cells[1].querySelector("small").textContent;
|
||||
const source = document.createElement("small"); source.textContent = `证据:${row.dataset.evidence || "银行原始行、导入批次、账户覆盖区间与公司说明"}`;
|
||||
evidence.append(heading, detail, source);
|
||||
if (button.dataset.record && !isActiveAccount) {
|
||||
decision.value = button.dataset.decision;
|
||||
reason.value = button.dataset.reason;
|
||||
decision.disabled = true;
|
||||
reason.disabled = true;
|
||||
submit.hidden = true;
|
||||
$("#auditDialogMeta").textContent = `已处理 · ${button.dataset.processedAt} · 系统管理员`;
|
||||
const record = document.createElement("small");
|
||||
record.textContent = `处理记录:${button.dataset.record}`;
|
||||
evidence.append(record);
|
||||
}
|
||||
auditDialog.showModal();
|
||||
});
|
||||
$("#auditForm")?.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
const row = state.auditRow;
|
||||
const decision = String(data.get("decision"));
|
||||
const reason = String(data.get("reason"));
|
||||
const approved = decision.includes("通过") || decision.includes("确认并纳入");
|
||||
function openModal(id) { $("#" + id)?.classList.add("open"); }
|
||||
function closeModal(id) { $("#" + id)?.classList.remove("open"); }
|
||||
document.querySelectorAll("[data-close]").forEach((el) => el.addEventListener("click", () => closeModal(el.dataset.close)));
|
||||
document.querySelectorAll(".modal-backdrop").forEach((bd) => bd.addEventListener("click", (e) => { if (e.target === bd) bd.classList.remove("open"); }));
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "Escape") document.querySelectorAll(".modal-backdrop.open").forEach((m) => m.classList.remove("open")); });
|
||||
|
||||
async function submitAuditResult(row) {
|
||||
const decision = state.auditDecision;
|
||||
const reason = state.auditReason || "";
|
||||
const approved = decision.includes("通过") || decision.includes("确认并纳入") || decision.includes("启用");
|
||||
const returned = decision.includes("退回");
|
||||
let storedStatus;
|
||||
let reviewedAccount = null;
|
||||
@@ -982,27 +1040,66 @@ function initAdmin() {
|
||||
}
|
||||
const status = recordStatus(storedStatus);
|
||||
const statusCell = row.children[5];
|
||||
statusCell.innerHTML = `<span class="status ${status.className}">${status.label}</span>`;
|
||||
statusCell.innerHTML = `<span class="pill ${pillClass(status.className)}">${status.label}</span>`;
|
||||
row.dataset.resolved = "true";
|
||||
const action = $("[data-audit-action]", row);
|
||||
if (reviewedAccount) {
|
||||
row.dataset.accountStatus = reviewedAccount.status;
|
||||
action.textContent = reviewedAccount.status === "active" ? "管理" : "查看记录";
|
||||
if (reviewedAccount) row.dataset.accountStatus = reviewedAccount.status;
|
||||
const actionCell = row.children[6];
|
||||
if (reviewedAccount && reviewedAccount.status === "active") {
|
||||
actionCell.innerHTML = '<button type="button" class="btn btn-sm btn-danger" data-audit-action="disable">停用</button>';
|
||||
} else {
|
||||
action.textContent = "查看记录";
|
||||
actionCell.innerHTML = `<span class="meta">${approved ? "已通过" : "已驳回"} · 系统管理员</span>`;
|
||||
}
|
||||
action.dataset.record = `${decision} · ${data.get("reason")}`;
|
||||
action.dataset.decision = decision;
|
||||
action.dataset.reason = data.get("reason");
|
||||
action.dataset.processedAt = new Date().toLocaleString("zh-CN", { hour12: false });
|
||||
updateAuditCounts();
|
||||
auditDialog.close();
|
||||
event.currentTarget.reset();
|
||||
showToast("审核结果已记录", approved ? "已通过的数据取得相应使用或核算资格" : "当前记录不参与自动核算");
|
||||
}
|
||||
|
||||
$("#auditRows")?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-audit-action]");
|
||||
if (!button) return;
|
||||
const row = button.closest("tr");
|
||||
state.auditRow = row;
|
||||
const cells = $$("td", row);
|
||||
const company = cells[1]?.querySelector(".cell-main")?.textContent.trim() || "";
|
||||
const period = cells[3]?.textContent.trim() || "";
|
||||
const type = cells[2]?.textContent.trim() || "";
|
||||
const basis = row.dataset.evidence || "银行原始行、导入批次、账户覆盖区间与公司说明";
|
||||
const action = button.dataset.auditAction;
|
||||
if (action === "approve") {
|
||||
$("#approve-company").textContent = company;
|
||||
$("#approve-period").textContent = period;
|
||||
$("#approve-type").textContent = type;
|
||||
$("#approve-basis").textContent = basis;
|
||||
openModal("modal-approve");
|
||||
} else if (action === "reject") {
|
||||
$("#reject-reason").value = "";
|
||||
$("#reject-hint").style.display = "none";
|
||||
openModal("modal-reject");
|
||||
} else if (action === "disable") {
|
||||
state.auditDecision = "停用并驳回";
|
||||
state.auditReason = "停用并驳回该账户";
|
||||
submitAuditResult(row);
|
||||
}
|
||||
});
|
||||
|
||||
const dialog = $("#companyDialog");
|
||||
$("#openCompanyDialog")?.addEventListener("click", () => dialog.showModal());
|
||||
$("#approve-confirm")?.addEventListener("click", () => {
|
||||
if (!state.auditRow) return;
|
||||
closeModal("modal-approve");
|
||||
state.auditDecision = state.auditRow.dataset.recordKind === "account" ? "复核通过并启用账户" : "确认并纳入计算";
|
||||
state.auditReason = state.auditDecision;
|
||||
submitAuditResult(state.auditRow);
|
||||
});
|
||||
|
||||
$("#reject-confirm")?.addEventListener("click", () => {
|
||||
if (!state.auditRow) return;
|
||||
const reason = $("#reject-reason").value.trim();
|
||||
if (reason.length < 5) { $("#reject-hint").style.display = ""; return; }
|
||||
closeModal("modal-reject");
|
||||
state.auditDecision = state.auditRow.dataset.recordKind === "account" ? "退回公司修改" : "退回公司补充材料";
|
||||
state.auditReason = reason;
|
||||
submitAuditResult(state.auditRow);
|
||||
});
|
||||
|
||||
$("#openCompanyDialog")?.addEventListener("click", () => openModal("companyDialog"));
|
||||
$("#companyForm")?.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
@@ -1032,7 +1129,7 @@ function initAdmin() {
|
||||
return;
|
||||
}
|
||||
const accountCreated = Boolean(result.username);
|
||||
dialog.close();
|
||||
closeModal("companyDialog");
|
||||
event.currentTarget.reset();
|
||||
await loadAdminCompanies();
|
||||
showToast(
|
||||
@@ -1041,47 +1138,121 @@ function initAdmin() {
|
||||
);
|
||||
});
|
||||
|
||||
function openCompanyDetail(companyId) {
|
||||
const company = (state.companies || []).find((c) => String(c.id) === String(companyId));
|
||||
if (!company) return;
|
||||
$("#d-title").textContent = company.name;
|
||||
$("#d-sub").textContent = `统一社会信用代码 ${company.credit_code || "待补充"}`;
|
||||
$("#d-kv").innerHTML =
|
||||
'<dt>公司名称</dt><dd>' + company.name + '</dd>' +
|
||||
'<dt>统一社会信用代码</dt><dd>' + (company.credit_code || "待补充") + '</dd>' +
|
||||
'<dt>出纳</dt><dd>' + (company.cashier_name || "未指定") + '</dd>' +
|
||||
'<dt>银行账户</dt><dd>' + (company.account_count ?? 0) + ' 个</dd>' +
|
||||
'<dt>状态</dt><dd>' + companyStatusBadge(company.status).label + '</dd>';
|
||||
$("#d-login-account").textContent = company.usernames || "未创建公司账号";
|
||||
$("#d-login-meta").textContent = company.usernames ? "公司端登录账号" : "该公司尚未创建登录账号";
|
||||
const resetBtn = $("#btn-reset-pwd");
|
||||
resetBtn.disabled = !company.usernames;
|
||||
resetBtn.textContent = "重置密码";
|
||||
resetBtn.dataset.companyId = company.id;
|
||||
openModal("modal-detail");
|
||||
}
|
||||
|
||||
$("#companyTable tbody")?.addEventListener("click", (event) => {
|
||||
const btn = event.target.closest("[data-company-view]");
|
||||
if (btn) openCompanyDetail(btn.dataset.companyView);
|
||||
});
|
||||
|
||||
$("#btn-reset-pwd")?.addEventListener("click", async () => {
|
||||
const companyId = $("#btn-reset-pwd").dataset.companyId;
|
||||
const usersResponse = await fetch("/api/admin/users").catch(() => null);
|
||||
const usersResult = await usersResponse?.json().catch(() => ({}));
|
||||
const user = (usersResult?.users || []).find((u) => String(u.company_id) === String(companyId) && u.role === "company");
|
||||
if (!user) { showToast("重置失败", "该公司尚无公司登录账号"); return; }
|
||||
const response = await fetch(`/api/admin/users/${user.id}/reset-password`, { method: "POST" }).catch(() => null);
|
||||
if (response?.status === 401) { window.location.href = "index.html"; return; }
|
||||
const result = await response?.json().catch(() => ({}));
|
||||
if (!response || !response.ok) { showToast("重置失败", result?.message || "请稍后重试"); return; }
|
||||
$("#btn-reset-pwd").disabled = true;
|
||||
$("#btn-reset-pwd").textContent = "已重置";
|
||||
$("#d-login-meta").textContent = `临时密码已生成(仅此一次):${result.initial_password},首次登录必须修改`;
|
||||
showToast("密码已重置", "临时密码仅本次显示,首次登录必须修改");
|
||||
});
|
||||
|
||||
const remind = $("#cs-remind");
|
||||
const track = $("#cs-switch-track");
|
||||
const thumb = $("#cs-switch-thumb");
|
||||
const daysSel = $("#cs-remind-days");
|
||||
function renderSwitch() {
|
||||
if (!remind) return;
|
||||
track.style.background = remind.checked ? "var(--accent)" : "var(--fg-soft)";
|
||||
thumb.style.left = remind.checked ? "18px" : "2px";
|
||||
if (daysSel) daysSel.disabled = !remind.checked;
|
||||
}
|
||||
remind?.addEventListener("change", renderSwitch);
|
||||
renderSwitch();
|
||||
|
||||
$("#systemSettings")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const day = parseInt($("#cs-day")?.value, 10);
|
||||
const tip = $("#cs-save-tip");
|
||||
if (day && (day < 1 || day > 28)) {
|
||||
if (tip) { tip.style.display = ""; tip.style.color = "var(--danger)"; tip.textContent = "结账日须为 1-28 之间的整数"; }
|
||||
return;
|
||||
}
|
||||
if (tip) { tip.style.display = ""; tip.style.color = "var(--success)"; tip.textContent = "已保存 · 立即生效"; }
|
||||
showToast("系统计算口径已保存", "正式系统将记录修改前后值与操作人");
|
||||
});
|
||||
|
||||
$("#runClosingCheck")?.addEventListener("click", () => {
|
||||
const unresolved = $$(".audit-table tbody tr").filter((row) => row.dataset.resolved !== "true" && !$(".status.success", row)).length;
|
||||
const unresolved = $$(".audit-table tbody tr").filter((row) => row.dataset.resolved !== "true").length;
|
||||
const blockNotice = $("#block-notice");
|
||||
if (unresolved) {
|
||||
if (blockNotice) blockNotice.style.display = "";
|
||||
$("#closingDescription").textContent = `2026 年 7 月 · 仍有 ${unresolved} 项审核事项未处理`;
|
||||
$("#closingStatus").className = "pill pill-danger";
|
||||
$("#closingStatus").textContent = "已阻断";
|
||||
$("#executeClosing").disabled = true;
|
||||
showToast("结账检查未通过", `仍有 ${unresolved} 项审核事项,已打开审核中心`);
|
||||
showView("audit");
|
||||
} else {
|
||||
$$("#closingPanel .is-blocked").forEach((item) => {
|
||||
item.classList.remove("is-blocked");
|
||||
$("use", item).setAttribute("href", "icons.svg#circle-check");
|
||||
$("small", item).textContent = "检查已通过";
|
||||
if (blockNotice) blockNotice.style.display = "none";
|
||||
$$("#closingPanel [data-closing-state]").forEach((item) => {
|
||||
item.className = "pill pill-success";
|
||||
item.textContent = "已通过";
|
||||
});
|
||||
$("#closingDescription").textContent = "2026 年 7 月 · 全部前置检查已通过";
|
||||
$("#closingStatus").className = "status success";
|
||||
$("#closingStatus").className = "pill pill-success";
|
||||
$("#closingStatus").textContent = "可结账";
|
||||
$("#executeClosing").disabled = false;
|
||||
$("#executeClosing").removeAttribute("title");
|
||||
showToast("结账检查通过", "执行结账按钮已解锁");
|
||||
}
|
||||
});
|
||||
const closingDialog = $("#closingDialog");
|
||||
$("#executeClosing")?.addEventListener("click", () => closingDialog.showModal());
|
||||
$$('[data-close-closing]').forEach((button) => button.addEventListener("click", () => closingDialog.close()));
|
||||
|
||||
$("#executeClosing")?.addEventListener("click", () => openModal("closingDialog"));
|
||||
$$("[data-close-closing]").forEach((button) => button.addEventListener("click", () => closeModal("closingDialog")));
|
||||
$("#closingForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
closingDialog.close();
|
||||
closeModal("closingDialog");
|
||||
$("#closingDescription").textContent = "2026 年 7 月 · 已完成集团结账";
|
||||
$("#closingStatus").className = "status success";
|
||||
$("#closingStatus").className = "pill pill-success";
|
||||
$("#closingStatus").textContent = "已结账";
|
||||
$("#closingHistory").textContent = `${new Date().toLocaleString("zh-CN", { hour12: false })} · 系统管理员执行 2026 年 7 月结账 · 已写入审计记录`;
|
||||
$("#runClosingCheck").disabled = true;
|
||||
$("#executeClosing").disabled = true;
|
||||
$("#executeClosing").textContent = "7 月已结账";
|
||||
const closedNotice = $("#closed-notice");
|
||||
if (closedNotice) closedNotice.style.display = "";
|
||||
const tlCurrent = $("#tl-current");
|
||||
tlCurrent?.classList.remove("current");
|
||||
tlCurrent?.classList.add("closed");
|
||||
$("#tl-current-state").textContent = "已结账";
|
||||
showToast("2026 年 7 月已完成结账", "本期结果已锁定,后续补录将进入重开流程");
|
||||
});
|
||||
const openingDialog = $("#openingDialog");
|
||||
$("#openOpeningDialog")?.addEventListener("click", () => openingDialog.showModal());
|
||||
$$('[data-close-opening]').forEach((button) => button.addEventListener("click", () => openingDialog.close()));
|
||||
|
||||
$("#openOpeningDialog")?.addEventListener("click", () => openModal("openingDialog"));
|
||||
$$("[data-close-opening]").forEach((button) => button.addEventListener("click", () => closeModal("openingDialog")));
|
||||
$("#openingForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
@@ -1090,36 +1261,191 @@ function initAdmin() {
|
||||
return;
|
||||
}
|
||||
const row = document.createElement("tr");
|
||||
[data.get("from"), data.get("to"), data.get("subject"), data.get("direction")].forEach((value) => {
|
||||
const cell = document.createElement("td"); cell.textContent = value; row.append(cell);
|
||||
});
|
||||
const amount = document.createElement("td"); amount.className = "number"; amount.textContent = Number(data.get("amount")).toLocaleString("zh-CN", {minimumFractionDigits:2}); row.append(amount);
|
||||
const date = document.createElement("td"); date.textContent = data.get("effectiveDate"); row.append(date);
|
||||
const status = document.createElement("td"); status.innerHTML = '<span class="status warning">待复核</span>'; row.append(status);
|
||||
const self = document.createElement("td"); self.className = "cell-main"; self.textContent = data.get("from"); row.append(self);
|
||||
const peer = document.createElement("td"); peer.textContent = data.get("to"); row.append(peer);
|
||||
const subject = document.createElement("td"); subject.textContent = data.get("subject"); row.append(subject);
|
||||
const direction = document.createElement("td"); direction.textContent = data.get("direction"); row.append(direction);
|
||||
const amount = document.createElement("td"); amount.className = "num-col"; amount.textContent = Number(data.get("amount")).toLocaleString("zh-CN", { minimumFractionDigits: 2 }); row.append(amount);
|
||||
const date = document.createElement("td"); date.className = "num"; date.textContent = data.get("effectiveDate"); row.append(date);
|
||||
const status = document.createElement("td"); status.innerHTML = '<span class="pill pill-warn">待复核</span>'; row.append(status);
|
||||
$("#openingRows").append(row);
|
||||
openingDialog.close();
|
||||
closeModal("openingDialog");
|
||||
event.currentTarget.reset();
|
||||
showToast("期初余额已提交复核", "正式系统将保留录入依据与操作人");
|
||||
});
|
||||
|
||||
$("#reminderForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget);
|
||||
const article = document.createElement("article");
|
||||
article.innerHTML = '<span class="notification-icon warning"><svg><use href="icons.svg#bell"/></svg></span>';
|
||||
const content = document.createElement("span");
|
||||
const heading = document.createElement("strong");
|
||||
heading.textContent = `${data.get("company")} · ${data.get("message")}`;
|
||||
const meta = document.createElement("small");
|
||||
meta.textContent = `手动提醒 · 截止 ${data.get("dueDate")} · 刚刚`;
|
||||
content.append(heading, meta);
|
||||
const status = document.createElement("em");
|
||||
status.className = "status warning";
|
||||
status.textContent = "未读";
|
||||
article.append(content, status);
|
||||
$("#adminReminderList").prepend(article);
|
||||
event.currentTarget.reset();
|
||||
showToast("提醒已发送", "对方将在公司业务端收到站内通知");
|
||||
const form = event.currentTarget;
|
||||
const checked = Array.prototype.slice.call(form.querySelectorAll('input[name="company"]:checked'));
|
||||
const type = $("#reminder-type").value;
|
||||
const content = $("#reminder-content").value.trim();
|
||||
const deadline = $("#reminder-deadline").value;
|
||||
$("#company-error").style.display = checked.length ? "none" : "";
|
||||
$("#content-error").style.display = content ? "none" : "";
|
||||
if (!checked.length || !content) return;
|
||||
const companies = checked.map((c) => c.value).join("、");
|
||||
const summary = content.length > 46 ? content.slice(0, 46) + "…" : content;
|
||||
const row = document.createElement("tr");
|
||||
row.dataset.source = "manual";
|
||||
row.innerHTML =
|
||||
'<td class="cell-main">' + companies + '</td>' +
|
||||
'<td><span class="tag">' + type + '</span></td>' +
|
||||
'<td class="wrap">' + summary.replace(/</g, "<") + '</td>' +
|
||||
'<td class="meta">刚刚<span class="cell-sub">人工 · 系统管理员</span></td>' +
|
||||
'<td class="meta">' + (deadline || "—") + '</td>' +
|
||||
'<td><span class="pill pill-danger">未读</span></td>' +
|
||||
'<td><div class="row" style="gap:6px;"><button type="button" class="btn btn-sm act-remind">再提醒</button><button type="button" class="btn btn-sm btn-ghost act-history" data-company="' + checked[0].value + '">查看历史</button></div></td>';
|
||||
$("#reminder-tbody").insertBefore(row, $("#reminder-tbody").firstChild);
|
||||
refreshReminderCounts();
|
||||
form.querySelectorAll('input[name="company"]').forEach((c) => { c.checked = false; });
|
||||
$("#reminder-content").value = "请于截止日期前完成 2026 年 7 月银行流水上传与待确认事项处理。";
|
||||
$("#reminder-deadline").value = "2026-08-29";
|
||||
const sendHint = $("#send-hint");
|
||||
sendHint.style.display = "";
|
||||
sendHint.textContent = "已发送给 " + companies + ",共 " + checked.length + " 家公司。";
|
||||
clearTimeout(sendHint._t);
|
||||
sendHint._t = setTimeout(() => { sendHint.style.display = "none"; }, 4000);
|
||||
});
|
||||
|
||||
// 提醒历史 tabs / 计数
|
||||
function refreshReminderCounts() {
|
||||
const rows = $$("#reminder-tbody tr");
|
||||
let all = rows.length, sys = 0, man = 0;
|
||||
rows.forEach((r) => { if (r.dataset.source === "system") sys++; else man++; });
|
||||
$("#count-all").textContent = all;
|
||||
$("#count-system").textContent = sys;
|
||||
$("#count-manual").textContent = man;
|
||||
$("#table-foot-count").textContent = "共 " + all + " 条提醒记录";
|
||||
}
|
||||
$("#reminder-tabs")?.addEventListener("click", (event) => {
|
||||
const btn = event.target.closest("button[data-filter]");
|
||||
if (!btn) return;
|
||||
$("#reminder-tabs").querySelectorAll("button").forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
const filter = btn.dataset.filter;
|
||||
$$("#reminder-tbody tr").forEach((r) => { r.style.display = (filter === "all" || r.dataset.source === filter) ? "" : "none"; });
|
||||
});
|
||||
|
||||
$("#reminder-tbody")?.addEventListener("click", (event) => {
|
||||
const remindBtn = event.target.closest(".act-remind");
|
||||
if (remindBtn) {
|
||||
const tr = remindBtn.closest("tr");
|
||||
const timeCell = tr.children[3];
|
||||
const sub = timeCell.querySelector(".cell-sub");
|
||||
timeCell.firstChild.textContent = "刚刚";
|
||||
if (sub) sub.textContent = sub.textContent.replace(/\s*·?\s*第 \d+ 次/, "") + " · 再提醒";
|
||||
else timeCell.insertAdjacentHTML("beforeend", '<span class="cell-sub">再提醒</span>');
|
||||
remindBtn.textContent = "已再提醒";
|
||||
remindBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
const historyBtn = event.target.closest(".act-history");
|
||||
if (historyBtn) openReminderHistory(historyBtn.dataset.company);
|
||||
});
|
||||
|
||||
const reminderHISTORIES = {
|
||||
"金牛农业": [
|
||||
{ time: "2026-08-18 09:02", src: "系统 · 第 2 次", type: "流水未提交", status: "danger", statusText: "未读", body: "贵公司 7 月全部银行账户流水尚未提交,距 7 月结账顺延截止日仅剩 11 天,请尽快上传。" },
|
||||
{ time: "2026-08-11 09:00", src: "系统 · 第 1 次", type: "流水未提交", status: "danger", statusText: "未读", body: "2026 年 7 月银行流水上传提醒:请于截止日前完成全部账户流水提交。" },
|
||||
],
|
||||
"金牛新能源": [
|
||||
{ time: "2026-08-15 09:00", src: "系统", type: "单边待确认", status: "warn", statusText: "处理中", body: "与金牛贸易 3 笔单边流水合计 ¥4,860,000.00 待确认,请选择对方银行流水佐证。" },
|
||||
],
|
||||
"金牛置业": [
|
||||
{ time: "2026-08-14 16:40", src: "人工 · 张维", type: "流水未提交", status: "warn", statusText: "处理中", body: "中行尾号 8821 账户 7 月 6—16 日流水断档,请补传该期间银行回单或对账单。" },
|
||||
],
|
||||
"金牛贸易": [
|
||||
{ time: "2026-08-15 09:00", src: "系统", type: "单边待确认", status: "success", statusText: "已完成", body: "与金牛新能源往来 3 笔单边流水待确认,请核对 7 月销售回款记录。" },
|
||||
],
|
||||
"金牛煤业": [
|
||||
{ time: "2026-08-12 09:00", src: "系统", type: "科目待确认", status: "warn", statusText: "处理中", body: "2 笔手工记录往来科目待确认(应收 / 其他应收),涉及煤炭运费结算。" },
|
||||
],
|
||||
"金牛物流": [
|
||||
{ time: "2026-08-10 11:25", src: "人工 · 张维", type: "科目待确认", status: "success", statusText: "已完成", body: "1 笔运费结算手工记录科目待确认(其他应付待判定),请于结账前完成。" },
|
||||
],
|
||||
};
|
||||
|
||||
function openReminderHistory(company) {
|
||||
$("#history-title").textContent = company + " · 提醒历史";
|
||||
$("#history-sub").textContent = "2026 年 7 月账期以来的全部触达记录,按时间倒序。";
|
||||
const list = $("#history-list");
|
||||
const items = reminderHISTORIES[company] || [];
|
||||
if (!items.length) {
|
||||
list.innerHTML = '<div class="empty"><div class="e-title">暂无历史记录</div>该公司在 7 月账期内尚未收到过提醒。</div>';
|
||||
} else {
|
||||
list.innerHTML = items.map((it) => {
|
||||
return '<div class="list-row">' +
|
||||
'<span class="tag">' + it.type + '</span>' +
|
||||
'<div class="lr-main"><div class="lr-title">' + it.body + '</div><div class="lr-sub"><span class="meta">' + it.time + ' · ' + it.src + '</span></div></div>' +
|
||||
'<div class="lr-side"><span class="pill pill-' + it.status + '">' + it.statusText + '</span></div></div>';
|
||||
}).join("");
|
||||
}
|
||||
openModal("history-modal");
|
||||
}
|
||||
$("#history-close")?.addEventListener("click", () => closeModal("history-modal"));
|
||||
$("#history-ok")?.addEventListener("click", () => closeModal("history-modal"));
|
||||
|
||||
refreshReminderCounts();
|
||||
}
|
||||
|
||||
const FLOW_DEMO = [
|
||||
{ date: "2026-07-01", company: "金牛煤业", bank: "工行", account: "3305", acctLabel: "工行 · 尾号 3305", dir: "收", dirPill: "pill-success", peer: "平顶山市恒源电力燃料有限公司", summary: "煤炭销售款(6 月结算)", serial: "ICBC202607010031825", status: "未归集", statusPill: "pill-muted", amount: "1,860,000.00", amtClass: "amt-in", detail: { bank: "工商银行", account: "河南金牛煤业有限公司 · 基本户 1702 0231 0900 8133 05", time: "2026-07-01 09:42:17", peer: "平顶山市恒源电力燃料有限公司", peerAcct: "工行平顶山分行 1702 0218 0902 6641 20", amount: "¥ 1,860,000.00(收)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "对方为集团外客户,不进入内部往来归集,仅作银行流水留档。" } },
|
||||
{ date: "2026-07-03", company: "金牛新能源", bank: "工行", account: "6649", acctLabel: "工行 · 尾号 6649", dir: "付", dirPill: "pill-danger", peer: "河南金牛贸易有限公司", summary: "光伏支架材料款", serial: "ICBC202607030094417", status: "单边", statusPill: "pill-danger", amount: "1,620,000.00", amtClass: "amt-out", detail: { bank: "工商银行", account: "河南金牛新能源有限公司 · 一般户 1704 0512 0900 2266 49", time: "2026-07-03 14:08:52", peer: "河南金牛贸易有限公司", peerAcct: "农行郑州金水支行 1606 3301 0400 0220 8", amount: "¥ 1,620,000.00(付)", status: "单边", pair: "金牛新能源 ↔ 金牛贸易", subject: "其他应付(新能源侧)", batch: "UNI-2026-07-002", note: "贸易侧 7 月上报流水中未找到对应收款,已挂起为单边流水,待贸易侧补充银行凭证佐证。" } },
|
||||
{ date: "2026-07-03", company: "金牛置业", bank: "中行", account: "8821", acctLabel: "中行 · 尾号 8821", dir: "收", dirPill: "pill-success", peer: "郑州市商品房预售资金监管专户", summary: "商品房预售款(A 区 12 号楼)", serial: "BOC202607030552108", status: "未归集", statusPill: "pill-muted", amount: "4,150,000.00", amtClass: "amt-in", detail: { bank: "中国银行", account: "河南金牛置业有限公司 · 一般户 2546 0387 0200 8821", time: "2026-07-03 10:26:31", peer: "郑州市商品房预售资金监管专户", peerAcct: "中行郑州郑东新区支行 2546 1180 0200 3477", amount: "¥ 4,150,000.00(收)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "预售监管资金划入,属对外经营收款,不参与集团内部往来归集。" } },
|
||||
{ date: "2026-07-05", company: "金牛煤业", bank: "工行", account: "3305", acctLabel: "工行 · 尾号 3305", dir: "付", dirPill: "pill-danger", peer: "河南金牛物流有限公司", summary: "矿区运输费(6 月)", serial: "ICBC202607050127663", status: "已归集", statusPill: "pill-success", amount: "1,240,000.00", amtClass: "amt-out", detail: { bank: "工商银行", account: "河南金牛煤业有限公司 · 基本户 1702 0231 0900 8133 05", time: "2026-07-05 11:15:09", peer: "河南金牛物流有限公司", peerAcct: "建行郑州经开区支行 4105 0167 8080 5562", amount: "¥ 1,240,000.00(付)", status: "已归集", pair: "金牛煤业 ↔ 金牛物流", subject: "应付(煤业侧)", batch: "JC-2026-07-014", note: "与物流侧建行尾号 5562 账户 07-05 收款流水双向匹配,金额一致。" } },
|
||||
{ date: "2026-07-05", company: "金牛物流", bank: "建行", account: "5562", acctLabel: "建行 · 尾号 5562", dir: "收", dirPill: "pill-success", peer: "河南金牛煤业有限公司", summary: "矿区运输费(6 月)", serial: "CCB202607050312940", status: "已归集", statusPill: "pill-success", amount: "1,240,000.00", amtClass: "amt-in", detail: { bank: "建设银行", account: "河南金牛物流有限公司 · 基本户 4105 0167 8080 5562", time: "2026-07-05 11:15:36", peer: "河南金牛煤业有限公司", peerAcct: "工行平顶山分行 1702 0231 0900 8133 05", amount: "¥ 1,240,000.00(收)", status: "已归集", pair: "金牛物流 ↔ 金牛煤业", subject: "应收(物流侧)", batch: "JC-2026-07-014", note: "与煤业侧工行尾号 3305 账户 07-05 付款流水双向匹配,金额一致。" } },
|
||||
{ date: "2026-07-08", company: "金牛煤业", bank: "工行", account: "3305", acctLabel: "工行 · 尾号 3305", dir: "收", dirPill: "pill-success", peer: "河南金牛置业有限公司", summary: "煤炭采购款(7 月)", serial: "ICBC202607080208554", status: "待确认", statusPill: "pill-warn", amount: "3,200,000.00", amtClass: "amt-in", detail: { bank: "工商银行", account: "河南金牛煤业有限公司 · 基本户 1702 0231 0900 8133 05", time: "2026-07-08 15:47:22", peer: "河南金牛置业有限公司", peerAcct: "中行郑州郑东新区支行 2546 0387 0200 8821", amount: "¥ 3,200,000.00(收)", status: "待确认", pair: "金牛煤业 ↔ 金牛置业", subject: "应收(煤业侧)", batch: "—(待归集)", note: "置业中行尾号 8821 账户 07-06 至 07-16 流水断档,对方付款凭证缺失,暂无法完成双边匹配,已列入审核中心高风险事项。" } },
|
||||
{ date: "2026-07-09", company: "金牛贸易", bank: "农行", account: "2208", acctLabel: "农行 · 尾号 2208", dir: "收", dirPill: "pill-success", peer: "洛阳建工集团有限公司", summary: "钢材销售款(6 月发货)", serial: "ABC202607090773261", status: "未归集", statusPill: "pill-muted", amount: "2,480,000.00", amtClass: "amt-in", detail: { bank: "农业银行", account: "河南金牛贸易有限公司 · 基本户 1606 3301 0400 0220 8", time: "2026-07-09 09:58:44", peer: "洛阳建工集团有限公司", peerAcct: "中行洛阳分行 2546 2201 0500 7915", amount: "¥ 2,480,000.00(收)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "对外钢材销售回款,不参与集团内部往来归集。" } },
|
||||
{ date: "2026-07-11", company: "金牛新能源", bank: "工行", account: "6649", acctLabel: "工行 · 尾号 6649", dir: "付", dirPill: "pill-danger", peer: "河南金牛贸易有限公司", summary: "电缆及配电柜采购款", serial: "ICBC202607110158902", status: "单边", statusPill: "pill-danger", amount: "1,950,000.00", amtClass: "amt-out", detail: { bank: "工商银行", account: "河南金牛新能源有限公司 · 一般户 1704 0512 0900 2266 49", time: "2026-07-11 16:32:08", peer: "河南金牛贸易有限公司", peerAcct: "农行郑州金水支行 1606 3301 0400 0220 8", amount: "¥ 1,950,000.00(付)", status: "单边", pair: "金牛新能源 ↔ 金牛贸易", subject: "其他应付(新能源侧)", batch: "UNI-2026-07-005", note: "贸易侧无对应收款记录,单边挂起。新能源↔贸易本月累计 3 笔单边流水,合计 486 万元。" } },
|
||||
{ date: "2026-07-14", company: "金牛煤业", bank: "中行", account: "9916", acctLabel: "中行 · 尾号 9916", dir: "付", dirPill: "pill-danger", peer: "平顶山天安煤业设备租赁有限公司", summary: "综采设备租赁费(7 月)", serial: "BOC202607140416337", status: "未归集", statusPill: "pill-muted", amount: "920,000.00", amtClass: "amt-out", detail: { bank: "中国银行", account: "河南金牛煤业有限公司 · 一般户 2546 0387 1100 9916", time: "2026-07-14 10:11:57", peer: "平顶山天安煤业设备租赁有限公司", peerAcct: "建行平顶山分行 4105 0229 8080 1347", amount: "¥ 920,000.00(付)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "综采设备月度租赁支出,对方为集团外供应商,不参与内部归集。" } },
|
||||
{ date: "2026-07-18", company: "金牛煤业", bank: "交行", account: "7710", acctLabel: "交行 · 尾号 7710", dir: "付", dirPill: "pill-danger", peer: "平顶山市安泰矿山设备有限公司", summary: "提升机大修款", serial: "BOCOM202607180062194", status: "待确认", statusPill: "pill-warn", amount: "685,400.00", amtClass: "amt-out", detail: { bank: "交通银行", account: "河南金牛煤业有限公司 · 一般户 4110 6120 0181 0077 10(账户待审核)", time: "2026-07-18 13:29:40", peer: "平顶山市安泰矿山设备有限公司", peerAcct: "工行平顶山分行 1702 0218 0902 9075 63", amount: "¥ 685,400.00(付)", status: "待确认", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "付款账户(交行尾号 7710)为新开户,尚在账户审核流程中,流水暂挂待确认,审核通过后自动归档为外部交易。" } },
|
||||
{ date: "2026-07-21", company: "金牛置业", bank: "中行", account: "8821", acctLabel: "中行 · 尾号 8821", dir: "收", dirPill: "pill-success", peer: "郑州市商品房预售资金监管专户", summary: "商品房预售款(A 区 15 号楼)", serial: "BOC202607210588420", status: "未归集", statusPill: "pill-muted", amount: "3,780,000.00", amtClass: "amt-in", detail: { bank: "中国银行", account: "河南金牛置业有限公司 · 一般户 2546 0387 0200 8821", time: "2026-07-21 09:35:12", peer: "郑州市商品房预售资金监管专户", peerAcct: "中行郑州郑东新区支行 2546 1180 0200 3477", amount: "¥ 3,780,000.00(收)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "预售监管资金划入。该账户 07-06 至 07-16 存在流水断档,本笔为断档后首笔入账。" } },
|
||||
{ date: "2026-07-22", company: "金牛物流", bank: "建行", account: "5562", acctLabel: "建行 · 尾号 5562", dir: "收", dirPill: "pill-success", peer: "河南金牛贸易有限公司", summary: "钢材干线运输费(6-7 月)", serial: "CCB202607220347815", status: "已归集", statusPill: "pill-success", amount: "462,800.00", amtClass: "amt-in", detail: { bank: "建设银行", account: "河南金牛物流有限公司 · 基本户 4105 0167 8080 5562", time: "2026-07-22 14:52:26", peer: "河南金牛贸易有限公司", peerAcct: "农行郑州金水支行 1606 3301 0400 0220 8", amount: "¥ 462,800.00(收)", status: "已归集", pair: "金牛物流 ↔ 金牛贸易", subject: "应收(物流侧)", batch: "JC-2026-07-021", note: "与贸易侧农行尾号 2208 账户 07-22 付款流水双向匹配,金额一致,已计入 7 月往来批次。" } },
|
||||
{ date: "2026-07-24", company: "金牛新能源", bank: "工行", account: "6649", acctLabel: "工行 · 尾号 6649", dir: "付", dirPill: "pill-danger", peer: "河南金牛贸易有限公司", summary: "组件辅材结算款", serial: "ICBC202607240221476", status: "单边", statusPill: "pill-danger", amount: "1,290,000.00", amtClass: "amt-out", detail: { bank: "工商银行", account: "河南金牛新能源有限公司 · 一般户 1704 0512 0900 2266 49", time: "2026-07-24 11:06:33", peer: "河南金牛贸易有限公司", peerAcct: "农行郑州金水支行 1606 3301 0400 0220 8", amount: "¥ 1,290,000.00(付)", status: "单边", pair: "金牛新能源 ↔ 金牛贸易", subject: "其他应付(新能源侧)", batch: "UNI-2026-07-009", note: "贸易侧无对应收款记录,单边挂起。新能源↔贸易本月累计 3 笔单边流水,合计 486 万元。" } },
|
||||
{ date: "2026-07-25", company: "金牛贸易", bank: "农行", account: "2208", acctLabel: "农行 · 尾号 2208", dir: "付", dirPill: "pill-danger", peer: "安阳钢铁集团有限责任公司", summary: "螺纹钢采购款(7 月)", serial: "ABC202607250819673", status: "未归集", statusPill: "pill-muted", amount: "5,620,000.00", amtClass: "amt-out", detail: { bank: "农业银行", account: "河南金牛贸易有限公司 · 基本户 1606 3301 0400 0220 8", time: "2026-07-25 10:19:05", peer: "安阳钢铁集团有限责任公司", peerAcct: "工行安阳分行 1706 0211 0900 4428 17", amount: "¥ 5,620,000.00(付)", status: "未归集", pair: "—(外部单位交易)", subject: "—", batch: "—", note: "对外螺纹钢采购付款,不参与集团内部往来归集。" } },
|
||||
];
|
||||
|
||||
function renderFlowRows() {
|
||||
const tbody = $("#flowTable tbody");
|
||||
if (!tbody) return;
|
||||
tbody.innerHTML = FLOW_DEMO.map((row, index) => {
|
||||
return `<tr class="clickable" data-flow-idx="${index}" data-company="${row.company}" data-bank="${row.bank}" data-account="${row.account}" data-date="${row.date}">
|
||||
<td class="num">${row.date}</td>
|
||||
<td class="cell-main">${row.company}</td>
|
||||
<td>${row.acctLabel}</td>
|
||||
<td><span class="pill ${row.dirPill}">${row.dir}</span></td>
|
||||
<td class="wrap">${row.peer}</td>
|
||||
<td class="wrap">${row.summary}</td>
|
||||
<td class="num">${row.serial}</td>
|
||||
<td><span class="pill ${row.statusPill}">${row.status}</span></td>
|
||||
<td class="num-col ${row.amtClass}">¥ ${row.amount}</td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
const count = $("#flowCount");
|
||||
if (count) count.textContent = `共 ${FLOW_DEMO.length} 笔 · 本页 1-${FLOW_DEMO.length}`;
|
||||
}
|
||||
|
||||
function openFlowDetail(index) {
|
||||
const modal = $("#tx-modal");
|
||||
if (!modal) return;
|
||||
const row = FLOW_DEMO[index];
|
||||
if (!row) return;
|
||||
$("#tx-modal-sub").textContent = `${row.company} · ${row.acctLabel} · ${row.date}`;
|
||||
const d = row.detail;
|
||||
$("#d-serial").textContent = row.serial;
|
||||
$("#d-bank").textContent = d.bank;
|
||||
$("#d-account").textContent = d.account;
|
||||
$("#d-time").textContent = d.time;
|
||||
$("#d-peer").textContent = d.peer;
|
||||
$("#d-peer-acct").textContent = d.peerAcct;
|
||||
$("#d-amount").textContent = d.amount;
|
||||
$("#d-status").textContent = d.status;
|
||||
$("#d-pair").textContent = d.pair;
|
||||
$("#d-subject").textContent = d.subject;
|
||||
$("#d-batch").textContent = d.batch;
|
||||
$("#d-note").textContent = d.note;
|
||||
modal.classList.add("open");
|
||||
}
|
||||
|
||||
function visibleRows(table) {
|
||||
@@ -1141,26 +1467,29 @@ function filterFlows() {
|
||||
}
|
||||
let count = 0;
|
||||
$$("tbody tr", table).forEach((row) => {
|
||||
const rowDate = $("td", row).textContent.trim().replaceAll(".", "-");
|
||||
const rowDate = row.dataset.date || $("td", row).textContent.trim().replaceAll(".", "-");
|
||||
const matchesCompany = company === "全部公司" || row.dataset.company === company;
|
||||
const matchesBank = bank === "全部银行" || row.dataset.bank === bank;
|
||||
const matchesAccount = account === "全部账户" || row.textContent.includes(account);
|
||||
const matchesAccount = account === "全部账户" || row.dataset.account === account || (row.dataset.account === undefined && row.textContent.includes(account));
|
||||
const matchesDate = rowDate >= startDate && rowDate <= endDate;
|
||||
const matchesKeyword = !keyword || row.textContent.toLowerCase().includes(keyword);
|
||||
row.hidden = !(matchesCompany && matchesBank && matchesAccount && matchesDate && matchesKeyword);
|
||||
if (!row.hidden) count += 1;
|
||||
});
|
||||
$("#flowCount").textContent = count;
|
||||
$("#flowCount").textContent = count === 0 ? "共 0 条 · 无匹配记录" : `共 ${count} 笔 · 本页 1-${count}`;
|
||||
showToast("查询完成", `当前显示 ${count} 笔流水`);
|
||||
}
|
||||
|
||||
function exportFlows() {
|
||||
const table = $("#flowTable");
|
||||
const rows = visibleRows(table);
|
||||
const headers = ["交易日期", "公司", "银行及账号", "收付方向", "对方户名及账号", "摘要", "银行流水号", "归集状态", "金额", "导入批次", "源行定位"];
|
||||
const headers = ["交易日期", "公司", "银行账户", "方向", "对方户名", "摘要", "银行流水号", "归集状态", "金额", "导入批次", "源行定位"];
|
||||
const records = rows.map((row, index) => {
|
||||
const cells = $$('td', row).map((cell) => cell.innerText.replace(/\n/g, " ").trim());
|
||||
return [cells[0], portal === "company" ? "A公司" : cells[1].split(" ")[0], portal === "company" ? cells[1] : cells[1], ...cells.slice(2), `IMP-DEMO-${String(index + 1).padStart(3, "0")}`, `Sheet1!R${index + 8}`];
|
||||
const imp = `IMP-DEMO-${String(index + 1).padStart(3, "0")}`;
|
||||
const loc = `Sheet1!R${index + 8}`;
|
||||
if (portal === "admin") return [...cells.slice(0, 9), imp, loc];
|
||||
return [cells[0], "A公司", cells[1], ...cells.slice(2), imp, loc];
|
||||
});
|
||||
const csv = [headers, ...records].map((record) => record.map((value) => `"${String(value ?? "").replace(/"/g, '""')}"`).join(",")).join("\r\n");
|
||||
const link = document.createElement("a");
|
||||
@@ -1174,6 +1503,24 @@ function exportFlows() {
|
||||
function initFlowTools() {
|
||||
$("#applyFlowFilters")?.addEventListener("click", filterFlows);
|
||||
$("#exportFlows")?.addEventListener("click", exportFlows);
|
||||
if (portal !== "admin") return;
|
||||
renderFlowRows();
|
||||
$("#resetFlowFilters")?.addEventListener("click", () => {
|
||||
$("#flowCompany").value = "全部公司";
|
||||
$("#flowBank").value = "全部银行";
|
||||
$("#flowAccount").value = "全部账户";
|
||||
$("#flowStart").value = "2026-07-01";
|
||||
$("#flowEnd").value = "2026-07-31";
|
||||
$("#flowKeyword").value = "";
|
||||
filterFlows();
|
||||
});
|
||||
$("#flowTable tbody")?.addEventListener("click", (event) => {
|
||||
const row = event.target.closest("tr[data-flow-idx]");
|
||||
if (row) openFlowDetail(Number(row.dataset.flowIdx));
|
||||
});
|
||||
$("#tx-modal-close")?.addEventListener("click", () => $("#tx-modal")?.classList.remove("open"));
|
||||
$("#tx-modal-ok")?.addEventListener("click", () => $("#tx-modal")?.classList.remove("open"));
|
||||
$("#tx-modal")?.addEventListener("click", (event) => { if (event.target === event.currentTarget) event.currentTarget.classList.remove("open"); });
|
||||
}
|
||||
|
||||
function resetUpload() {
|
||||
|
||||
@@ -312,7 +312,7 @@ p { margin: 0; }
|
||||
.field .hint.error { color: var(--danger); }
|
||||
|
||||
/* ─── Tabs ──────────────────────────────────────────────────────── */
|
||||
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
|
||||
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; overflow-x: auto; }
|
||||
.tabs button {
|
||||
background: none; border: 0;
|
||||
padding: 9px 14px;
|
||||
@@ -320,6 +320,8 @@ p { margin: 0; }
|
||||
color: var(--muted);
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
flex: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tabs button:hover { color: var(--fg); }
|
||||
.tabs button.active { color: var(--fg); border-bottom-color: var(--accent); }
|
||||
@@ -332,7 +334,7 @@ p { margin: 0; }
|
||||
.tabs button.active .tab-count { background: var(--accent-soft); color: var(--accent); }
|
||||
|
||||
/* ─── 时间轴(账期) ─────────────────────────────────────────────── */
|
||||
.timeline { display: flex; gap: 0; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); }
|
||||
.timeline { display: flex; gap: 0; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); padding: 0; }
|
||||
.tl-cell { flex: 1; padding: 12px 10px; border-right: 1px solid var(--border); text-align: center; min-width: 0; }
|
||||
.tl-cell:last-child { border-right: 0; }
|
||||
.tl-cell .tl-month { font-family: var(--font-mono); font-size: 11px; color: var(--muted); letter-spacing: 0.04em; }
|
||||
|
||||
Reference in New Issue
Block a user