2330 lines
78 KiB
JavaScript
2330 lines
78 KiB
JavaScript
// 氢费(采购端)汇总报表 PRD(与 氢费采购端汇总报表-需求文档.md 同步)
|
||
var H2_PURCHASE_SUMMARY_REQUIREMENT_DOC = `# 氢费(采购端)汇总报表 — 产品需求说明(PRD)
|
||
|
||
| 项目 | 内容 |
|
||
|------|------|
|
||
| 文档版本 | v1.0(业务版) |
|
||
| 产品模块 | 台账数据 → 氢费(采购端)汇总报表 |
|
||
| 文档类型 | 产品需求说明 |
|
||
| 适用读者 | 产品、采购、财务、运营、测试、项目 |
|
||
|
||
---
|
||
|
||
## 一、为什么做这件事
|
||
|
||
### 1.1 业务痛点
|
||
|
||
- 采购侧需按加氢站维度掌握氢费规模、应付与实付、开票与预付余额,现有数据分散在车辆氢费明细、打款、开票等模块,缺少一站汇总视图。
|
||
- 财务对账需快速识别「未付金额」「站点欠费」等风险,并下钻到账单、流水、发票凭据。
|
||
|
||
### 1.2 产品价值
|
||
|
||
| 价值点 | 说明 |
|
||
|--------|------|
|
||
| 站点汇总 | 一行一站,看清加氢量、应付、已付、未付、开票、未开票、当前余额 |
|
||
| 对账效率 | 顶部合计条随筛选即时汇总,支撑采购与财务日常核对 |
|
||
| 可追溯 | 关键金额可钻取明细,付款凭据、发票支持在线预览与下载 |
|
||
| 风险可见 | 未付/未开票金额为正、当前余额为负时突出展示 |
|
||
|
||
### 1.3 本期目标
|
||
|
||
建设 Web 端「氢费(采购端)汇总报表」,按地区、加氢站、结算方式查询,展示站点汇总列表及合计;支持多类钻取与 CSV 导出。
|
||
|
||
### 1.4 本期不做
|
||
|
||
- 报表内直接发起打款、开票(跳转或对接后续迭代)。
|
||
- 跨年度历史数据全量迁移方案(本期以 2026 年起统计口径描述为准,上线以数据准备结果为准)。
|
||
|
||
---
|
||
|
||
## 二、谁在用、用来干什么
|
||
|
||
### 2.1 用户角色
|
||
|
||
| 角色 | 典型诉求 |
|
||
|------|----------|
|
||
| **采购人员** | 按站点查看氢费与支付进度,核对当前余额、欠费站点 |
|
||
| **财务人员** | 核对应付/实付/未付/已开票,查看付款证明与发票附件 |
|
||
| **运营/主管** | 按地区、结算方式浏览整体规模与异常站点 |
|
||
|
||
### 2.2 核心使用场景
|
||
|
||
1. **日常巡检**:按地区或结算方式筛选,查看顶部合计与列表,关注未付、已欠费站点。
|
||
2. **站点下钻**:点击加氢总量、应付总金额等,打开明细弹窗核对构成。
|
||
3. **付款与发票核查**:从已付总金额、已开票金额钻取,预览付款凭据或发票。
|
||
4. **导出报送**:按当前筛选结果导出 CSV。
|
||
|
||
---
|
||
|
||
## 三、页面功能说明
|
||
|
||
### 3.1 页面组成
|
||
|
||
路径:**台账数据 → 氢费(采购端)汇总报表**
|
||
|
||
自上而下:
|
||
|
||
1. **查询条件区** — 地区、加氢站全称、结算方式
|
||
2. **汇总列表区** — 标题、导出、筛选摘要、顶部合计条、站点表格
|
||
3. **弹窗** — 需求说明、各类钻取明细、附件预览
|
||
|
||
面包屑行右上角提供 **「查看需求说明」**(本文档);汇总列表标题右侧提供 **「导出」**。
|
||
|
||
### 3.2 查询条件
|
||
|
||
| 查询项 | 业务说明 |
|
||
|--------|----------|
|
||
| 地区 | 加氢站所属省/市,支持搜索;空为全部 |
|
||
| 加氢站全称 | 从加氢站主数据选择;空为全部 |
|
||
| 结算方式 | 预付 / 月结;空为全部 |
|
||
|
||
**交互:** 修改条件后点击 **「查询」** 生效;**「重置」** 清空条件。查询成功给予简短提示。列表上方展示当前筛选摘要(地区、加氢站、结算方式)。
|
||
|
||
### 3.3 顶部合计条
|
||
|
||
表格上方独立展示(随当前列表筛选结果汇总):
|
||
|
||
| 合计项 | 计算口径 |
|
||
|--------|----------|
|
||
| 加氢总量(kg) | 列表各行加氢总量之和 |
|
||
| 应付总金额(元) | 列表各行应付总金额之和 |
|
||
| 已付总金额(元) | 列表各行已付总金额之和 |
|
||
| 未付总金额(元) | **应付总金额合计 − 已付总金额合计**;大于 0 时红色显示 |
|
||
| 未开票总金额(元) | **应付总金额合计 − 已开票总金额合计**;大于 0 时红色显示 |
|
||
| 充值总额(元) | 列表各行充值总额之和 |
|
||
| 当前总余额(元) | 列表各行当前余额之和;合计为负时红色显示 |
|
||
|
||
### 3.4 汇总列表字段
|
||
|
||
**列顺序(左→右):**
|
||
序号 → 地区 → 加氢站全称 → 结算方式 → 加氢总量(kg) → 应付总金额(元) → 已付总金额(元) → 未付总金额(元) → 已开票金额(元) → 未开票金额(元) → 充值总额(元) → 当前余额(元)
|
||
|
||
**表头列宽:** 支持鼠标拖动表头右侧调整列宽(最小宽度限制),便于长站名与金额列阅读。
|
||
|
||
| 字段 | 业务口径 | 交互 |
|
||
|------|----------|------|
|
||
| 结算方式 | 预付 / 月结 | 只读 |
|
||
| 加氢总量(kg) | 自 **2026 年起** 该站全部氢费加氢量之和 | 点击钻取 **加氢量明细**(仅加氢量,不含金额) |
|
||
| 应付总金额(元) | 该站「车辆氢费明细」**加氢总价(元)** 求和 | 点击钻取明细(加氢量、成本单价、成本总价) |
|
||
| 已付总金额(元) | 加氢站打款管理中 **已支付金额** 求和 | 点击钻取支付账单明细 |
|
||
| 未付总金额(元) | **应付总金额 − 已付总金额**(行内计算) | 只读;大于 0 红色 |
|
||
| 已开票金额(元) | 该站已开票金额合计 | 点击钻取开票记录 |
|
||
| 未开票金额(元) | **应付总金额 − 已开票金额**(行内计算) | 只读;大于 0 红色 |
|
||
| 充值总额(元) | 该站预充值(打款入账)金额合计 | 点击钻取充值明细 |
|
||
| 当前余额(元) | 2026 年期初余额,扣减 2025 年及以前未扣款记录后的实时余额 | 点击钻取余额变更明细;**为负** 时金额红色 + 左侧 **「已欠费」** 标签 |
|
||
|
||
### 3.5 钻取明细说明
|
||
|
||
#### (1)加氢量明细(由加氢总量进入)
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 序号、加氢时间、订单编号、车牌号、加氢量(kg) | 来源于车辆氢费明细,按当前加氢站筛选 |
|
||
| 合计 | 加氢量合计 |
|
||
|
||
#### (2)应付总金额明细
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 序号、加氢时间、订单编号、车牌号、加氢量(kg)、成本单价(元/kg)、成本总价(元) | 按站筛选的车辆氢费明细 |
|
||
| 合计 | 加氢量、成本总价;弹窗标题含站名 |
|
||
|
||
#### (3)已付总金额明细(由已付总金额进入)
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 加氢站全称、账单开始时间、账单结束时间、应付总金额(元)、已付总金额(元)、银行付款证明 | 按账单维度展示 |
|
||
| 银行付款证明 | **查看付款凭据** — 预览付款证明图片 |
|
||
| 合计 | 应付、实付合计 |
|
||
|
||
#### (4)已开票明细(由已开票金额进入)
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 加氢站全称、开票时间、开票金额(元)、发票 | |
|
||
| 发票 | **查看发票** — 在线预览 PDF/图片;**下载发票** — 下载 PDF/图片文件 |
|
||
| 合计 | 开票金额合计 |
|
||
|
||
#### (5)充值总额明细(由充值总额进入)
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 加氢站全称、支付时间、预充金额(元)、付款凭证 | 支付时间为 **YYYY-MM-DD** |
|
||
| 付款凭证 | **预览** — 在线查看;**下载** — 下载凭证文件 |
|
||
| 合计 | 预充金额(充值总额)合计 |
|
||
|
||
#### (6)余额变更明细(由当前余额进入)
|
||
|
||
| 列 | 说明 |
|
||
|----|------|
|
||
| 加氢站全称、收入金额(元)、支出金额(元)、余额(元)、订单编号 | 收入/支出为空显示「—」;余额为负红色 |
|
||
| 合计 | 收入合计、支出合计、末行余额(与列表该行当前余额一致) |
|
||
|
||
### 3.6 附件预览
|
||
|
||
- 付款凭据、发票在弹窗中预览;PDF 使用文档预览,图片直接展示。
|
||
- 发票支持下载,文件名与开票记录一致。
|
||
|
||
### 3.7 导出
|
||
|
||
- 点击 **「导出」** 下载当前筛选结果 CSV(含列表字段及合计行)。
|
||
- 编码 UTF-8(带 BOM),便于 Excel 打开。
|
||
|
||
---
|
||
|
||
## 四、业务规则摘要
|
||
|
||
| 规则 | 说明 |
|
||
|------|------|
|
||
| 统计起点 | 加氢总量等业务口径默认 **2026 年起**(与车辆氢费明细、主数据生效规则一致) |
|
||
| 未付(行) | 应付总金额 − 已付总金额 |
|
||
| 未付(合计条) | 各站未付之和,等价于应付合计 − 已付合计 |
|
||
| 未开票(行) | 应付总金额 − 已开票金额 |
|
||
| 未开票(合计条) | 各站未开票之和,等价于应付合计 − 已开票合计 |
|
||
| 已欠费 | 仅 **当前余额 < 0** 时展示标签 |
|
||
| 钻取范围 | 均限定为 **当前汇总行对应加氢站** |
|
||
|
||
---
|
||
|
||
## 五、验收要点(业务)
|
||
|
||
1. 查询、重置、筛选摘要、合计条与列表数据一致。
|
||
2. 各可点击金额/加氢量钻取弹窗字段与上文一致,合计正确。
|
||
3. 未付、已欠费展示规则符合第四节。
|
||
4. 付款凭据可预览;发票可预览与下载。
|
||
5. 列宽可拖动;导出字段完整。
|
||
6. 「查看需求说明」可打开本 PRD 全文。
|
||
|
||
---
|
||
|
||
**文档结束**
|
||
`;
|
||
|
||
/** PRD Markdown 渲染(氢费采购端汇总报表) */
|
||
function parsePurchasePrdInlineText(text) {
|
||
var parts = String(text || '').split(/(\*\*[^*]+\*\*)/g);
|
||
var nodes = [];
|
||
var i;
|
||
for (i = 0; i < parts.length; i++) {
|
||
var p = parts[i];
|
||
if (!p) continue;
|
||
if (p.indexOf('**') === 0 && p.lastIndexOf('**') === p.length - 2) {
|
||
nodes.push(React.createElement('strong', { key: i }, p.slice(2, -2)));
|
||
} else {
|
||
nodes.push(p);
|
||
}
|
||
}
|
||
return nodes.length === 1 ? nodes[0] : nodes;
|
||
}
|
||
function isPurchasePrdTableRow(line) {
|
||
return /^\|.+\|$/.test(String(line || '').trim());
|
||
}
|
||
function isPurchasePrdTableSep(line) {
|
||
return /^\|[\s\-:|]+\|$/.test(String(line || '').trim());
|
||
}
|
||
function renderPurchasePrdTableRow(line, rowKey, isHeader) {
|
||
var cells = String(line).trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(function (c) { return c.trim(); });
|
||
return React.createElement('tr', { key: rowKey }, cells.map(function (cell, ci) {
|
||
var Tag = isHeader ? 'th' : 'td';
|
||
return React.createElement(Tag, {
|
||
key: ci,
|
||
style: { border: '1px solid #e5e7eb', padding: '8px 10px', textAlign: 'left', verticalAlign: 'top', fontWeight: isHeader ? 600 : 400, background: isHeader ? '#f8fafc' : '#fff', fontSize: 13, lineHeight: 1.5 }
|
||
}, parsePurchasePrdInlineText(cell));
|
||
}));
|
||
}
|
||
function renderPurchasePrdMarkdown(markdown) {
|
||
var lines = String(markdown || '').split(/\r?\n/);
|
||
var nodes = [];
|
||
var i = 0;
|
||
var inCode = false;
|
||
var codeBuf = [];
|
||
while (i < lines.length) {
|
||
var line = lines[i];
|
||
var trimmed = String(line || '').trim();
|
||
if (trimmed.indexOf('```') === 0) {
|
||
if (inCode) {
|
||
nodes.push(React.createElement('pre', { key: 'code-' + i, style: { margin: '12px 0', padding: '12px 14px', background: '#f6f8fa', border: '1px solid #e5e7eb', borderRadius: 8, fontSize: 12, lineHeight: 1.6, overflow: 'auto', color: '#334155', whiteSpace: 'pre-wrap' } }, codeBuf.join('\n')));
|
||
codeBuf = [];
|
||
inCode = false;
|
||
} else inCode = true;
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (inCode) { codeBuf.push(line); i += 1; continue; }
|
||
if (trimmed === '---') {
|
||
nodes.push(React.createElement('hr', { key: 'hr-' + i, style: { border: 'none', borderTop: '1px solid #e8ecf0', margin: '20px 0' } }));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (isPurchasePrdTableRow(trimmed)) {
|
||
var tableLines = [];
|
||
while (i < lines.length && isPurchasePrdTableRow(String(lines[i]).trim())) {
|
||
tableLines.push(String(lines[i]).trim());
|
||
i += 1;
|
||
}
|
||
var bodyRows = [];
|
||
var ti;
|
||
for (ti = 0; ti < tableLines.length; ti++) {
|
||
if (isPurchasePrdTableSep(tableLines[ti])) continue;
|
||
bodyRows.push(renderPurchasePrdTableRow(tableLines[ti], 'tr-' + i + '-' + ti, ti === 0));
|
||
}
|
||
if (bodyRows.length) {
|
||
nodes.push(React.createElement('div', { key: 'tbl-' + i, style: { overflowX: 'auto', margin: '12px 0 16px' } },
|
||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
|
||
React.createElement('tbody', null, bodyRows))));
|
||
}
|
||
continue;
|
||
}
|
||
if (!trimmed) { i += 1; continue; }
|
||
if (trimmed.indexOf('# ') === 0) {
|
||
nodes.push(React.createElement('h1', { key: 'h1-' + i, style: { fontSize: 20, fontWeight: 700, color: '#0f172a', margin: '0 0 16px', lineHeight: 1.35 } }, parsePurchasePrdInlineText(trimmed.slice(2).trim())));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (trimmed.indexOf('## ') === 0) {
|
||
nodes.push(React.createElement('h2', { key: 'h2-' + i, style: { fontSize: 16, fontWeight: 700, color: '#1e293b', margin: '24px 0 12px', paddingBottom: 6, borderBottom: '2px solid #e0f2fe', lineHeight: 1.4 } }, parsePurchasePrdInlineText(trimmed.slice(3).trim())));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (trimmed.indexOf('### ') === 0) {
|
||
nodes.push(React.createElement('h3', { key: 'h3-' + i, style: { fontSize: 14, fontWeight: 600, color: '#334155', margin: '16px 0 8px', lineHeight: 1.45 } }, parsePurchasePrdInlineText(trimmed.slice(4).trim())));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (trimmed === '**文档结束**') {
|
||
nodes.push(React.createElement('div', { key: 'end-' + i, style: { marginTop: 24, paddingTop: 16, borderTop: '1px dashed #e2e8f0', color: '#94a3b8', fontSize: 13, textAlign: 'center' } }, '— 文档结束 —'));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (/^\d+\.\s/.test(trimmed)) {
|
||
nodes.push(React.createElement('div', { key: 'ol-' + i, style: { fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '6px 0 6px 4px', paddingLeft: 4 } }, parsePurchasePrdInlineText(trimmed)));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (trimmed.indexOf('- ') === 0) {
|
||
nodes.push(React.createElement('div', { key: 'ul-' + i, style: { display: 'flex', gap: 8, fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '4px 0 4px 2px' } },
|
||
React.createElement('span', { style: { color: '#1677ff', flexShrink: 0 } }, '•'),
|
||
React.createElement('span', { style: { flex: 1 } }, parsePurchasePrdInlineText(trimmed.slice(2).trim()))));
|
||
i += 1;
|
||
continue;
|
||
}
|
||
nodes.push(React.createElement('p', { key: 'p-' + i, style: { fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '6px 0' } }, parsePurchasePrdInlineText(trimmed)));
|
||
i += 1;
|
||
}
|
||
return nodes;
|
||
}
|
||
function renderPurchaseRequirementDocPanel() {
|
||
var md = typeof H2_PURCHASE_SUMMARY_REQUIREMENT_DOC !== 'undefined' && H2_PURCHASE_SUMMARY_REQUIREMENT_DOC ? H2_PURCHASE_SUMMARY_REQUIREMENT_DOC : '';
|
||
if (!md) {
|
||
return React.createElement('div', { style: { padding: 24, color: '#64748b', textAlign: 'center' } }, '需求文档未加载');
|
||
}
|
||
return React.createElement('div', { className: 'h2-req-doc-panel', style: { padding: '4px 4px 16px' } }, renderPurchasePrdMarkdown(md));
|
||
}
|
||
|
||
|
||
// 【重要】必须使用 const Component 作为组件变量名
|
||
// 台账数据 - 氢费(采购端)汇总报表
|
||
|
||
const Component = function () {
|
||
var useState = React.useState;
|
||
var useMemo = React.useMemo;
|
||
var useCallback = React.useCallback;
|
||
|
||
var antd = window.antd;
|
||
var App = antd.App;
|
||
var Breadcrumb = antd.Breadcrumb;
|
||
var Card = antd.Card;
|
||
var Button = antd.Button;
|
||
var Table = antd.Table;
|
||
var Select = antd.Select;
|
||
var Row = antd.Row;
|
||
var Col = antd.Col;
|
||
var Space = antd.Space;
|
||
var Tag = antd.Tag;
|
||
var Tooltip = antd.Tooltip;
|
||
var Modal = antd.Modal;
|
||
var message = antd.message;
|
||
|
||
var SETTLEMENT_OPTIONS = [
|
||
{ value: '预付', label: '预付' },
|
||
{ value: '月结', label: '月结' }
|
||
];
|
||
|
||
/** 原型:加氢站主数据(联调后由接口加载) */
|
||
var STATION_MASTER = [
|
||
{ code: 'HS000059', name: '佛山南海羚牛加氢站', region: '广东省/佛山市', project: '佛山氢能城配', settlement: '预付' },
|
||
{ code: 'HS000035', name: '广州联新氢能东晖加氢站', region: '广东省/广州市', project: '广州干线运输', settlement: '月结' },
|
||
{ code: '000069', name: '中国石化广州开泰北加油加氢站', region: '广东省/广州市', project: '广州干线运输', settlement: '月结' },
|
||
{ code: '000072', name: '中国石化广州金坑加氢站', region: '广东省/广州市', project: '广州冷链专线', settlement: '预付' },
|
||
{ code: 'HS000030', name: '大兴国际氢能示范区海珀尔加氢站', region: '北京市/大兴区', project: '北京示范运营', settlement: '预付' },
|
||
{ code: 'HS000007', name: '上海嘉氢实业加氢站(江桥重塑)', region: '上海市/嘉定区', project: '长三角城际', settlement: '月结' },
|
||
{ code: 'HS000096', name: '松江九亭加油站', region: '上海市/松江区', project: '长三角城际', settlement: '月结' },
|
||
{ code: 'HS000041', name: '宿迁沭阳开发区加氢站', region: '江苏省/宿迁市', project: '苏北物流', settlement: '预付' },
|
||
{ code: 'HS000040', name: '扬州文昌西路站', region: '江苏省/扬州市', project: '苏北物流', settlement: '月结' },
|
||
{ code: '000048', name: '南京溧水柘塘东站', region: '江苏省/南京市', project: '南京港口短驳', settlement: '预付' },
|
||
{ code: 'HS000051', name: '成都华通加氢站', region: '四川省/成都市', project: '西南干线', settlement: '月结' },
|
||
{ code: '000090', name: '成都天府机场高速北站', region: '四川省/成都市', project: '西南干线', settlement: '预付' },
|
||
{ code: 'HS000057', name: '重庆元琨双宝氢能综合能源站', region: '重庆市/渝北区', project: '西南干线', settlement: '月结' },
|
||
{ code: '000091', name: '重庆双溪加氢站', region: '重庆市/涪陵区', project: '西南干线', settlement: '预付' },
|
||
{ code: '000053', name: '武汉革新大道加油站', region: '湖北省/武汉市', project: '华中城配', settlement: '月结' },
|
||
{ code: 'HS000081', name: '武汉群力加油站', region: '湖北省/武汉市', project: '华中城配', settlement: '预付' },
|
||
{ code: '000049', name: '宁波镇海区中国石化加氢站', region: '浙江省/宁波市', project: '浙江氢能租赁', settlement: '月结' },
|
||
{ code: 'HS000086', name: '空气化工产品(浙江)有限公司海盐经济开发区加氢站', region: '浙江省/嘉兴市', project: '浙江氢能租赁', settlement: '预付' },
|
||
{ code: 'HS000039', name: '海盐JUPITER厂内加氢站', region: '浙江省/嘉兴市', project: '园区通勤', settlement: '预付' },
|
||
{ code: '000043', name: '豪汇综合能源站', region: '山东省/淄博市', project: '鲁中物流', settlement: '月结' }
|
||
];
|
||
|
||
var REGION_OPTIONS = (function () {
|
||
var map = {};
|
||
STATION_MASTER.forEach(function (s) {
|
||
map[s.region] = s.region;
|
||
});
|
||
return Object.keys(map).sort().map(function (r) {
|
||
return { value: r, label: r };
|
||
});
|
||
})();
|
||
|
||
var STATION_OPTIONS = STATION_MASTER.map(function (s) {
|
||
return { value: s.code, label: s.name };
|
||
});
|
||
|
||
function filterOption(input, option) {
|
||
var label = (option && (option.label || option.children)) || '';
|
||
return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0;
|
||
}
|
||
|
||
function numOrZero(v) {
|
||
if (v === null || v === undefined || v === '') return 0;
|
||
var n = Number(v);
|
||
return isNaN(n) ? 0 : n;
|
||
}
|
||
|
||
function fmtMoney(n, digits) {
|
||
if (n === null || n === undefined || n === '') return '-';
|
||
var x = Number(n);
|
||
if (isNaN(x)) return '-';
|
||
var d = digits === undefined ? 2 : digits;
|
||
return x.toLocaleString('zh-CN', { minimumFractionDigits: d, maximumFractionDigits: d });
|
||
}
|
||
|
||
function fmtQty(n) {
|
||
if (n === null || n === undefined || n === '') return '-';
|
||
var x = Number(n);
|
||
if (isNaN(x)) return '-';
|
||
return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
}
|
||
|
||
function escapeCsv(v) {
|
||
var s = v == null ? '' : String(v);
|
||
if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1 || s.indexOf('\r') !== -1) {
|
||
return '"' + s.replace(/"/g, '""') + '"';
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function downloadCsv(filename, lines) {
|
||
var csv = lines.map(function (row) { return row.map(escapeCsv).join(','); }).join('\n');
|
||
var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' });
|
||
var url = URL.createObjectURL(blob);
|
||
var a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = filename;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
function findStation(code) {
|
||
for (var i = 0; i < STATION_MASTER.length; i++) {
|
||
if (STATION_MASTER[i].code === code) return STATION_MASTER[i];
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function formatDateTime(d) {
|
||
if (!d || !window.dayjs) return '—';
|
||
try {
|
||
var x = window.dayjs(d);
|
||
return x.isValid() ? x.format('YYYY-MM-DD HH:mm:ss') : '—';
|
||
} catch (eDt) {
|
||
return '—';
|
||
}
|
||
}
|
||
|
||
function formatDate(d) {
|
||
if (!d || !window.dayjs) return '—';
|
||
try {
|
||
var x = window.dayjs(d);
|
||
return x.isValid() ? x.format('YYYY-MM-DD') : '—';
|
||
} catch (eD) {
|
||
return '—';
|
||
}
|
||
}
|
||
|
||
var MOCK_SAMPLE_PDF_URL =
|
||
'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';
|
||
|
||
var MOCK_PLATES = ['粤A12345F', '浙AD12345F', '沪A06695F', '苏EF99887F', '京CN88771F', '渝A88888F'];
|
||
|
||
/**
|
||
* 原型:车辆氢费明细钻取数据(联调后按加氢站 + 汇总条件查询车辆氢费明细接口)
|
||
* payable:应付总金额 = 该站车辆氢费明细「加氢总价」求和
|
||
*/
|
||
function buildMockStationDetailRows(summaryRow, drillType) {
|
||
var seed = 0;
|
||
var code = summaryRow.stationCode || '';
|
||
var i;
|
||
for (i = 0; i < code.length; i++) seed += code.charCodeAt(i);
|
||
var lineCount = Math.max(4, 5 + (seed % 6));
|
||
var rows = [];
|
||
var unitPrice = numOrZero(summaryRow.unitPrice) || 28;
|
||
var baseDay = window.dayjs ? window.dayjs('2026-05-01') : null;
|
||
|
||
if (drillType === 'hydrogenKg') {
|
||
var totalKg = numOrZero(summaryRow.hydrogenKg);
|
||
var remainKg = totalKg;
|
||
for (i = 0; i < lineCount; i++) {
|
||
var isLast = i === lineCount - 1;
|
||
var kg = isLast
|
||
? Math.round(remainKg * 100) / 100
|
||
: Math.round((totalKg / lineCount) * (0.82 + ((seed + i) % 4) * 0.06) * 100) / 100;
|
||
if (!isLast) remainKg -= kg;
|
||
var costTotal = Math.round(kg * unitPrice * 100) / 100;
|
||
var customerAmount = Math.round(kg * (unitPrice + 2) * 100) / 100;
|
||
var day = baseDay && baseDay.subtract ? baseDay.subtract((seed + i) % 40, 'day') : null;
|
||
rows.push({
|
||
key: code + '-kg-' + i,
|
||
seq: i + 1,
|
||
hydrogenTime: day,
|
||
orderNo: 'JQ' + code + (day && day.format ? day.format('YYMMDD') : '') + String(i + 1).padStart(5, '0'),
|
||
plateNo: MOCK_PLATES[(seed + i) % MOCK_PLATES.length],
|
||
hydrogenKg: kg,
|
||
costUnitPrice: unitPrice,
|
||
costTotal: costTotal,
|
||
customerAmount: customerAmount
|
||
});
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
if (drillType === 'payableAmount') {
|
||
var totalPay = numOrZero(summaryRow.payableAmount);
|
||
var remainPay = totalPay;
|
||
for (i = 0; i < lineCount; i++) {
|
||
var isLast = i === lineCount - 1;
|
||
var customerAmount = isLast
|
||
? Math.round(remainPay * 100) / 100
|
||
: Math.round((totalPay / lineCount) * (0.8 + ((seed + i) % 5) * 0.05) * 100) / 100;
|
||
if (!isLast) remainPay -= customerAmount;
|
||
var kg = unitPrice > 0 ? Math.round((customerAmount / (unitPrice + 2)) * 100) / 100 : 0;
|
||
var costTotal = Math.round(kg * unitPrice * 100) / 100;
|
||
var day = baseDay && baseDay.subtract ? baseDay.subtract((seed + i) % 40, 'day') : null;
|
||
rows.push({
|
||
key: code + '-pay-' + i,
|
||
seq: i + 1,
|
||
hydrogenTime: day,
|
||
orderNo: 'JQ' + code + (day && day.format ? day.format('YYMMDD') : '') + String(100 + i).padStart(5, '0'),
|
||
plateNo: MOCK_PLATES[(seed + i + 2) % MOCK_PLATES.length],
|
||
hydrogenKg: kg,
|
||
costUnitPrice: unitPrice,
|
||
costTotal: costTotal,
|
||
customerAmount: customerAmount
|
||
});
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
return rows;
|
||
}
|
||
|
||
/**
|
||
* 原型:加氢站预付余额变更明细(联调后对接加氢站打款/余额流水接口)
|
||
*/
|
||
function buildMockPrepaidBalanceRows(summaryRow) {
|
||
var seed = 0;
|
||
var code = summaryRow.stationCode || '';
|
||
var i;
|
||
for (i = 0; i < code.length; i++) seed += code.charCodeAt(i);
|
||
var stationName = summaryRow.stationName || '—';
|
||
var finalBalance = numOrZero(summaryRow.prepaidBalance);
|
||
var lineCount = Math.max(5, 6 + (seed % 5));
|
||
var rows = [];
|
||
var openingBalance = Math.round((finalBalance + 38000 + seed * 620) * 100) / 100;
|
||
if (openingBalance < 0) openingBalance = Math.abs(openingBalance) + 50000;
|
||
|
||
rows.push({
|
||
key: code + '-bal-0',
|
||
stationName: stationName,
|
||
incomeAmount: openingBalance,
|
||
expenseAmount: null,
|
||
balance: openingBalance,
|
||
orderNo: 'QC' + code + '260101'
|
||
});
|
||
|
||
var balance = openingBalance;
|
||
for (i = 1; i < lineCount - 1; i++) {
|
||
var isIncome = (seed + i) % 3 === 0;
|
||
var incomeAmount = null;
|
||
var expenseAmount = null;
|
||
if (isIncome) {
|
||
incomeAmount = Math.round((6000 + (seed + i) * 380) * 100) / 100;
|
||
balance = Math.round((balance + incomeAmount) * 100) / 100;
|
||
} else {
|
||
expenseAmount = Math.round((2800 + (seed + i) * 210) * 100) / 100;
|
||
balance = Math.round((balance - expenseAmount) * 100) / 100;
|
||
}
|
||
rows.push({
|
||
key: code + '-bal-' + i,
|
||
stationName: stationName,
|
||
incomeAmount: incomeAmount,
|
||
expenseAmount: expenseAmount,
|
||
balance: balance,
|
||
orderNo: (isIncome ? 'DK' : 'JQ') + code + String(200 + i).padStart(5, '0')
|
||
});
|
||
}
|
||
|
||
var diff = Math.round((finalBalance - balance) * 100) / 100;
|
||
if (Math.abs(diff) < 0.01) {
|
||
rows.push({
|
||
key: code + '-bal-last',
|
||
stationName: stationName,
|
||
incomeAmount: null,
|
||
expenseAmount: null,
|
||
balance: finalBalance,
|
||
orderNo: 'BZ' + code + String(900).padStart(5, '0')
|
||
});
|
||
} else if (diff > 0) {
|
||
rows.push({
|
||
key: code + '-bal-last',
|
||
stationName: stationName,
|
||
incomeAmount: diff,
|
||
expenseAmount: null,
|
||
balance: finalBalance,
|
||
orderNo: 'DK' + code + String(999).padStart(5, '0')
|
||
});
|
||
} else {
|
||
rows.push({
|
||
key: code + '-bal-last',
|
||
stationName: stationName,
|
||
incomeAmount: null,
|
||
expenseAmount: Math.round(Math.abs(diff) * 100) / 100,
|
||
balance: finalBalance,
|
||
orderNo: 'JQ' + code + String(999).padStart(5, '0')
|
||
});
|
||
}
|
||
|
||
return rows;
|
||
}
|
||
|
||
/** 原型:充值总额钻取(联调后对接加氢站预充值记录接口) */
|
||
function buildMockRechargeDrillRows(summaryRow) {
|
||
var seed = 0;
|
||
var code = summaryRow.stationCode || '';
|
||
var i;
|
||
for (i = 0; i < code.length; i++) seed += code.charCodeAt(i);
|
||
var stationName = summaryRow.stationName || '—';
|
||
var totalRecharge = numOrZero(summaryRow.rechargeTotal);
|
||
var rechargeCount = Math.max(2, 4 + (seed % 4));
|
||
var rows = [];
|
||
var remain = totalRecharge;
|
||
var baseDay = window.dayjs ? window.dayjs('2026-01-15') : null;
|
||
|
||
for (i = 0; i < rechargeCount; i++) {
|
||
var isLast = i === rechargeCount - 1;
|
||
var prepayAmount = isLast
|
||
? Math.round(remain * 100) / 100
|
||
: Math.round((totalRecharge / rechargeCount) * (0.75 + ((seed + i) % 5) * 0.05) * 100) / 100;
|
||
if (!isLast) remain -= prepayAmount;
|
||
var payDay = baseDay && baseDay.add ? baseDay.add((seed + i) * 17, 'day') : null;
|
||
var isPdf = (seed + i) % 3 === 0;
|
||
rows.push({
|
||
key: code + '-rc-' + i,
|
||
stationName: stationName,
|
||
payTime: payDay,
|
||
prepayAmount: prepayAmount,
|
||
voucherUrl: isPdf
|
||
? MOCK_SAMPLE_PDF_URL
|
||
: 'https://picsum.photos/seed/rc-' + code + '-' + i + '/960/640',
|
||
voucherFileName: '付款凭证_' + code + '_' + String(i + 1).padStart(2, '0') + (isPdf ? '.pdf' : '.jpg'),
|
||
voucherMime: isPdf ? 'pdf' : 'image'
|
||
});
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
/** 原型:采购支付钻取(联调后对接加氢站打款账单接口) */
|
||
function buildMockPaymentDrillRows(summaryRow) {
|
||
var seed = 0;
|
||
var code = summaryRow.stationCode || '';
|
||
var i;
|
||
for (i = 0; i < code.length; i++) seed += code.charCodeAt(i);
|
||
var stationName = summaryRow.stationName || '—';
|
||
var totalPaid = numOrZero(summaryRow.paidAmount);
|
||
var billCount = Math.max(2, 3 + (seed % 3));
|
||
var rows = [];
|
||
var remainPaid = totalPaid;
|
||
var baseMonth = window.dayjs ? window.dayjs('2026-01-01') : null;
|
||
|
||
for (i = 0; i < billCount; i++) {
|
||
var isLast = i === billCount - 1;
|
||
var paidPart = isLast
|
||
? Math.round(remainPaid * 100) / 100
|
||
: Math.round((totalPaid / billCount) * (0.78 + ((seed + i) % 4) * 0.06) * 100) / 100;
|
||
if (!isLast) remainPaid -= paidPart;
|
||
var payablePart = Math.round(paidPart * (1.04 + ((seed + i) % 3) * 0.02) * 100) / 100;
|
||
var start = baseMonth && baseMonth.add ? baseMonth.add(i * 2, 'month').startOf('month') : null;
|
||
var end = start && start.endOf ? start.endOf('month') : null;
|
||
rows.push({
|
||
key: code + '-payment-' + i,
|
||
stationName: stationName,
|
||
billStartDate: start,
|
||
billEndDate: end,
|
||
payableAmount: payablePart,
|
||
paidAmount: paidPart,
|
||
voucherUrl: 'https://picsum.photos/seed/pay-' + code + '-' + i + '/960/640',
|
||
voucherName: '付款凭据_' + code + '_' + String(i + 1).padStart(2, '0') + '.jpg',
|
||
voucherMime: 'image'
|
||
});
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
/** 原型:已开票金额钻取(联调后对接开票记录接口) */
|
||
function buildMockInvoiceDrillRows(summaryRow) {
|
||
var seed = 0;
|
||
var code = summaryRow.stationCode || '';
|
||
var i;
|
||
for (i = 0; i < code.length; i++) seed += code.charCodeAt(i);
|
||
var stationName = summaryRow.stationName || '—';
|
||
var totalInvoiced = numOrZero(summaryRow.invoicedAmount);
|
||
var invoiceCount = Math.max(2, 3 + (seed % 3));
|
||
var rows = [];
|
||
var remainInv = totalInvoiced;
|
||
var baseDay = window.dayjs ? window.dayjs('2026-02-01') : null;
|
||
|
||
for (i = 0; i < invoiceCount; i++) {
|
||
var isLast = i === invoiceCount - 1;
|
||
var amount = isLast
|
||
? Math.round(remainInv * 100) / 100
|
||
: Math.round((totalInvoiced / invoiceCount) * (0.8 + ((seed + i) % 4) * 0.05) * 100) / 100;
|
||
if (!isLast) remainInv -= amount;
|
||
var isPdf = (seed + i) % 2 === 0;
|
||
var invDay = baseDay && baseDay.add ? baseDay.add((seed + i) * 11, 'day') : null;
|
||
rows.push({
|
||
key: code + '-inv-' + i,
|
||
stationName: stationName,
|
||
invoiceTime: invDay,
|
||
invoiceAmount: amount,
|
||
invoiceUrl: isPdf
|
||
? MOCK_SAMPLE_PDF_URL
|
||
: 'https://picsum.photos/seed/inv-' + code + '-' + i + '/860/1200',
|
||
invoiceFileName: '发票_' + code + '_' + String(i + 1).padStart(2, '0') + (isPdf ? '.pdf' : '.jpg'),
|
||
invoiceMime: isPdf ? 'pdf' : 'image'
|
||
});
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
function fmtLedgerMoney(v) {
|
||
if (v === null || v === undefined || v === '' || Number(v) === 0) return '—';
|
||
return fmtMoney(v, 2);
|
||
}
|
||
|
||
/** 原型:按站点生成汇总行(联调后由后端聚合) */
|
||
function buildMockSummaryRows() {
|
||
return STATION_MASTER.map(function (s, idx) {
|
||
var seed = idx + 1;
|
||
var hydrogenKg = Math.round((1200 + seed * 137.5) * 100) / 100;
|
||
var unitPrice = 28 + (seed % 5) * 0.5;
|
||
/** 应付总金额 = 车辆氢费明细「加氢总价」按站点求和(原型用加氢单价近似) */
|
||
var customerUnitPrice = unitPrice + 2;
|
||
var payable = Math.round(hydrogenKg * customerUnitPrice * 100) / 100;
|
||
var paid = Math.round(payable * (0.55 + (seed % 4) * 0.1) * 100) / 100;
|
||
var unpaid = Math.round((payable - paid) * 100) / 100;
|
||
var invoiced = Math.round(Math.min(payable, paid * (0.72 + (seed % 5) * 0.05)) * 100) / 100;
|
||
var prepaid = Math.round((80000 - seed * 4200 + (seed % 3 === 0 ? -15000 : 0)) * 100) / 100;
|
||
/** 原型:首行演示预付余额为负(已欠费) */
|
||
if (idx === 0) {
|
||
prepaid = -12580.5;
|
||
}
|
||
var rechargeTotal = Math.round((48000 + seed * 3200 + (idx === 0 ? 8000 : 0)) * 100) / 100;
|
||
return {
|
||
key: s.code,
|
||
stationCode: s.code,
|
||
region: s.region,
|
||
stationName: s.name,
|
||
hydrogenKg: hydrogenKg,
|
||
unitPrice: unitPrice,
|
||
payableAmount: payable,
|
||
paidAmount: paid,
|
||
unpaidAmount: unpaid,
|
||
invoicedAmount: invoiced,
|
||
rechargeTotal: rechargeTotal,
|
||
prepaidBalance: prepaid,
|
||
settlement: s.settlement
|
||
};
|
||
});
|
||
}
|
||
|
||
var layoutStyle = {
|
||
padding: '16px 24px 24px',
|
||
minHeight: '100vh',
|
||
background: 'linear-gradient(165deg, #eef4ff 0%, #f5f7fa 42%, #f0f2f5 100%)'
|
||
};
|
||
var filterLabelStyle = { marginBottom: 6, fontSize: 13, color: 'rgba(0,0,0,0.55)', fontWeight: 500 };
|
||
var filterItemStyle = { marginBottom: 12 };
|
||
var filterControlStyle = { width: '100%' };
|
||
var filterActionsColStyle = { flex: '0 0 auto', marginLeft: 'auto' };
|
||
var filterCardStyle = {
|
||
marginBottom: 20,
|
||
borderRadius: 16,
|
||
boxShadow: '0 4px 20px -4px rgba(16,24,40,0.03), 0 0 0 1px rgba(16,24,40,0.06)',
|
||
border: 'none',
|
||
background: '#ffffff'
|
||
};
|
||
var tableCardStyle = {
|
||
borderRadius: 16,
|
||
boxShadow: '0 10px 32px -4px rgba(16,24,40,0.06), 0 0 0 1px rgba(16,24,40,0.04)',
|
||
border: 'none',
|
||
background: '#ffffff',
|
||
overflow: 'hidden'
|
||
};
|
||
var ledgerTableStyle =
|
||
'.h2-purchase-summary-wrap{border-radius:12px;overflow:hidden;box-shadow:0 4px 24px -6px rgba(15,23,42,0.05),0 0 0 1px rgba(22,119,255,0.1)}' +
|
||
'.h2-purchase-summary-wrap .ant-table-wrapper,.h2-purchase-summary-wrap .ant-table-content{overflow-x:hidden!important}' +
|
||
'.h2-purchase-summary-wrap .h2-purchase-summary-table{width:100%!important;table-layout:fixed}' +
|
||
'.h2-purchase-summary-table .ant-table-thead>tr>th{white-space:nowrap;color:#1e293b!important;font-weight:600!important;font-size:13px!important;' +
|
||
'background:#e8f4fc!important;border-bottom:1px solid #bae6fd!important;border-inline-end:1px solid #dbeafe!important;padding:0 8px!important;height:38px!important}' +
|
||
'.h2-purchase-summary-table .ant-table-tbody>tr:not(.ant-table-measure-row)>td{white-space:nowrap;font-variant-numeric:tabular-nums;color:#334155;border-bottom:1px solid #f1f5f9!important;border-inline-end:1px solid #f8fafc!important;padding:0 8px!important;height:38px!important;overflow:hidden!important}' +
|
||
'.h2-purchase-summary-table td.h2-cell-prepaid-balance{overflow:hidden!important}' +
|
||
'.h2-purchase-summary-table .h2-prepaid-balance-cell{display:flex;align-items:center;justify-content:flex-end;gap:4px;max-width:100%;min-width:0;overflow:hidden;box-sizing:border-box}' +
|
||
'.h2-purchase-summary-table .h2-prepaid-balance-cell .h2-prepaid-balance-amount{flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border:none;background:none;padding:0;font:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}' +
|
||
'.h2-purchase-summary-table .h2-prepaid-balance-cell .ant-tag{flex-shrink:0;margin:0!important;font-size:11px;line-height:16px;padding:0 5px}' +
|
||
'.h2-purchase-summary-table .ant-table-tbody>tr.h2-purchase-row:hover>td{background:#f0f9ff!important}' +
|
||
'.h2-purchase-summary-table .ant-table-summary>tr>td{font-weight:700;background:#f8fafc!important;color:#0f172a!important;border-top:2px solid #cbd5e1!important;padding:0 8px!important;height:38px!important}' +
|
||
'.h2-ledger-totals-bar{display:flex;align-items:stretch;gap:0;margin-bottom:10px;border:1px solid #bae6fd;border-radius:10px;overflow:hidden;background:#f8fafc;box-shadow:0 1px 0 rgba(15,23,42,0.04)}' +
|
||
'.h2-ledger-totals-bar__title{display:flex;align-items:center;justify-content:center;min-width:72px;padding:10px 14px;font-size:14px;font-weight:700;color:#0f172a;background:#e8f4fc;border-right:1px solid #bae6fd}' +
|
||
'.h2-ledger-totals-bar__items{display:flex;flex:1;flex-wrap:wrap}' +
|
||
'.h2-ledger-totals-bar__item{flex:1;min-width:140px;padding:8px 16px;border-right:1px solid #e2e8f0;display:flex;flex-direction:column;justify-content:center;gap:4px}' +
|
||
'.h2-ledger-totals-bar__item:last-child{border-right:none}' +
|
||
'.h2-ledger-totals-bar__label{font-size:12px;color:rgba(15,23,42,0.55);font-weight:500;line-height:1.2}' +
|
||
'.h2-ledger-totals-bar__value{font-size:16px;font-weight:700;color:#0f172a;font-variant-numeric:tabular-nums;line-height:1.3}' +
|
||
'.h2-ledger-totals-bar__value.is-warn{color:#cf1322}' +
|
||
'.h2-purchase-summary-table .h2-col-header{position:relative;display:flex;align-items:center;width:100%;height:100%;min-height:38px;padding:0 10px 0 8px;box-sizing:border-box}' +
|
||
'.h2-purchase-summary-table .h2-col-header__text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}' +
|
||
'.h2-purchase-summary-table .h2-col-header__resizer{position:absolute;right:0;top:0;bottom:0;width:8px;cursor:col-resize;z-index:2;touch-action:none}' +
|
||
'.h2-purchase-summary-table .h2-col-header__resizer:hover{background:rgba(22,119,255,0.12)}' +
|
||
'.h2-purchase-summary-table.ant-table-wrapper .ant-table-thead>tr>th{position:relative}' +
|
||
'.h2-req-doc-panel{max-width:100%}' +
|
||
'.h2-req-doc-panel h1:first-child{margin-top:0}';
|
||
|
||
/** 主表默认列宽 */
|
||
var DEFAULT_MAIN_COLUMN_WIDTHS = {
|
||
seq: 48,
|
||
region: 96,
|
||
stationName: 148,
|
||
settlement: 72,
|
||
hydrogenKg: 96,
|
||
payableAmount: 100,
|
||
paidAmount: 100,
|
||
unpaidAmount: 100,
|
||
invoicedAmount: 100,
|
||
uninvoicedAmount: 100,
|
||
rechargeTotal: 100,
|
||
prepaidBalance: 118
|
||
};
|
||
var MIN_COLUMN_WIDTH = 48;
|
||
|
||
var drillLinkStyle = {
|
||
cursor: 'pointer',
|
||
color: '#1677ff',
|
||
border: 'none',
|
||
background: 'none',
|
||
padding: 0,
|
||
font: 'inherit',
|
||
fontVariantNumeric: 'tabular-nums',
|
||
textDecoration: 'underline',
|
||
textUnderlineOffset: 2
|
||
};
|
||
|
||
var regionDraftState = useState(undefined);
|
||
var regionDraft = regionDraftState[0];
|
||
var setRegionDraft = regionDraftState[1];
|
||
var stationDraftState = useState(undefined);
|
||
var stationDraft = stationDraftState[0];
|
||
var setStationDraft = stationDraftState[1];
|
||
var settlementDraftState = useState(undefined);
|
||
var settlementDraft = settlementDraftState[0];
|
||
var setSettlementDraft = settlementDraftState[1];
|
||
|
||
var regionAppliedState = useState(undefined);
|
||
var regionApplied = regionAppliedState[0];
|
||
var setRegionApplied = regionAppliedState[1];
|
||
var stationAppliedState = useState(undefined);
|
||
var stationApplied = stationAppliedState[0];
|
||
var setStationApplied = stationAppliedState[1];
|
||
var settlementAppliedState = useState(undefined);
|
||
var settlementApplied = settlementAppliedState[0];
|
||
var setSettlementApplied = settlementAppliedState[1];
|
||
|
||
var detailDrillModalState = useState({
|
||
open: false,
|
||
type: '',
|
||
stationName: '',
|
||
summary: null,
|
||
rows: []
|
||
});
|
||
var detailDrillModal = detailDrillModalState[0];
|
||
var setDetailDrillModal = detailDrillModalState[1];
|
||
|
||
var prepaidBalanceDrillModalState = useState({
|
||
open: false,
|
||
stationName: '',
|
||
endingBalance: 0,
|
||
rows: []
|
||
});
|
||
var prepaidBalanceDrillModal = prepaidBalanceDrillModalState[0];
|
||
var setPrepaidBalanceDrillModal = prepaidBalanceDrillModalState[1];
|
||
|
||
var paymentDrillModalState = useState({ open: false, stationName: '', rows: [] });
|
||
var paymentDrillModal = paymentDrillModalState[0];
|
||
var setPaymentDrillModal = paymentDrillModalState[1];
|
||
|
||
var invoiceDrillModalState = useState({ open: false, stationName: '', rows: [] });
|
||
var invoiceDrillModal = invoiceDrillModalState[0];
|
||
var setInvoiceDrillModal = invoiceDrillModalState[1];
|
||
|
||
var rechargeDrillModalState = useState({ open: false, stationName: '', rows: [] });
|
||
var rechargeDrillModal = rechargeDrillModalState[0];
|
||
var setRechargeDrillModal = rechargeDrillModalState[1];
|
||
|
||
var attachmentPreviewState = useState({ open: false, url: '', title: '', mime: 'image' });
|
||
var attachmentPreview = attachmentPreviewState[0];
|
||
var setAttachmentPreview = attachmentPreviewState[1];
|
||
|
||
var reqDetailOpenState = useState(false);
|
||
var reqDetailOpen = reqDetailOpenState[0];
|
||
var setReqDetailOpen = reqDetailOpenState[1];
|
||
|
||
var columnWidthsState = useState(function () {
|
||
return Object.assign({}, DEFAULT_MAIN_COLUMN_WIDTHS);
|
||
});
|
||
var columnWidths = columnWidthsState[0];
|
||
var setColumnWidths = columnWidthsState[1];
|
||
var columnWidthsRef = { current: columnWidths };
|
||
columnWidthsRef.current = columnWidths;
|
||
|
||
var handleColumnResize = useCallback(function (key, width) {
|
||
setColumnWidths(function (prev) {
|
||
var next = Object.assign({}, prev);
|
||
next[key] = Math.max(MIN_COLUMN_WIDTH, Math.round(width));
|
||
return next;
|
||
});
|
||
}, []);
|
||
|
||
var makeResizableTitle = useCallback(
|
||
function (title, key) {
|
||
return function ResizableColumnTitle() {
|
||
var onResizeMouseDown = function (e) {
|
||
if (e.button !== 0) return;
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
var startX = e.clientX;
|
||
var startWidth = columnWidthsRef.current[key] || DEFAULT_MAIN_COLUMN_WIDTHS[key] || 100;
|
||
var onMouseMove = function (ev) {
|
||
handleColumnResize(key, startWidth + ev.clientX - startX);
|
||
};
|
||
var onMouseUp = function () {
|
||
document.removeEventListener('mousemove', onMouseMove);
|
||
document.removeEventListener('mouseup', onMouseUp);
|
||
document.body.style.cursor = '';
|
||
document.body.style.userSelect = '';
|
||
};
|
||
document.body.style.cursor = 'col-resize';
|
||
document.body.style.userSelect = 'none';
|
||
document.addEventListener('mousemove', onMouseMove);
|
||
document.addEventListener('mouseup', onMouseUp);
|
||
};
|
||
return React.createElement(
|
||
'div',
|
||
{ className: 'h2-col-header' },
|
||
React.createElement('span', { className: 'h2-col-header__text' }, title),
|
||
React.createElement('span', {
|
||
className: 'h2-col-header__resizer',
|
||
onMouseDown: onResizeMouseDown,
|
||
onClick: function (ev) {
|
||
ev.stopPropagation();
|
||
}
|
||
})
|
||
);
|
||
};
|
||
},
|
||
[handleColumnResize]
|
||
);
|
||
|
||
var allRows = useMemo(function () {
|
||
return buildMockSummaryRows();
|
||
}, []);
|
||
|
||
var dataSource = useMemo(function () {
|
||
var list = allRows.filter(function (r) {
|
||
if (regionApplied && r.region !== regionApplied) return false;
|
||
if (stationApplied && r.stationCode !== stationApplied) return false;
|
||
if (settlementApplied && r.settlement !== settlementApplied) return false;
|
||
return true;
|
||
});
|
||
return list.map(function (r, idx) {
|
||
var unpaid = Math.round((numOrZero(r.payableAmount) - numOrZero(r.paidAmount)) * 100) / 100;
|
||
var uninvoiced = Math.round((numOrZero(r.payableAmount) - numOrZero(r.invoicedAmount)) * 100) / 100;
|
||
return Object.assign({}, r, { seq: idx + 1, unpaidAmount: unpaid, uninvoicedAmount: uninvoiced });
|
||
});
|
||
}, [allRows, regionApplied, stationApplied, settlementApplied]);
|
||
|
||
var totals = useMemo(function () {
|
||
var acc = dataSource.reduce(
|
||
function (sum, r) {
|
||
sum.hydrogenKg += numOrZero(r.hydrogenKg);
|
||
sum.payableAmount += numOrZero(r.payableAmount);
|
||
sum.paidAmount += numOrZero(r.paidAmount);
|
||
sum.invoicedAmount += numOrZero(r.invoicedAmount);
|
||
sum.rechargeTotal += numOrZero(r.rechargeTotal);
|
||
sum.prepaidBalance += numOrZero(r.prepaidBalance);
|
||
return sum;
|
||
},
|
||
{ hydrogenKg: 0, payableAmount: 0, paidAmount: 0, invoicedAmount: 0, rechargeTotal: 0, prepaidBalance: 0 }
|
||
);
|
||
acc.unpaidAmount = Math.round((acc.payableAmount - acc.paidAmount) * 100) / 100;
|
||
acc.uninvoicedAmount = Math.round((acc.payableAmount - acc.invoicedAmount) * 100) / 100;
|
||
return acc;
|
||
}, [dataSource]);
|
||
|
||
var filterSummaryText = useMemo(function () {
|
||
var parts = [];
|
||
parts.push('地区:' + (regionApplied || '全部'));
|
||
var st = findStation(stationApplied);
|
||
parts.push('加氢站:' + (st ? st.name : '全部'));
|
||
parts.push('结算方式:' + (settlementApplied || '全部'));
|
||
return parts.join(' ');
|
||
}, [regionApplied, stationApplied, settlementApplied]);
|
||
|
||
var openDetailDrill = useCallback(function (record, drillType) {
|
||
var rows = buildMockStationDetailRows(record, drillType);
|
||
var title =
|
||
drillType === 'payableAmount'
|
||
? '应付总金额明细'
|
||
: '加氢总量明细';
|
||
setDetailDrillModal({
|
||
open: true,
|
||
type: drillType,
|
||
stationName: record.stationName,
|
||
summary: record,
|
||
rows: rows
|
||
});
|
||
}, []);
|
||
|
||
var closeDetailDrill = useCallback(function () {
|
||
setDetailDrillModal({ open: false, type: '', stationName: '', summary: null, rows: [] });
|
||
}, []);
|
||
|
||
var openPrepaidBalanceDrill = useCallback(function (record) {
|
||
setPrepaidBalanceDrillModal({
|
||
open: true,
|
||
stationName: record.stationName,
|
||
endingBalance: numOrZero(record.prepaidBalance),
|
||
rows: buildMockPrepaidBalanceRows(record)
|
||
});
|
||
}, []);
|
||
|
||
var closePrepaidBalanceDrill = useCallback(function () {
|
||
setPrepaidBalanceDrillModal({ open: false, stationName: '', endingBalance: 0, rows: [] });
|
||
}, []);
|
||
|
||
var openPaymentDrill = useCallback(function (record) {
|
||
setPaymentDrillModal({
|
||
open: true,
|
||
stationName: record.stationName,
|
||
rows: buildMockPaymentDrillRows(record)
|
||
});
|
||
}, []);
|
||
|
||
var closePaymentDrill = useCallback(function () {
|
||
setPaymentDrillModal({ open: false, stationName: '', rows: [] });
|
||
}, []);
|
||
|
||
var openInvoiceDrill = useCallback(function (record) {
|
||
setInvoiceDrillModal({
|
||
open: true,
|
||
stationName: record.stationName,
|
||
rows: buildMockInvoiceDrillRows(record)
|
||
});
|
||
}, []);
|
||
|
||
var closeInvoiceDrill = useCallback(function () {
|
||
setInvoiceDrillModal({ open: false, stationName: '', rows: [] });
|
||
}, []);
|
||
|
||
var openRechargeDrill = useCallback(function (record) {
|
||
setRechargeDrillModal({
|
||
open: true,
|
||
stationName: record.stationName,
|
||
rows: buildMockRechargeDrillRows(record)
|
||
});
|
||
}, []);
|
||
|
||
var closeRechargeDrill = useCallback(function () {
|
||
setRechargeDrillModal({ open: false, stationName: '', rows: [] });
|
||
}, []);
|
||
|
||
var openAttachmentPreview = useCallback(function (file) {
|
||
if (!file || !file.url) {
|
||
message.warning('暂无附件');
|
||
return;
|
||
}
|
||
setAttachmentPreview({
|
||
open: true,
|
||
url: file.url,
|
||
title: file.title || '预览',
|
||
mime: file.mime || 'image'
|
||
});
|
||
}, []);
|
||
|
||
var closeAttachmentPreview = useCallback(function () {
|
||
setAttachmentPreview({ open: false, url: '', title: '', mime: 'image' });
|
||
}, []);
|
||
|
||
var downloadAttachment = useCallback(function (file) {
|
||
if (!file || !file.url) {
|
||
message.warning('暂无文件');
|
||
return;
|
||
}
|
||
var a = document.createElement('a');
|
||
a.href = file.url;
|
||
a.download = file.fileName || 'download';
|
||
a.target = '_blank';
|
||
a.rel = 'noopener noreferrer';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
message.success('开始下载');
|
||
}, []);
|
||
|
||
var renderDrillLink = useCallback(function (display, record, drillType) {
|
||
return React.createElement(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
style: drillLinkStyle,
|
||
title: '点击查看明细',
|
||
onClick: function (e) {
|
||
if (e && e.stopPropagation) e.stopPropagation();
|
||
openDetailDrill(record, drillType);
|
||
}
|
||
},
|
||
display
|
||
);
|
||
}, [openDetailDrill]);
|
||
|
||
var renderDrillLinkWithHandler = useCallback(function (display, record, onDrill) {
|
||
return React.createElement(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
style: drillLinkStyle,
|
||
title: '点击查看明细',
|
||
onClick: function (e) {
|
||
if (e && e.stopPropagation) e.stopPropagation();
|
||
onDrill(record);
|
||
}
|
||
},
|
||
display
|
||
);
|
||
}, []);
|
||
|
||
var renderPrepaidBalanceCell = useCallback(function (v, record) {
|
||
var n = numOrZero(v);
|
||
var isArrears = n < 0;
|
||
var amountStyle = isArrears
|
||
? { color: '#cf1322', fontVariantNumeric: 'tabular-nums' }
|
||
: { color: '#1677ff', fontVariantNumeric: 'tabular-nums' };
|
||
return React.createElement(
|
||
'div',
|
||
{ className: 'h2-prepaid-balance-cell' },
|
||
isArrears
|
||
? React.createElement(Tag, { color: 'error' }, '已欠费')
|
||
: null,
|
||
React.createElement(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
className: 'h2-prepaid-balance-amount',
|
||
style: amountStyle,
|
||
title: '点击查看明细',
|
||
onClick: function (e) {
|
||
if (e && e.stopPropagation) e.stopPropagation();
|
||
openPrepaidBalanceDrill(record);
|
||
}
|
||
},
|
||
fmtMoney(v, 2)
|
||
)
|
||
);
|
||
}, [openPrepaidBalanceDrill]);
|
||
|
||
var renderUnpaidAmountCell = useCallback(function (v) {
|
||
var n = numOrZero(v);
|
||
var style = n > 0 ? { color: '#cf1322', fontWeight: 600, fontVariantNumeric: 'tabular-nums' } : { fontVariantNumeric: 'tabular-nums' };
|
||
return React.createElement('span', { style: style }, fmtMoney(v, 2));
|
||
}, []);
|
||
|
||
var renderUninvoicedAmountCell = renderUnpaidAmountCell;
|
||
|
||
var handleQuery = useCallback(function () {
|
||
setRegionApplied(regionDraft);
|
||
setStationApplied(stationDraft);
|
||
setSettlementApplied(settlementDraft);
|
||
message.success('查询成功');
|
||
}, [regionDraft, stationDraft, settlementDraft]);
|
||
|
||
var handleReset = useCallback(function () {
|
||
setRegionDraft(undefined);
|
||
setStationDraft(undefined);
|
||
setSettlementDraft(undefined);
|
||
setRegionApplied(undefined);
|
||
setStationApplied(undefined);
|
||
setSettlementApplied(undefined);
|
||
}, []);
|
||
|
||
var handleExport = useCallback(function () {
|
||
var headers = [
|
||
'序号',
|
||
'地区',
|
||
'加氢站全称',
|
||
'结算方式',
|
||
'加氢总量(kg)',
|
||
'应付总金额(元)',
|
||
'已付总金额(元)',
|
||
'未付总金额(元)',
|
||
'已开票金额(元)',
|
||
'未开票金额(元)',
|
||
'充值总额(元)',
|
||
'当前余额(元)',
|
||
'结算方式'
|
||
];
|
||
var body = dataSource.map(function (r) {
|
||
return [
|
||
r.seq,
|
||
r.region,
|
||
r.stationName,
|
||
r.settlement,
|
||
r.hydrogenKg,
|
||
r.payableAmount,
|
||
r.paidAmount,
|
||
r.unpaidAmount,
|
||
r.invoicedAmount,
|
||
r.uninvoicedAmount,
|
||
r.rechargeTotal,
|
||
r.prepaidBalance
|
||
];
|
||
});
|
||
body.push([
|
||
'合计',
|
||
'',
|
||
'',
|
||
'',
|
||
'',
|
||
Math.round(totals.hydrogenKg * 100) / 100,
|
||
'',
|
||
Math.round(totals.payableAmount * 100) / 100,
|
||
Math.round(totals.paidAmount * 100) / 100,
|
||
Math.round(totals.unpaidAmount * 100) / 100,
|
||
Math.round(totals.invoicedAmount * 100) / 100,
|
||
Math.round(totals.uninvoicedAmount * 100) / 100,
|
||
Math.round(totals.rechargeTotal * 100) / 100,
|
||
Math.round(totals.prepaidBalance * 100) / 100
|
||
]);
|
||
downloadCsv('氢费采购端汇总报表_' + new Date().getTime() + '.csv', [headers].concat(body));
|
||
message.success('已导出 CSV');
|
||
}, [dataSource, totals]);
|
||
|
||
var columns = useMemo(function () {
|
||
var w = columnWidths;
|
||
return [
|
||
{
|
||
title: makeResizableTitle('序号', 'seq'),
|
||
dataIndex: 'seq',
|
||
key: 'seq',
|
||
width: w.seq,
|
||
align: 'center',
|
||
fixed: 'left'
|
||
},
|
||
{
|
||
title: makeResizableTitle('地区', 'region'),
|
||
dataIndex: 'region',
|
||
key: 'region',
|
||
width: w.region,
|
||
align: 'center',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: makeResizableTitle('加氢站全称', 'stationName'),
|
||
dataIndex: 'stationName',
|
||
key: 'stationName',
|
||
width: w.stationName,
|
||
align: 'left',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: makeResizableTitle('结算方式', 'settlement'),
|
||
dataIndex: 'settlement',
|
||
key: 'settlement',
|
||
width: w.settlement,
|
||
align: 'center'
|
||
},
|
||
{
|
||
title: makeResizableTitle('加氢总量(kg)', 'hydrogenKg'),
|
||
dataIndex: 'hydrogenKg',
|
||
key: 'hydrogenKg',
|
||
width: w.hydrogenKg,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
return renderDrillLink(fmtQty(v), record, 'hydrogenKg');
|
||
}
|
||
},
|
||
{
|
||
title: makeResizableTitle('应付总金额(元)', 'payableAmount'),
|
||
dataIndex: 'payableAmount',
|
||
key: 'payableAmount',
|
||
width: w.payableAmount,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
return renderDrillLink(fmtMoney(v, 2), record, 'payableAmount');
|
||
}
|
||
},
|
||
{
|
||
title: makeResizableTitle('已付总金额(元)', 'paidAmount'),
|
||
dataIndex: 'paidAmount',
|
||
key: 'paidAmount',
|
||
width: w.paidAmount,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
return renderDrillLinkWithHandler(fmtMoney(v, 2), record, openPaymentDrill);
|
||
}
|
||
},
|
||
{
|
||
title: makeResizableTitle('未付总金额(元)', 'unpaidAmount'),
|
||
dataIndex: 'unpaidAmount',
|
||
key: 'unpaidAmount',
|
||
width: w.unpaidAmount,
|
||
align: 'right',
|
||
render: renderUnpaidAmountCell
|
||
},
|
||
{
|
||
title: makeResizableTitle('已开票金额(元)', 'invoicedAmount'),
|
||
dataIndex: 'invoicedAmount',
|
||
key: 'invoicedAmount',
|
||
width: w.invoicedAmount,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
return renderDrillLinkWithHandler(fmtMoney(v, 2), record, openInvoiceDrill);
|
||
}
|
||
},
|
||
{
|
||
title: makeResizableTitle('未开票金额(元)', 'uninvoicedAmount'),
|
||
dataIndex: 'uninvoicedAmount',
|
||
key: 'uninvoicedAmount',
|
||
width: w.uninvoicedAmount,
|
||
align: 'right',
|
||
render: renderUninvoicedAmountCell
|
||
},
|
||
{
|
||
title: makeResizableTitle('充值总额(元)', 'rechargeTotal'),
|
||
dataIndex: 'rechargeTotal',
|
||
key: 'rechargeTotal',
|
||
width: w.rechargeTotal,
|
||
align: 'right',
|
||
render: function (v, record) {
|
||
return renderDrillLinkWithHandler(fmtMoney(v, 2), record, openRechargeDrill);
|
||
}
|
||
},
|
||
{
|
||
title: makeResizableTitle('当前余额(元)', 'prepaidBalance'),
|
||
dataIndex: 'prepaidBalance',
|
||
key: 'prepaidBalance',
|
||
width: w.prepaidBalance,
|
||
align: 'right',
|
||
className: 'h2-col-prepaid-balance',
|
||
onCell: function () {
|
||
return { className: 'h2-cell-prepaid-balance' };
|
||
},
|
||
render: renderPrepaidBalanceCell
|
||
}
|
||
];
|
||
}, [
|
||
columnWidths,
|
||
makeResizableTitle,
|
||
renderDrillLink,
|
||
renderDrillLinkWithHandler,
|
||
openPaymentDrill,
|
||
openInvoiceDrill,
|
||
openRechargeDrill,
|
||
renderUnpaidAmountCell,
|
||
renderUninvoicedAmountCell,
|
||
renderPrepaidBalanceCell
|
||
]);
|
||
|
||
var detailDrillBaseColumns = useMemo(function () {
|
||
return [
|
||
{ title: '序号', dataIndex: 'seq', key: 'seq', width: 56, align: 'center' },
|
||
{
|
||
title: '加氢时间',
|
||
dataIndex: 'hydrogenTime',
|
||
key: 'hydrogenTime',
|
||
width: 168,
|
||
align: 'center',
|
||
render: function (v) { return formatDateTime(v); }
|
||
},
|
||
{
|
||
title: '订单编号',
|
||
dataIndex: 'orderNo',
|
||
key: 'orderNo',
|
||
width: 168,
|
||
align: 'center',
|
||
render: function (v) {
|
||
return React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 12 } }, v || '—');
|
||
}
|
||
},
|
||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 108, align: 'center' }
|
||
];
|
||
}, []);
|
||
|
||
var detailDrillQtyColumn = useMemo(function () {
|
||
return {
|
||
title: '加氢量(kg)',
|
||
dataIndex: 'hydrogenKg',
|
||
key: 'hydrogenKg',
|
||
width: 100,
|
||
align: 'right',
|
||
render: function (v) { return fmtQty(v); }
|
||
};
|
||
}, []);
|
||
|
||
var detailDrillCostColumns = useMemo(function () {
|
||
return [
|
||
{
|
||
title: '成本单价(元/kg)',
|
||
dataIndex: 'costUnitPrice',
|
||
key: 'costUnitPrice',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
},
|
||
{
|
||
title: '成本总价(元)',
|
||
dataIndex: 'costTotal',
|
||
key: 'costTotal',
|
||
width: 110,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
}
|
||
];
|
||
}, []);
|
||
|
||
var detailDrillColumns = useMemo(
|
||
function () {
|
||
if (detailDrillModal.type === 'payableAmount') {
|
||
return detailDrillBaseColumns.concat([detailDrillQtyColumn], detailDrillCostColumns);
|
||
}
|
||
return detailDrillBaseColumns.concat([detailDrillQtyColumn]);
|
||
},
|
||
[detailDrillModal.type, detailDrillBaseColumns, detailDrillQtyColumn, detailDrillCostColumns]
|
||
);
|
||
|
||
var detailDrillSummary = useMemo(function () {
|
||
if (!detailDrillModal.rows || !detailDrillModal.rows.length) {
|
||
return { hydrogenKg: 0, costTotal: 0 };
|
||
}
|
||
return detailDrillModal.rows.reduce(
|
||
function (acc, r) {
|
||
acc.hydrogenKg += numOrZero(r.hydrogenKg);
|
||
acc.costTotal += numOrZero(r.costTotal);
|
||
return acc;
|
||
},
|
||
{ hydrogenKg: 0, costTotal: 0 }
|
||
);
|
||
}, [detailDrillModal.rows]);
|
||
|
||
var detailDrillTableSummary = useCallback(function () {
|
||
if (detailDrillModal.type === 'payableAmount') {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1, colSpan: 3 }),
|
||
React.createElement(Table.Summary.Cell, { index: 4, align: 'right' }, fmtQty(detailDrillSummary.hydrogenKg)),
|
||
React.createElement(Table.Summary.Cell, { index: 5 }),
|
||
React.createElement(Table.Summary.Cell, { index: 6, align: 'right' }, fmtMoney(detailDrillSummary.costTotal, 2))
|
||
)
|
||
);
|
||
}
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1, colSpan: 3 }),
|
||
React.createElement(Table.Summary.Cell, { index: 4, align: 'right' }, fmtQty(detailDrillSummary.hydrogenKg))
|
||
)
|
||
);
|
||
}, [detailDrillModal.type, detailDrillSummary]);
|
||
|
||
var detailDrillTitle = useMemo(function () {
|
||
if (!detailDrillModal.open) return '';
|
||
var name = detailDrillModal.stationName ? ' · ' + detailDrillModal.stationName : '';
|
||
return (detailDrillModal.type === 'payableAmount' ? '应付总金额明细' : '加氢量明细') + name;
|
||
}, [detailDrillModal]);
|
||
|
||
var renderTotalsBar = useCallback(function () {
|
||
var items = [
|
||
{ key: 'hydrogenKg', label: '加氢总量(kg)', value: fmtQty(totals.hydrogenKg) },
|
||
{ key: 'payableAmount', label: '应付总金额(元)', value: fmtMoney(totals.payableAmount, 2) },
|
||
{ key: 'paidAmount', label: '已付总金额(元)', value: fmtMoney(totals.paidAmount, 2) },
|
||
{
|
||
key: 'unpaidAmount',
|
||
label: '未付总金额(元)',
|
||
value: fmtMoney(totals.unpaidAmount, 2),
|
||
warn: numOrZero(totals.unpaidAmount) > 0
|
||
},
|
||
{
|
||
key: 'uninvoicedAmount',
|
||
label: '未开票总金额(元)',
|
||
value: fmtMoney(totals.uninvoicedAmount, 2),
|
||
warn: numOrZero(totals.uninvoicedAmount) > 0
|
||
},
|
||
{ key: 'rechargeTotal', label: '充值总额(元)', value: fmtMoney(totals.rechargeTotal, 2) },
|
||
{
|
||
key: 'prepaidBalance',
|
||
label: '当前总余额(元)',
|
||
value: fmtMoney(totals.prepaidBalance, 2),
|
||
warn: numOrZero(totals.prepaidBalance) < 0
|
||
}
|
||
];
|
||
return React.createElement(
|
||
'div',
|
||
{ className: 'h2-ledger-totals-bar' },
|
||
React.createElement('div', { className: 'h2-ledger-totals-bar__title' }, '合计'),
|
||
React.createElement(
|
||
'div',
|
||
{ className: 'h2-ledger-totals-bar__items' },
|
||
items.map(function (item) {
|
||
return React.createElement(
|
||
'div',
|
||
{ key: item.key, className: 'h2-ledger-totals-bar__item' },
|
||
React.createElement('div', { className: 'h2-ledger-totals-bar__label' }, item.label),
|
||
React.createElement(
|
||
'div',
|
||
{
|
||
className:
|
||
'h2-ledger-totals-bar__value' + (item.warn ? ' is-warn' : '')
|
||
},
|
||
item.value
|
||
)
|
||
);
|
||
})
|
||
)
|
||
);
|
||
}, [totals]);
|
||
|
||
var prepaidBalanceDrillColumns = useMemo(function () {
|
||
return [
|
||
{
|
||
title: '加氢站全称',
|
||
dataIndex: 'stationName',
|
||
key: 'stationName',
|
||
width: 220,
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '收入金额(元)',
|
||
dataIndex: 'incomeAmount',
|
||
key: 'incomeAmount',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtLedgerMoney(v); }
|
||
},
|
||
{
|
||
title: '支出金额(元)',
|
||
dataIndex: 'expenseAmount',
|
||
key: 'expenseAmount',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtLedgerMoney(v); }
|
||
},
|
||
{
|
||
title: '余额(元)',
|
||
dataIndex: 'balance',
|
||
key: 'balance',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) {
|
||
var n = numOrZero(v);
|
||
var style = n < 0 ? { color: '#cf1322', fontWeight: 600 } : undefined;
|
||
return React.createElement('span', { style: style }, fmtMoney(v, 2));
|
||
}
|
||
},
|
||
{
|
||
title: '订单编号',
|
||
dataIndex: 'orderNo',
|
||
key: 'orderNo',
|
||
width: 168,
|
||
align: 'center',
|
||
render: function (v) {
|
||
return React.createElement('span', { style: { fontFamily: 'monospace', fontSize: 12 } }, v || '—');
|
||
}
|
||
}
|
||
];
|
||
}, []);
|
||
|
||
var prepaidBalanceDrillSummary = useMemo(function () {
|
||
if (!prepaidBalanceDrillModal.rows || !prepaidBalanceDrillModal.rows.length) {
|
||
return { incomeTotal: 0, expenseTotal: 0, endingBalance: 0 };
|
||
}
|
||
var last = prepaidBalanceDrillModal.rows[prepaidBalanceDrillModal.rows.length - 1];
|
||
return prepaidBalanceDrillModal.rows.reduce(
|
||
function (acc, r) {
|
||
acc.incomeTotal += numOrZero(r.incomeAmount);
|
||
acc.expenseTotal += numOrZero(r.expenseAmount);
|
||
return acc;
|
||
},
|
||
{ incomeTotal: 0, expenseTotal: 0, endingBalance: numOrZero(last.balance) }
|
||
);
|
||
}, [prepaidBalanceDrillModal.rows]);
|
||
|
||
var prepaidBalanceDrillTableSummary = useCallback(function () {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 1, align: 'right' },
|
||
fmtMoney(prepaidBalanceDrillSummary.incomeTotal, 2)
|
||
),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 2, align: 'right' },
|
||
fmtMoney(prepaidBalanceDrillSummary.expenseTotal, 2)
|
||
),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 3, align: 'right' },
|
||
fmtMoney(prepaidBalanceDrillSummary.endingBalance, 2)
|
||
),
|
||
React.createElement(Table.Summary.Cell, { index: 4 })
|
||
)
|
||
);
|
||
}, [prepaidBalanceDrillSummary]);
|
||
|
||
var rechargeDrillColumns = useMemo(
|
||
function () {
|
||
return [
|
||
{
|
||
title: '加氢站全称',
|
||
dataIndex: 'stationName',
|
||
key: 'stationName',
|
||
width: 220,
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '支付时间',
|
||
dataIndex: 'payTime',
|
||
key: 'payTime',
|
||
width: 120,
|
||
align: 'center',
|
||
render: function (v) { return formatDate(v); }
|
||
},
|
||
{
|
||
title: '预充金额(元)',
|
||
dataIndex: 'prepayAmount',
|
||
key: 'prepayAmount',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
},
|
||
{
|
||
title: '付款凭证',
|
||
key: 'voucher',
|
||
width: 160,
|
||
align: 'center',
|
||
render: function (_, row) {
|
||
if (!row.voucherUrl) return '—';
|
||
var file = {
|
||
url: row.voucherUrl,
|
||
fileName: row.voucherFileName,
|
||
mime: row.voucherMime || 'image',
|
||
title: row.voucherFileName || '付款凭证'
|
||
};
|
||
return React.createElement(
|
||
Space,
|
||
{ size: 4, wrap: true, style: { justifyContent: 'center' } },
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
size: 'small',
|
||
style: { padding: 0 },
|
||
onClick: function () {
|
||
openAttachmentPreview({
|
||
url: file.url,
|
||
title: file.title,
|
||
mime: file.mime
|
||
});
|
||
}
|
||
},
|
||
'预览'
|
||
),
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
size: 'small',
|
||
style: { padding: 0 },
|
||
onClick: function () {
|
||
downloadAttachment(file);
|
||
}
|
||
},
|
||
'下载'
|
||
)
|
||
);
|
||
}
|
||
}
|
||
];
|
||
},
|
||
[openAttachmentPreview, downloadAttachment]
|
||
);
|
||
|
||
var rechargeDrillSummary = useMemo(function () {
|
||
if (!rechargeDrillModal.rows || !rechargeDrillModal.rows.length) {
|
||
return { prepayAmount: 0 };
|
||
}
|
||
return rechargeDrillModal.rows.reduce(
|
||
function (acc, r) {
|
||
acc.prepayAmount += numOrZero(r.prepayAmount);
|
||
return acc;
|
||
},
|
||
{ prepayAmount: 0 }
|
||
);
|
||
}, [rechargeDrillModal.rows]);
|
||
|
||
var rechargeDrillTableSummary = useCallback(function () {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1 }),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 2, align: 'right' },
|
||
fmtMoney(rechargeDrillSummary.prepayAmount, 2)
|
||
),
|
||
React.createElement(Table.Summary.Cell, { index: 3 })
|
||
)
|
||
);
|
||
}, [rechargeDrillSummary]);
|
||
|
||
var paymentDrillColumns = useMemo(
|
||
function () {
|
||
return [
|
||
{
|
||
title: '加氢站全称',
|
||
dataIndex: 'stationName',
|
||
key: 'stationName',
|
||
width: 220,
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '账单开始时间',
|
||
dataIndex: 'billStartDate',
|
||
key: 'billStartDate',
|
||
width: 120,
|
||
align: 'center',
|
||
render: function (v) { return formatDate(v); }
|
||
},
|
||
{
|
||
title: '账单结束时间',
|
||
dataIndex: 'billEndDate',
|
||
key: 'billEndDate',
|
||
width: 120,
|
||
align: 'center',
|
||
render: function (v) { return formatDate(v); }
|
||
},
|
||
{
|
||
title: '应付总金额(元)',
|
||
dataIndex: 'payableAmount',
|
||
key: 'payableAmount',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
},
|
||
{
|
||
title: '已付总金额(元)',
|
||
dataIndex: 'paidAmount',
|
||
key: 'paidAmount',
|
||
width: 140,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
},
|
||
{
|
||
title: '银行付款证明',
|
||
key: 'voucher',
|
||
width: 130,
|
||
align: 'center',
|
||
render: function (_, row) {
|
||
if (!row.voucherUrl) return '—';
|
||
return React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
size: 'small',
|
||
style: { padding: 0 },
|
||
onClick: function () {
|
||
openAttachmentPreview({
|
||
url: row.voucherUrl,
|
||
title: row.voucherName || '付款凭据',
|
||
mime: row.voucherMime || 'image'
|
||
});
|
||
}
|
||
},
|
||
'查看付款凭据'
|
||
);
|
||
}
|
||
}
|
||
];
|
||
},
|
||
[openAttachmentPreview]
|
||
);
|
||
|
||
var paymentDrillSummary = useMemo(function () {
|
||
if (!paymentDrillModal.rows || !paymentDrillModal.rows.length) {
|
||
return { payableAmount: 0, paidAmount: 0 };
|
||
}
|
||
return paymentDrillModal.rows.reduce(
|
||
function (acc, r) {
|
||
acc.payableAmount += numOrZero(r.payableAmount);
|
||
acc.paidAmount += numOrZero(r.paidAmount);
|
||
return acc;
|
||
},
|
||
{ payableAmount: 0, paidAmount: 0 }
|
||
);
|
||
}, [paymentDrillModal.rows]);
|
||
|
||
var paymentDrillTableSummary = useCallback(function () {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1, colSpan: 2 }),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 3, align: 'right' },
|
||
fmtMoney(paymentDrillSummary.payableAmount, 2)
|
||
),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 4, align: 'right' },
|
||
fmtMoney(paymentDrillSummary.paidAmount, 2)
|
||
),
|
||
React.createElement(Table.Summary.Cell, { index: 5 })
|
||
)
|
||
);
|
||
}, [paymentDrillSummary]);
|
||
|
||
var invoiceDrillColumns = useMemo(
|
||
function () {
|
||
return [
|
||
{
|
||
title: '加氢站全称',
|
||
dataIndex: 'stationName',
|
||
key: 'stationName',
|
||
width: 220,
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '开票时间',
|
||
dataIndex: 'invoiceTime',
|
||
key: 'invoiceTime',
|
||
width: 168,
|
||
align: 'center',
|
||
render: function (v) { return formatDateTime(v); }
|
||
},
|
||
{
|
||
title: '开票金额(元)',
|
||
dataIndex: 'invoiceAmount',
|
||
key: 'invoiceAmount',
|
||
width: 120,
|
||
align: 'right',
|
||
render: function (v) { return fmtMoney(v, 2); }
|
||
},
|
||
{
|
||
title: '发票',
|
||
key: 'invoiceFile',
|
||
width: 200,
|
||
align: 'center',
|
||
render: function (_, row) {
|
||
if (!row.invoiceUrl) return '—';
|
||
var file = {
|
||
url: row.invoiceUrl,
|
||
fileName: row.invoiceFileName,
|
||
mime: row.invoiceMime || 'image',
|
||
title: row.invoiceFileName || '发票'
|
||
};
|
||
return React.createElement(
|
||
Space,
|
||
{ size: 4, wrap: true, style: { justifyContent: 'center' } },
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
size: 'small',
|
||
style: { padding: 0 },
|
||
onClick: function () {
|
||
openAttachmentPreview({
|
||
url: file.url,
|
||
title: file.title,
|
||
mime: file.mime
|
||
});
|
||
}
|
||
},
|
||
'查看发票'
|
||
),
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
size: 'small',
|
||
style: { padding: 0 },
|
||
onClick: function () {
|
||
downloadAttachment(file);
|
||
}
|
||
},
|
||
'下载发票'
|
||
)
|
||
);
|
||
}
|
||
}
|
||
];
|
||
},
|
||
[openAttachmentPreview, downloadAttachment]
|
||
);
|
||
|
||
var invoiceDrillSummary = useMemo(function () {
|
||
if (!invoiceDrillModal.rows || !invoiceDrillModal.rows.length) {
|
||
return { invoiceAmount: 0 };
|
||
}
|
||
return invoiceDrillModal.rows.reduce(
|
||
function (acc, r) {
|
||
acc.invoiceAmount += numOrZero(r.invoiceAmount);
|
||
return acc;
|
||
},
|
||
{ invoiceAmount: 0 }
|
||
);
|
||
}, [invoiceDrillModal.rows]);
|
||
|
||
var invoiceDrillTableSummary = useCallback(function () {
|
||
return React.createElement(
|
||
Table.Summary,
|
||
null,
|
||
React.createElement(
|
||
Table.Summary.Row,
|
||
null,
|
||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '合计'),
|
||
React.createElement(Table.Summary.Cell, { index: 1 }),
|
||
React.createElement(
|
||
Table.Summary.Cell,
|
||
{ index: 2, align: 'right' },
|
||
fmtMoney(invoiceDrillSummary.invoiceAmount, 2)
|
||
),
|
||
React.createElement(Table.Summary.Cell, { index: 3 })
|
||
)
|
||
);
|
||
}, [invoiceDrillSummary]);
|
||
|
||
return React.createElement(
|
||
App,
|
||
null,
|
||
React.createElement('style', null, ledgerTableStyle),
|
||
React.createElement(
|
||
'div',
|
||
{ style: layoutStyle },
|
||
React.createElement(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 12,
|
||
gap: 16
|
||
}
|
||
},
|
||
React.createElement(Breadcrumb, {
|
||
items: [{ title: '台账数据' }, { title: '氢费(采购端)汇总报表' }]
|
||
}),
|
||
React.createElement(
|
||
Button,
|
||
{
|
||
type: 'link',
|
||
style: { padding: 0, flexShrink: 0, fontSize: 14 },
|
||
onClick: function () {
|
||
setReqDetailOpen(true);
|
||
}
|
||
},
|
||
'查看需求说明'
|
||
)
|
||
),
|
||
React.createElement(
|
||
Card,
|
||
{ style: filterCardStyle, bodyStyle: { paddingBottom: 4 } },
|
||
React.createElement(
|
||
Row,
|
||
{ gutter: [16, 0], align: 'bottom' },
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, lg: 8 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '地区'),
|
||
React.createElement(Select, {
|
||
allowClear: true,
|
||
showSearch: true,
|
||
placeholder: '全部',
|
||
style: filterControlStyle,
|
||
value: regionDraft,
|
||
onChange: function (v) { setRegionDraft(v); },
|
||
options: REGION_OPTIONS,
|
||
filterOption: filterOption
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, lg: 8 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '加氢站全称'),
|
||
React.createElement(Select, {
|
||
allowClear: true,
|
||
showSearch: true,
|
||
placeholder: '全部',
|
||
style: filterControlStyle,
|
||
value: stationDraft,
|
||
onChange: function (v) { setStationDraft(v); },
|
||
options: STATION_OPTIONS,
|
||
filterOption: filterOption
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 12, lg: 8 },
|
||
React.createElement(
|
||
'div',
|
||
{ style: filterItemStyle },
|
||
React.createElement('div', { style: filterLabelStyle }, '结算方式'),
|
||
React.createElement(Select, {
|
||
allowClear: true,
|
||
placeholder: '全部',
|
||
style: filterControlStyle,
|
||
value: settlementDraft,
|
||
onChange: function (v) { setSettlementDraft(v); },
|
||
options: SETTLEMENT_OPTIONS
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Col,
|
||
{ xs: 24, sm: 24, lg: 24, style: Object.assign({}, filterActionsColStyle, { textAlign: 'right' }) },
|
||
React.createElement(
|
||
'div',
|
||
{ style: Object.assign({}, filterItemStyle, { marginBottom: 4 }) },
|
||
React.createElement(
|
||
Space,
|
||
{ wrap: true, style: { justifyContent: 'flex-end', width: '100%' } },
|
||
React.createElement(Button, { onClick: handleReset }, '重置'),
|
||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询')
|
||
)
|
||
)
|
||
)
|
||
)
|
||
),
|
||
React.createElement(
|
||
Card,
|
||
{ style: tableCardStyle, bodyStyle: { padding: '20px 20px 24px' } },
|
||
React.createElement(
|
||
'div',
|
||
{ style: { position: 'relative', marginBottom: 8, minHeight: 36 } },
|
||
React.createElement(
|
||
'div',
|
||
{
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 18,
|
||
fontWeight: 700,
|
||
color: 'rgba(15,23,42,0.92)',
|
||
letterSpacing: '0.02em',
|
||
padding: '0 88px'
|
||
}
|
||
},
|
||
'氢费(采购端)汇总报表'
|
||
),
|
||
React.createElement(
|
||
'div',
|
||
{ style: { position: 'absolute', right: 0, top: '50%', transform: 'translateY(-50%)' } },
|
||
React.createElement(Button, { onClick: handleExport }, '导出')
|
||
)
|
||
),
|
||
React.createElement(
|
||
'div',
|
||
{
|
||
style: {
|
||
textAlign: 'center',
|
||
marginBottom: 16,
|
||
fontSize: 13,
|
||
color: 'rgba(15,23,42,0.55)',
|
||
fontWeight: 500
|
||
}
|
||
},
|
||
filterSummaryText
|
||
),
|
||
renderTotalsBar(),
|
||
React.createElement(
|
||
'div',
|
||
{ className: 'h2-purchase-summary-wrap' },
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table h2-purchase-summary-table--resizable',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
columns: columns,
|
||
dataSource: dataSource,
|
||
pagination: false,
|
||
rowClassName: function () { return 'h2-purchase-row'; },
|
||
scroll: { y: 'calc(100vh - 430px)' },
|
||
sticky: true,
|
||
tableLayout: 'fixed',
|
||
locale: { emptyText: '暂无数据,请调整筛选条件后查询' }
|
||
})
|
||
)
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title: detailDrillTitle,
|
||
open: detailDrillModal.open,
|
||
onCancel: closeDetailDrill,
|
||
footer: React.createElement(Button, { onClick: closeDetailDrill }, '关闭'),
|
||
width: 960,
|
||
destroyOnClose: true,
|
||
styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8 } }
|
||
},
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
pagination: detailDrillModal.rows.length > 10 ? { pageSize: 10, showSizeChanger: false } : false,
|
||
columns: detailDrillColumns,
|
||
dataSource: detailDrillModal.rows,
|
||
summary: detailDrillTableSummary,
|
||
scroll: { x: 'max-content' },
|
||
locale: { emptyText: '暂无明细数据' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title:
|
||
'充值总额明细' +
|
||
(rechargeDrillModal.stationName ? ' · ' + rechargeDrillModal.stationName : ''),
|
||
open: rechargeDrillModal.open,
|
||
onCancel: closeRechargeDrill,
|
||
footer: React.createElement(Button, { onClick: closeRechargeDrill }, '关闭'),
|
||
width: 880,
|
||
destroyOnClose: true,
|
||
styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8 } }
|
||
},
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
pagination:
|
||
rechargeDrillModal.rows.length > 10
|
||
? { pageSize: 10, showSizeChanger: false }
|
||
: false,
|
||
columns: rechargeDrillColumns,
|
||
dataSource: rechargeDrillModal.rows,
|
||
summary: rechargeDrillTableSummary,
|
||
scroll: { x: 'max-content' },
|
||
locale: { emptyText: '暂无充值记录' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title:
|
||
'余额变更明细' +
|
||
(prepaidBalanceDrillModal.stationName ? ' · ' + prepaidBalanceDrillModal.stationName : ''),
|
||
open: prepaidBalanceDrillModal.open,
|
||
onCancel: closePrepaidBalanceDrill,
|
||
footer: React.createElement(Button, { onClick: closePrepaidBalanceDrill }, '关闭'),
|
||
width: 880,
|
||
destroyOnClose: true,
|
||
styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8 } }
|
||
},
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
pagination:
|
||
prepaidBalanceDrillModal.rows.length > 10
|
||
? { pageSize: 10, showSizeChanger: false }
|
||
: false,
|
||
columns: prepaidBalanceDrillColumns,
|
||
dataSource: prepaidBalanceDrillModal.rows,
|
||
summary: prepaidBalanceDrillTableSummary,
|
||
scroll: { x: 'max-content' },
|
||
locale: { emptyText: '暂无余额变更记录' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title:
|
||
'已付总金额明细' +
|
||
(paymentDrillModal.stationName ? ' · ' + paymentDrillModal.stationName : ''),
|
||
open: paymentDrillModal.open,
|
||
onCancel: closePaymentDrill,
|
||
footer: React.createElement(Button, { onClick: closePaymentDrill }, '关闭'),
|
||
width: 1020,
|
||
destroyOnClose: true,
|
||
styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8 } }
|
||
},
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
pagination:
|
||
paymentDrillModal.rows.length > 10
|
||
? { pageSize: 10, showSizeChanger: false }
|
||
: false,
|
||
columns: paymentDrillColumns,
|
||
dataSource: paymentDrillModal.rows,
|
||
summary: paymentDrillTableSummary,
|
||
scroll: { x: 'max-content' },
|
||
locale: { emptyText: '暂无支付明细' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title:
|
||
'已开票明细' + (invoiceDrillModal.stationName ? ' · ' + invoiceDrillModal.stationName : ''),
|
||
open: invoiceDrillModal.open,
|
||
onCancel: closeInvoiceDrill,
|
||
footer: React.createElement(Button, { onClick: closeInvoiceDrill }, '关闭'),
|
||
width: 920,
|
||
destroyOnClose: true,
|
||
styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8 } }
|
||
},
|
||
React.createElement(Table, {
|
||
className: 'h2-purchase-summary-table',
|
||
size: 'small',
|
||
bordered: true,
|
||
rowKey: 'key',
|
||
pagination:
|
||
invoiceDrillModal.rows.length > 10
|
||
? { pageSize: 10, showSizeChanger: false }
|
||
: false,
|
||
columns: invoiceDrillColumns,
|
||
dataSource: invoiceDrillModal.rows,
|
||
summary: invoiceDrillTableSummary,
|
||
scroll: { x: 'max-content' },
|
||
locale: { emptyText: '暂无开票记录' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title: attachmentPreview.title || '附件预览',
|
||
open: attachmentPreview.open,
|
||
onCancel: closeAttachmentPreview,
|
||
footer: React.createElement(Button, { onClick: closeAttachmentPreview }, '关闭'),
|
||
width: attachmentPreview.mime === 'pdf' ? 920 : 720,
|
||
destroyOnClose: true,
|
||
centered: true,
|
||
styles: { body: { paddingTop: 12, textAlign: 'center' } }
|
||
},
|
||
attachmentPreview.mime === 'pdf'
|
||
? React.createElement('iframe', {
|
||
src: attachmentPreview.url,
|
||
title: attachmentPreview.title,
|
||
style: { width: '100%', height: '70vh', border: 'none' }
|
||
})
|
||
: React.createElement('img', {
|
||
src: attachmentPreview.url,
|
||
alt: attachmentPreview.title,
|
||
style: { maxWidth: '100%', maxHeight: '70vh', objectFit: 'contain' }
|
||
})
|
||
),
|
||
React.createElement(
|
||
Modal,
|
||
{
|
||
title: '氢费(采购端)汇总报表 — 需求说明',
|
||
open: reqDetailOpen,
|
||
onCancel: function () {
|
||
setReqDetailOpen(false);
|
||
},
|
||
footer: React.createElement(
|
||
Button,
|
||
{
|
||
type: 'primary',
|
||
onClick: function () {
|
||
setReqDetailOpen(false);
|
||
}
|
||
},
|
||
'知道了'
|
||
),
|
||
width: 840,
|
||
centered: true,
|
||
destroyOnClose: true,
|
||
styles: {
|
||
body: {
|
||
maxHeight: '72vh',
|
||
overflow: 'auto',
|
||
paddingTop: 8,
|
||
paddingBottom: 16
|
||
}
|
||
}
|
||
},
|
||
renderPurchaseRequirementDocPanel()
|
||
)
|
||
)
|
||
);
|
||
};
|