update
This commit is contained in:
parent
8ea251b144
commit
bd716b467d
800
docs/财务结算中心.html
Normal file
800
docs/财务结算中心.html
Normal file
@ -0,0 +1,800 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>财务结算中心</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #246bfe;
|
||||
--primary-soft: #e9f0ff;
|
||||
--cyan: #00a9d6;
|
||||
--green: #0aa66a;
|
||||
--orange: #f59e0b;
|
||||
--red: #ef4444;
|
||||
--text: #1f2937;
|
||||
--muted: #667085;
|
||||
--line: #dbe7ff;
|
||||
--panel: rgba(255, 255, 255, 0.88);
|
||||
--bg: #f5f9ff;
|
||||
--shadow: 0 18px 50px rgba(36, 107, 254, 0.12);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
color: var(--text);
|
||||
font-family: "Inter", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
|
||||
background:
|
||||
radial-gradient(circle at 12% 10%, rgba(36, 107, 254, 0.14), transparent 30%),
|
||||
radial-gradient(circle at 85% 8%, rgba(0, 169, 214, 0.13), transparent 28%),
|
||||
linear-gradient(135deg, #f7fbff 0%, #eef6ff 48%, #ffffff 100%);
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.58;
|
||||
background-image:
|
||||
linear-gradient(rgba(36, 107, 254, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(36, 107, 254, 0.08) 1px, transparent 1px);
|
||||
background-size: 28px 28px;
|
||||
mask-image: linear-gradient(to bottom, black, transparent 75%);
|
||||
}
|
||||
|
||||
.page {
|
||||
position: relative;
|
||||
width: min(1440px, calc(100% - 40px));
|
||||
margin: 0 auto;
|
||||
padding: 28px 0 44px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 0.8fr;
|
||||
gap: 20px;
|
||||
align-items: stretch;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hero-card,
|
||||
.panel,
|
||||
.metric-card {
|
||||
border: 1px solid rgba(145, 178, 255, 0.46);
|
||||
border-radius: 22px;
|
||||
background: var(--panel);
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.hero-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: -80px;
|
||||
top: -80px;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(36, 107, 254, 0.18), transparent 68%);
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 7px 12px;
|
||||
border: 1px solid rgba(36, 107, 254, 0.18);
|
||||
border-radius: 999px;
|
||||
color: var(--primary);
|
||||
background: rgba(36, 107, 254, 0.08);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--green);
|
||||
box-shadow: 0 0 0 6px rgba(10, 166, 106, 0.14);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 18px 0 10px;
|
||||
font-size: clamp(30px, 4vw, 48px);
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
margin: 0;
|
||||
max-width: 760px;
|
||||
color: var(--muted);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.hero-side {
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.status-chip {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(36, 107, 254, 0.08), rgba(255, 255, 255, 0.88));
|
||||
border: 1px solid rgba(36, 107, 254, 0.12);
|
||||
}
|
||||
|
||||
.status-chip strong {
|
||||
font-size: 20px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 18px;
|
||||
min-height: 116px;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
margin-top: 12px;
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
color: var(--primary);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.metric-foot {
|
||||
margin-top: 8px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 380px 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.panel-title h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
color: var(--primary);
|
||||
background: var(--primary-soft);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
label {
|
||||
color: #344054;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
padding: 0 12px;
|
||||
color: var(--text);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
transition: 0.18s ease;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 4px rgba(36, 107, 254, 0.12);
|
||||
}
|
||||
|
||||
.two {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 42px;
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, var(--primary), #4f8cff);
|
||||
font-weight: 800;
|
||||
box-shadow: 0 10px 24px rgba(36, 107, 254, 0.22);
|
||||
transition: transform 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 14px 30px rgba(36, 107, 254, 0.28);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
color: var(--primary);
|
||||
border: 1px solid rgba(36, 107, 254, 0.22);
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
button.green {
|
||||
background: linear-gradient(135deg, var(--green), #32c28a);
|
||||
}
|
||||
|
||||
button.orange {
|
||||
background: linear-gradient(135deg, var(--orange), #fbbf24);
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
color: var(--muted);
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, var(--primary), var(--cyan));
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
border: 1px solid rgba(145, 178, 255, 0.38);
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 980px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 13px 14px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #edf2ff;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
color: #31507a;
|
||||
background: linear-gradient(180deg, #f6f9ff, #ffffff);
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
padding: 4px 9px;
|
||||
border-radius: 999px;
|
||||
color: var(--primary);
|
||||
background: var(--primary-soft);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.log {
|
||||
min-height: 170px;
|
||||
max-height: 340px;
|
||||
overflow: auto;
|
||||
padding: 14px;
|
||||
border: 1px solid rgba(145, 178, 255, 0.38);
|
||||
border-radius: 16px;
|
||||
background: #fbfdff;
|
||||
color: #344054;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-top: 12px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.hero,
|
||||
.layout,
|
||||
.metrics {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="page">
|
||||
<section class="hero">
|
||||
<div class="hero-card">
|
||||
<span class="eyebrow"><span class="pulse"></span> Finance Settlement Console</span>
|
||||
<h1>财务结算中心</h1>
|
||||
<p>
|
||||
面向供应商与分销商的日结、月结费用查询和结算处理页面。支持汇总查询、账单预览、结算单创建、审批提交、审批回调和结算单追踪。
|
||||
</p>
|
||||
</div>
|
||||
<div class="hero-card hero-side">
|
||||
<div class="status-chip">
|
||||
<span>接口状态</span>
|
||||
<strong id="apiStatus">Ready</strong>
|
||||
</div>
|
||||
<div class="status-chip">
|
||||
<span>当前模式</span>
|
||||
<strong id="modeText">供应商 / 日结</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="metrics">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">销售金额</div>
|
||||
<div class="metric-value" id="salesAmount">0.00000000</div>
|
||||
<div class="metric-foot">sales_amount</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">结算金额</div>
|
||||
<div class="metric-value" id="settlementAmount">0.00000000</div>
|
||||
<div class="metric-foot">settlement_amount</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">平台收入</div>
|
||||
<div class="metric-value" id="platformIncomeAmount">0.00000000</div>
|
||||
<div class="metric-foot">platform_income_amount</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">账单数量</div>
|
||||
<div class="metric-value" id="billCount">0</div>
|
||||
<div class="metric-foot">bill_count</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="layout">
|
||||
<aside class="panel">
|
||||
<div class="panel-title">
|
||||
<h2>查询条件</h2>
|
||||
<span class="badge">Light Tech UI</span>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="field">
|
||||
<label>账本机构 accounting_orgid</label>
|
||||
<input id="accountingOrgid" placeholder="请输入账本机构ID" />
|
||||
</div>
|
||||
|
||||
<div class="two">
|
||||
<div class="field">
|
||||
<label>对手方类型</label>
|
||||
<select id="counterpartyType">
|
||||
<option value="supplier">供应商 supplier</option>
|
||||
<option value="reseller">分销商 reseller</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>账期类型</label>
|
||||
<select id="periodType">
|
||||
<option value="day">日结 day</option>
|
||||
<option value="month">月结 month</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>对手方机构 counterparty_orgid</label>
|
||||
<input id="counterpartyOrgid" placeholder="汇总可不填;预览/创建必填" />
|
||||
</div>
|
||||
|
||||
<div class="two">
|
||||
<div class="field">
|
||||
<label>开始日期</label>
|
||||
<input id="startDate" type="date" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>结束日期</label>
|
||||
<input id="endDate" type="date" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="two">
|
||||
<div class="field">
|
||||
<label>页码</label>
|
||||
<input id="currentPage" type="number" min="1" value="1" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>每页数量</label>
|
||||
<input id="pageSize" type="number" min="1" value="20" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>结算单 ID settlement_id</label>
|
||||
<input id="settlementId" placeholder="提交审批/详情时填写" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>用户 ID userid</label>
|
||||
<input id="userid" placeholder="创建/提交审批时填写" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>审批 ID apv_id / approval_id</label>
|
||||
<input id="approvalId" placeholder="审批回调时填写" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>审批状态</label>
|
||||
<select id="approvalStatus">
|
||||
<option value="agree">agree 审批通过</option>
|
||||
<option value="start">start 审批中</option>
|
||||
<option value="refuse">refuse 拒绝</option>
|
||||
<option value="terminate">terminate 撤销</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button onclick="querySummary()">汇总查询</button>
|
||||
<button class="secondary" onclick="previewSettlement()">预览明细</button>
|
||||
<button class="green" onclick="createSettlement()">创建结算单</button>
|
||||
<button class="orange" onclick="submitSettlement()">提交审批</button>
|
||||
<button class="secondary" onclick="queryList()">结算单列表</button>
|
||||
<button class="secondary" onclick="queryDetail()">结算单详情</button>
|
||||
<button onclick="approvalCallback()">审批回调</button>
|
||||
<button class="secondary" onclick="clearResult()">清空结果</button>
|
||||
</div>
|
||||
|
||||
<div class="hint">
|
||||
推荐流程:汇总查询 -> 预览明细 -> 创建结算单 -> 提交审批 -> 审批回调。接口路径默认按 `/bill/*.dspy` 调用。
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">
|
||||
<h2>结算数据</h2>
|
||||
<span class="badge" id="resultBadge">等待查询</span>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="switchView('table')">数据表格</button>
|
||||
<button class="tab" onclick="switchView('raw')">接口返回</button>
|
||||
</div>
|
||||
|
||||
<div id="tableView" class="table-wrap">
|
||||
<table>
|
||||
<thead id="tableHead">
|
||||
<tr>
|
||||
<th>提示</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tableBody">
|
||||
<tr>
|
||||
<td>请先执行查询</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<pre id="rawView" class="log" style="display: none;">暂无数据</pre>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const API = {
|
||||
summary: "/bill/finance_settlement_summary.dspy",
|
||||
preview: "/bill/finance_settlement_preview.dspy",
|
||||
create: "/bill/finance_settlement_create.dspy",
|
||||
list: "/bill/finance_settlement_list.dspy",
|
||||
detail: "/bill/finance_settlement_detail.dspy",
|
||||
submit: "/bill/finance_settlement_submit.dspy",
|
||||
callback: "/bill/finance_settlement_apv_callback.dspy"
|
||||
};
|
||||
|
||||
const moneyFields = ["sales_amount", "settlement_amount", "platform_income_amount"];
|
||||
let lastResponse = null;
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function value(id) {
|
||||
return $(id).value.trim();
|
||||
}
|
||||
|
||||
function basePayload() {
|
||||
return {
|
||||
accounting_orgid: value("accountingOrgid"),
|
||||
counterparty_type: value("counterpartyType"),
|
||||
period_type: value("periodType"),
|
||||
start_date: value("startDate"),
|
||||
end_date: value("endDate"),
|
||||
period_start: value("startDate"),
|
||||
period_end: value("endDate"),
|
||||
counterparty_orgid: value("counterpartyOrgid"),
|
||||
current_page: Number(value("currentPage") || 1),
|
||||
page_size: Number(value("pageSize") || 20)
|
||||
};
|
||||
}
|
||||
|
||||
function compact(obj) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([, v]) => v !== "" && v !== null && v !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
async function postJson(url, payload) {
|
||||
$("apiStatus").textContent = "Loading";
|
||||
$("resultBadge").textContent = "请求中";
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(compact(payload))
|
||||
});
|
||||
const text = await response.text();
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (error) {
|
||||
return { status: false, msg: "接口未返回 JSON", raw: text };
|
||||
}
|
||||
}
|
||||
|
||||
function setModeText() {
|
||||
const typeText = value("counterpartyType") === "supplier" ? "供应商" : "分销商";
|
||||
const periodText = value("periodType") === "day" ? "日结" : "月结";
|
||||
$("modeText").textContent = `${typeText} / ${periodText}`;
|
||||
}
|
||||
|
||||
function updateMetrics(data) {
|
||||
const summary = data?.summary || data?.data?.summary || {};
|
||||
$("salesAmount").textContent = formatMoney(summary.sales_amount);
|
||||
$("settlementAmount").textContent = formatMoney(summary.settlement_amount);
|
||||
$("platformIncomeAmount").textContent = formatMoney(summary.platform_income_amount);
|
||||
$("billCount").textContent = summary.bill_count ?? 0;
|
||||
}
|
||||
|
||||
function formatMoney(value) {
|
||||
const number = Number(value || 0);
|
||||
return Number.isFinite(number) ? number.toFixed(8) : "0.00000000";
|
||||
}
|
||||
|
||||
function normalizeRows(resp) {
|
||||
const data = resp?.data || {};
|
||||
if (Array.isArray(data.items)) return data.items;
|
||||
if (Array.isArray(data)) return data;
|
||||
if (data.settlement) return [data.settlement, ...(data.items || [])];
|
||||
return [];
|
||||
}
|
||||
|
||||
function render(resp) {
|
||||
lastResponse = resp;
|
||||
$("rawView").textContent = JSON.stringify(resp, null, 2);
|
||||
$("apiStatus").textContent = resp?.status ? "Success" : "Failed";
|
||||
$("resultBadge").textContent = resp?.status ? "请求成功" : "请求失败";
|
||||
updateMetrics(resp?.data || {});
|
||||
|
||||
const rows = normalizeRows(resp);
|
||||
if (!rows.length) {
|
||||
$("tableHead").innerHTML = "<tr><th>提示</th></tr>";
|
||||
$("tableBody").innerHTML = `<tr><td>${escapeHtml(resp?.msg || "暂无数据")}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const columns = Array.from(
|
||||
rows.reduce((set, row) => {
|
||||
Object.keys(flatten(row)).forEach(key => set.add(key));
|
||||
return set;
|
||||
}, new Set())
|
||||
).slice(0, 14);
|
||||
|
||||
$("tableHead").innerHTML = `<tr>${columns.map(col => `<th>${escapeHtml(col)}</th>`).join("")}</tr>`;
|
||||
$("tableBody").innerHTML = rows.map(row => {
|
||||
const flat = flatten(row);
|
||||
return `<tr>${columns.map(col => `<td>${formatCell(col, flat[col])}</td>`).join("")}</tr>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function flatten(obj, prefix = "", output = {}) {
|
||||
Object.entries(obj || {}).forEach(([key, val]) => {
|
||||
const path = prefix ? `${prefix}.${key}` : key;
|
||||
if (val && typeof val === "object" && !Array.isArray(val)) {
|
||||
flatten(val, path, output);
|
||||
} else {
|
||||
output[path] = Array.isArray(val) ? JSON.stringify(val) : val;
|
||||
}
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
function formatCell(key, val) {
|
||||
if (moneyFields.some(field => key.endsWith(field))) {
|
||||
return `<strong>${formatMoney(val)}</strong>`;
|
||||
}
|
||||
if (key.endsWith("status") && val) {
|
||||
return `<span class="tag">${escapeHtml(val)}</span>`;
|
||||
}
|
||||
return escapeHtml(val ?? "");
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value)
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function requireFields(payload, fields) {
|
||||
const missing = fields.filter(field => !payload[field]);
|
||||
if (missing.length) {
|
||||
const resp = { status: false, msg: `缺少参数: ${missing.join(", ")}` };
|
||||
render(resp);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function querySummary() {
|
||||
setModeText();
|
||||
const payload = basePayload();
|
||||
if (!requireFields(payload, ["accounting_orgid", "counterparty_type", "period_type", "start_date", "end_date"])) return;
|
||||
render(await postJson(API.summary, payload));
|
||||
}
|
||||
|
||||
async function previewSettlement() {
|
||||
setModeText();
|
||||
const payload = basePayload();
|
||||
if (!requireFields(payload, ["accounting_orgid", "counterparty_type", "counterparty_orgid", "period_start", "period_end"])) return;
|
||||
render(await postJson(API.preview, payload));
|
||||
}
|
||||
|
||||
async function createSettlement() {
|
||||
setModeText();
|
||||
const payload = { ...basePayload(), userid: value("userid") };
|
||||
if (!requireFields(payload, ["accounting_orgid", "counterparty_type", "counterparty_orgid", "period_start", "period_end"])) return;
|
||||
render(await postJson(API.create, payload));
|
||||
}
|
||||
|
||||
async function queryList() {
|
||||
setModeText();
|
||||
const payload = basePayload();
|
||||
if (!requireFields(payload, ["accounting_orgid"])) return;
|
||||
render(await postJson(API.list, payload));
|
||||
}
|
||||
|
||||
async function queryDetail() {
|
||||
const payload = {
|
||||
settlement_id: value("settlementId"),
|
||||
current_page: Number(value("currentPage") || 1),
|
||||
page_size: Number(value("pageSize") || 100)
|
||||
};
|
||||
if (!requireFields(payload, ["settlement_id"])) return;
|
||||
render(await postJson(API.detail, payload));
|
||||
}
|
||||
|
||||
async function submitSettlement() {
|
||||
const payload = {
|
||||
settlement_id: value("settlementId"),
|
||||
userid: value("userid"),
|
||||
business_name: "财务结算"
|
||||
};
|
||||
if (!requireFields(payload, ["settlement_id", "userid"])) return;
|
||||
render(await postJson(API.submit, payload));
|
||||
}
|
||||
|
||||
async function approvalCallback() {
|
||||
const payload = {
|
||||
apv_id: value("approvalId"),
|
||||
status: value("approvalStatus")
|
||||
};
|
||||
if (!requireFields(payload, ["apv_id", "status"])) return;
|
||||
render(await postJson(API.callback, payload));
|
||||
}
|
||||
|
||||
function switchView(view) {
|
||||
document.querySelectorAll(".tab").forEach(tab => tab.classList.remove("active"));
|
||||
event.target.classList.add("active");
|
||||
$("tableView").style.display = view === "table" ? "block" : "none";
|
||||
$("rawView").style.display = view === "raw" ? "block" : "none";
|
||||
}
|
||||
|
||||
function clearResult() {
|
||||
lastResponse = null;
|
||||
$("rawView").textContent = "暂无数据";
|
||||
$("tableHead").innerHTML = "<tr><th>提示</th></tr>";
|
||||
$("tableBody").innerHTML = "<tr><td>请先执行查询</td></tr>";
|
||||
$("apiStatus").textContent = "Ready";
|
||||
$("resultBadge").textContent = "等待查询";
|
||||
updateMetrics({});
|
||||
}
|
||||
|
||||
setModeText();
|
||||
$("counterpartyType").addEventListener("change", setModeText);
|
||||
$("periodType").addEventListener("change", setModeText);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user