feat(web): 同步 web 端目录更新至 Gitea

包含加氢站站点信息、运维交车/故障、台账与数据分析等页面新增与改动。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-06-04 19:57:30 +08:00
parent d29e2a821b
commit d432d51eed
35 changed files with 26963 additions and 1463 deletions

View File

@@ -0,0 +1,515 @@
// 【重要】必须使用 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 DatePicker = antd.DatePicker;
var Row = antd.Row;
var Col = antd.Col;
var Space = antd.Space;
var message = antd.message;
var CUSTOMER_OPTIONS = [
{ value: '浙江羚牛氢能科技有限公司', label: '浙江羚牛氢能科技有限公司' },
{ value: '杭州绿运物流有限公司', label: '杭州绿运物流有限公司' },
{ value: '宁波港城新能源车队', label: '宁波港城新能源车队' },
{ value: '绍兴氢能示范运营中心', label: '绍兴氢能示范运营中心' }
];
var PROJECT_BY_CUSTOMER = {
'浙江羚牛氢能科技有限公司': ['氢能重卡租赁一期', '园区通勤包车'],
'杭州绿运物流有限公司': ['城配氢能车辆项目', '冷链专线'],
'宁波港城新能源车队': ['港口短驳氢能车', '堆场转运'],
'绍兴氢能示范运营中心': ['示范线路运营', '加氢站接驳']
};
var PLATE_PREFIX = ['浙A', '浙B', '浙D', '浙G'];
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 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 initialSettlementMonth() {
try {
if (window.dayjs) return window.dayjs('2026-05-01');
} catch (e1) {}
return null;
}
function settlementYm(d) {
if (!d || !window.dayjs) return '';
try {
return window.dayjs(d).format('YYYY-MM');
} catch (e2) {
return '';
}
}
/** 日成本 = 缴费金额 / 生效天数 */
function dailyCost(payment, effectiveDays) {
var pay = numOrZero(payment);
var days = numOrZero(effectiveDays);
if (days <= 0) return null;
return Math.round((pay / days) * 10000) / 10000;
}
/** 保险分摊成本 = Σ(各险日成本 × 分摊天数) */
function calcApportionCost(row) {
var d = numOrZero(row.apportionDays);
if (d <= 0) return null;
var sum = 0;
var has = false;
['compulsoryDaily', 'commercialDaily', 'excessDaily', 'cargoDaily'].forEach(function (k) {
var v = row[k];
if (v !== null && v !== undefined && !isNaN(Number(v))) {
sum += Number(v) * d;
has = true;
}
});
if (!has) return null;
return Math.round(sum * 100) / 100;
}
function buildMockRows(ym) {
var rows = [];
var seed = 0;
CUSTOMER_OPTIONS.forEach(function (cust, ci) {
var projects = PROJECT_BY_CUSTOMER[cust.value] || ['默认项目'];
projects.forEach(function (proj, pi) {
seed += 1;
var plate = PLATE_PREFIX[ci % PLATE_PREFIX.length] + String(10000 + seed).slice(-5) + 'F';
var effDays = 365;
var compulsoryPay = 950 + seed * 3;
var commercialPay = 4200 + seed * 17;
var excessPay = seed % 3 === 0 ? 1800 + seed * 5 : 0;
var cargoPay = seed % 2 === 0 ? 600 + seed * 2 : 0;
var apportionDays = 18 + ((seed + (ym ? ym.length : 0)) % 13);
var row = {
key: 'r' + seed,
seq: seed,
settlementCycle: ym || '2026-05',
customerName: cust.value,
projectName: proj,
plateNo: plate,
compulsoryDaily: dailyCost(compulsoryPay, effDays),
commercialDaily: dailyCost(commercialPay, effDays),
excessDaily: excessPay > 0 ? dailyCost(excessPay, effDays) : null,
cargoDaily: cargoPay > 0 ? dailyCost(cargoPay, effDays) : null,
apportionDays: apportionDays
};
row.apportionCost = calcApportionCost(row);
rows.push(row);
});
});
return rows;
}
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 =
'.ins-ledger-table-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)}' +
'.ins-ledger-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}' +
'.ins-ledger-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}' +
'.ins-ledger-table .ant-table-tbody>tr.ins-row-data:hover>td{background:#f0f9ff!important}' +
'.ins-ledger-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}';
var customerDraftState = useState(undefined);
var customerDraft = customerDraftState[0];
var setCustomerDraft = customerDraftState[1];
var monthDraftState = useState(initialSettlementMonth);
var monthDraft = monthDraftState[0];
var setMonthDraft = monthDraftState[1];
var customerAppliedState = useState(undefined);
var customerApplied = customerAppliedState[0];
var setCustomerApplied = customerAppliedState[1];
var monthAppliedState = useState(initialSettlementMonth);
var monthApplied = monthAppliedState[0];
var setMonthApplied = monthAppliedState[1];
var appliedYm = useMemo(function () {
return settlementYm(monthApplied) || '2026-05';
}, [monthApplied]);
var allRows = useMemo(function () {
return buildMockRows(appliedYm);
}, [appliedYm]);
var dataSource = useMemo(function () {
var list = allRows.filter(function (r) {
if (customerApplied && r.customerName !== customerApplied) return false;
return true;
});
return list.map(function (r, idx) {
return Object.assign({}, r, { seq: idx + 1 });
});
}, [allRows, customerApplied]);
var totalApportionCost = useMemo(function () {
return dataSource.reduce(function (acc, r) {
return acc + numOrZero(r.apportionCost);
}, 0);
}, [dataSource]);
var customerDisplayLabel = customerApplied || '默认全量数据';
var cycleDisplayLabel = appliedYm || '-';
var handleQuery = useCallback(function () {
setCustomerApplied(customerDraft);
setMonthApplied(monthDraft);
message.success('查询成功');
}, [customerDraft, monthDraft]);
var handleReset = useCallback(function () {
setCustomerDraft(undefined);
setMonthDraft(initialSettlementMonth());
setCustomerApplied(undefined);
setMonthApplied(initialSettlementMonth());
}, []);
var handleExport = useCallback(function () {
var headers = [
'序号',
'结算周期',
'客户名称',
'项目名称',
'车牌号',
'交强险日成本',
'商业险日成本',
'超赔险日成本',
'货物险日成本',
'分摊天数',
'保险分摊成本'
];
var body = dataSource.map(function (r) {
return [
r.seq,
r.settlementCycle,
r.customerName,
r.projectName,
r.plateNo,
r.compulsoryDaily,
r.commercialDaily,
r.excessDaily,
r.cargoDaily,
r.apportionDays,
r.apportionCost
];
});
body.push(['合计', '', '', '', '', '', '', '', '', '', totalApportionCost]);
downloadCsv('车辆保险台账_' + cycleDisplayLabel + '_' + new Date().getTime() + '.csv', [headers].concat(body));
message.success('已导出 CSV');
}, [dataSource, cycleDisplayLabel, totalApportionCost]);
var columns = useMemo(function () {
return [
{
title: '序号',
dataIndex: 'seq',
key: 'seq',
width: 64,
align: 'center',
fixed: 'left'
},
{
title: '结算周期',
dataIndex: 'settlementCycle',
key: 'settlementCycle',
width: 100,
align: 'center'
},
{
title: '客户名称',
dataIndex: 'customerName',
key: 'customerName',
width: 200,
align: 'center',
ellipsis: true
},
{
title: '项目名称',
dataIndex: 'projectName',
key: 'projectName',
width: 160,
align: 'center',
ellipsis: true
},
{
title: '车牌号',
dataIndex: 'plateNo',
key: 'plateNo',
width: 110,
align: 'center'
},
{
title: '交强险日成本',
dataIndex: 'compulsoryDaily',
key: 'compulsoryDaily',
width: 130,
align: 'right',
render: function (v) { return fmtMoney(v, 4); }
},
{
title: '商业险日成本',
dataIndex: 'commercialDaily',
key: 'commercialDaily',
width: 130,
align: 'right',
render: function (v) { return fmtMoney(v, 4); }
},
{
title: '超赔险日成本',
dataIndex: 'excessDaily',
key: 'excessDaily',
width: 130,
align: 'right',
render: function (v) { return fmtMoney(v, 4); }
},
{
title: '货物险日成本',
dataIndex: 'cargoDaily',
key: 'cargoDaily',
width: 130,
align: 'right',
render: function (v) { return fmtMoney(v, 4); }
},
{
title: '分摊天数',
dataIndex: 'apportionDays',
key: 'apportionDays',
width: 120,
align: 'center'
},
{
title: '保险分摊成本',
dataIndex: 'apportionCost',
key: 'apportionCost',
width: 140,
align: 'right',
fixed: 'right',
render: function (v) { return fmtMoney(v, 2); }
}
];
}, []);
var tableSummary = useCallback(function () {
return React.createElement(
Table.Summary,
null,
React.createElement(
Table.Summary.Row,
null,
React.createElement(Table.Summary.Cell, { index: 0, align: 'center', colSpan: 1 }, '合计'),
React.createElement(Table.Summary.Cell, { index: 1, colSpan: 9 }),
React.createElement(Table.Summary.Cell, { index: 10, align: 'right' }, fmtMoney(totalApportionCost, 2))
)
);
}, [totalApportionCost]);
return React.createElement(
App,
null,
React.createElement('style', null, ledgerTableStyle),
React.createElement(
'div',
{ style: layoutStyle },
React.createElement(Breadcrumb, {
style: { marginBottom: 12 },
items: [{ title: '台账数据' }, { title: '保险分摊明细' }]
}),
React.createElement(
Card,
{ style: filterCardStyle, bodyStyle: { paddingBottom: 4 } },
React.createElement(
Row,
{ gutter: [16, 16], align: 'bottom' },
React.createElement(
Col,
{ xs: 24, sm: 12, md: 8, lg: 6 },
React.createElement(
'div',
{ style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '客户名称'),
React.createElement(Select, {
allowClear: true,
showSearch: true,
placeholder: '默认全量数据',
style: filterControlStyle,
value: customerDraft,
onChange: function (v) { setCustomerDraft(v); },
options: CUSTOMER_OPTIONS,
filterOption: filterOption
})
)
),
React.createElement(
Col,
{ xs: 24, sm: 12, md: 8, lg: 6 },
React.createElement(
'div',
{ style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '结算周期'),
React.createElement(DatePicker, {
picker: 'month',
style: filterControlStyle,
placeholder: '请选择结算周期',
format: 'YYYY-MM',
value: monthDraft,
onChange: function (v) { setMonthDraft(v); }
})
)
),
React.createElement(
Col,
{ xs: 24, sm: 12, md: 8, lg: 6, style: filterActionsColStyle },
React.createElement(
'div',
{ style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '\u00a0'),
React.createElement(
Space,
{ wrap: true },
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
}
},
'结算周期:',
cycleDisplayLabel,
'\u00A0\u00A0\u00A0\u00A0客户',
customerDisplayLabel
),
React.createElement(
'div',
{ className: 'ins-ledger-table-wrap' },
React.createElement(Table, {
className: 'ins-ledger-table',
size: 'small',
bordered: true,
rowKey: 'key',
columns: columns,
dataSource: dataSource,
pagination: false,
rowClassName: function () { return 'ins-row-data'; },
scroll: { x: 'max-content', y: 'calc(100vh - 320px)' },
sticky: true,
summary: tableSummary
})
)
)
)
);
};

View File

@@ -0,0 +1,199 @@
# 氢费(采购端)汇总报表 — 产品需求说明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 年起**(与车辆氢费明细、主数据生效规则一致) |
| 未付(行) | 应付总金额 已付总金额 |
| 未付(合计条) | 各站未付之和,等价于应付合计 已付合计 |
| 未开票(行) | 应付总金额 已开票金额 |
| 未开票(合计条) | 各站未开票之和,等价于应付合计 已开票合计 |
| 已欠费 | 仅 **当前余额 &lt; 0** 时展示标签 |
| 钻取范围 | 均限定为 **当前汇总行对应加氢站** |
---
## 五、验收要点(业务)
1. 查询、重置、筛选摘要、合计条与列表数据一致。
2. 各可点击金额/加氢量钻取弹窗字段与上文一致,合计正确。
3. 未付、已欠费展示规则符合第四节。
4. 付款凭据可预览;发票可预览与下载。
5. 列宽可拖动;导出字段完整。
6. 「查看需求说明」可打开本 PRD 全文。
---
**文档结束**

File diff suppressed because it is too large Load Diff

View File

@@ -3,11 +3,11 @@ var H2_LEDGER_REQUIREMENT_DOC = `# 车辆氢费明细 — 产品需求说明P
| 项目 | 内容 |
|------|------|
| 文档版本 | v2.0(业务版) |
| 文档版本 | v2.1(业务版) |
| 产品模块 | 台账数据 → 车辆氢费明细 |
| 文档类型 | 产品需求说明 |
| 适用读者 | 产品、业务、运营、测试、项目 |
| 修订说明 | 从业务与使用场景出发描述需求,不展开技术实现细节 |
| 修订说明 | 同步列表字段顺序、顶部合计条、承担方式、系统带出字段等现行页面行为 |
---
@@ -130,9 +130,9 @@ flowchart LR
| 客户名称 | 从客户库选择 |
| 加氢站名称 | 从加氢站库选择 |
| 业务员 | 按客户归属业务员筛选 |
| 结算状态 | 客户承担 / 我司承担 |
| 款状态 | 未付款 / 已付款 / 部分付款 |
| 开票公司 | 从开票主体选择 |
| 承担方式 | 客户承担 / 我司承担 / 客户自行结算 / 其他结算 |
| 客户收款状态 | 已付款 / 未付款(系统带出,用于筛选) |
| 开票公司 | 从开票主体选择(系统带出字段对应公司) |
查询成功给予简短成功提示。
@@ -154,7 +154,17 @@ flowchart LR
不同区域之间有明显分隔,已对账区背景略灰、待保存区背景略黄,降低误操作概率。
**部合计:** 对当前列表可见记录,合计 **加氢量、成本总价、加氢总价**(随筛选与「仅看异常」变化)
**部合计** 表格上方独立展示 **加氢量(kg)、成本总价(元)、加氢总价(元)** 三项合计
- **统计范围:** 与当前列表可见行一致。
- **随查询重算:** 点击「查询」应用筛选条件后,合计立即按新结果重新汇总。
- **随「仅显示异常数据」变化:** 开关开启时,仅对当前可见的异常记录(及本人待保存行)合计。
- **说明:** 合计条含「待保存 / 未对账 / 已对账」等当前列表中的全部可见记录,与导出范围(仅未对账+已对账)不同。
**列表列顺序(左→右,业务视角):**
序号 → 加氢日期 → 加氢时间 → 加氢站名称 → 客户名称 → 车牌号 → 加氢量 → 成本单价 → 成本总价 → 加氢单价 → 加氢总价 → 行驶里程 → 备注 → 业务员 → 承担方式 → 对账日期 → **收票日期** → **加氢站付款状态** → **开票日期** → **客户收款状态** → **开票公司** → 状态 → 订单编号 → 操作(操作列固定右侧)。
**系统带出字段(不可手工维护):** 收票日期、加氢站付款状态、开票日期、客户收款状态、开票公司;选择/变更客户等信息后由系统自动刷新,单元格为浅灰底只读样式。
**列表勾选:** 仅 **未对账** 记录可勾选,用于批量「完成对账」。
@@ -178,26 +188,28 @@ flowchart LR
| 信息项 | 用户是否填写 | 业务说明 |
|--------|--------------|----------|
| 状态 | 否 | 展示「未对账」「已对账」;待保存不显示标签 |
| 订单编号 | 否 | 系统按规则自动生成,不可改 |
| 加氢时间 | 是* | 精确到秒,必填 |
| 加氢日期 | 否 | 由加氢时间自动得出 |
| 加氢日期 | 否 | 由加氢时间自动得出,只读 |
| 加氢站名称 | 是* | 必选 |
| 客户名称 | 是* | 必选,位于加氢站右侧;决定业务员与系统带出信息 |
| 车牌号 | 是* | 必选,须为公司登记车辆 |
| 行驶里程 | 否 | 选填,用于业务留痕 |
| 加氢量(kg) | 是* | 必填,用于算总价 |
| 成本单价 | 是* | 必填,向站点的采购成本价 |
| 成本总价 | 否 | 加氢量 × 成本单价,自动计算 |
| 客户名称 | 是* | 必选,决定业务员与财务带出信息 |
| 加氢单价 | 是* | 必填,对客户的销售单价 |
| 加氢总价 | 否 | 加氢量 × 加氢单价,自动计算 |
| 业务员 | 否 | 选客户后自动带出 |
| 结算状态 | 是* | 客户承担 / 我司承担 |
| 开票日期 | 否 | 系统按客户等业务规则带出 |
| 对账日期 | 否 | 系统带出 |
| 付款状态 | 否 | 系统带出 |
| 开票公司 | 否 | 系统带出 |
| 行驶里程 | 否 | 选填,位于加氢总价之后 |
| 备注 | 否 | 自由文本 |
| 业务员 | 否 | 选客户后自动带出 |
| 承担方式 | 是* | 客户承担 / 我司承担 / 客户自行结算 / 其他结算 |
| 对账日期 | 否 | 完成对账等流程带出,只读 |
| 收票日期 | 否 | 由财务/收票模块自动带出,不可编辑 |
| 加氢站付款状态 | 否 | 已付款 / 未付款,由加氢站打款管理带出 |
| 开票日期 | 否 | 由开票模块自动带出 |
| 客户收款状态 | 否 | 已付款 / 未付款,由客户收款模块带出 |
| 开票公司 | 否 | 显示开票公司名称,系统自动带出 |
| 状态 | 否 | 展示「未对账」「已对账」;待保存不显示标签;列在列表末尾 |
| 订单编号 | 否 | 系统按规则自动生成,不可改;列在列表末尾 |
\\* 保存时校验,缺失则标红提示。
@@ -267,7 +279,7 @@ flowchart LR
### 8.1 表头批量改价 / 改结算
- 在 **成本单价**、**加氢单价** 列表头可批量填入统一单价,作用于当前可编辑的所有记录(待保存 + 未对账)。
- 在 **结算状态** 列表头可批量改为「客户承担」或「我司承担」
- 在 **承担方式** 列表头可批量改为四种承担方式之一
- **已对账** 记录不参与批量修改。
### 8.2 批量导入
@@ -277,11 +289,11 @@ flowchart LR
**流程:**
1. 点击「批量导入」→ 下载标准模板
2. 按模板填写(无需填开票日期、对账日期、付款状态、开票公司
2. 按模板填写(无需填对账日期及收票/开票/付款等系统带出字段
3. 上传文件 → 系统生成多条 **待保存** 记录
4. 业务核对列表 → 点击 **保存** 进入未对账
**模板包含列:** 加氢时间、加氢站名称、车牌号、行驶里程、加氢量、成本单价、客户名称、加氢单价、结算状态、备注
**模板包含列:** 加氢时间、加氢站名称、车牌号、加氢量、成本单价、客户名称、加氢单价、承担方式、备注(行驶里程可在备注前按业务需要填写,导入模板列顺序以页面下载为准)
**导入后状态:** 均为待保存,与手工新增一致,须保存后才参与对账与导出。
@@ -297,7 +309,7 @@ flowchart LR
- 弹窗文案:「请选择导出的列」;支持全选/取消全选;默认勾选全部可导出列。
- 无符合条件数据时提示:暂无未对账或已对账数据可导出。
**可导出列(业务名称):** 序号、状态、订单编号、加氢时间、加氢日期、加氢站名称、车牌号、行驶里程、加氢量、成本单价、成本总价、客户名称、加氢单价、加氢总价、业务员、结算状态、开票日期、对账日期、付款状态、开票公司、备注
**可导出列(业务名称):** 序号、状态、订单编号、加氢时间、加氢日期、加氢站名称、车牌号、加氢量、成本单价、成本总价、客户名称、加氢单价、加氢总价、行驶里程、业务员、承担方式、对账日期、备注、收票日期、加氢站付款状态、开票日期、客户收款状态、开票公司。
---
@@ -326,7 +338,7 @@ flowchart LR
### 10.1 保存
- 一次保存处理页面上**全部**待保存记录。
- 必填:加氢时间、加氢站、车牌(须为登记车辆)、客户、加氢量、成本单价、加氢单价、结算状态
- 必填:加氢时间、加氢站、车牌(须为登记车辆)、客户、加氢量、成本单价、加氢单价、承担方式
- 校验失败:仅格子标红,无「保存成功」类提示。
### 10.2 完成对账
@@ -338,8 +350,8 @@ flowchart LR
### 10.3 筛选与列表
- 查询点击后生效;重置恢复。
- 筛选后合计与所见列表一致
- 本人待保存新增行在筛选后仍可见。
- **顶部合计条** 对当前列表可见行实时汇总;修改筛选并点击「查询」后重新计算;「仅显示异常数据」开启时仅统计可见行
- 本人待保存新增行在筛选后仍可见(合计亦包含这些行,若其在列表中展示)
### 10.4 导出
@@ -375,9 +387,13 @@ flowchart LR
| 待保存 | 草稿,未进入对账流程 |
| 未对账 | 已保存,等待业务确认并完成对账 |
| 已对账 | 对账完成,业务员不可随意改动 |
| 客户承担 | 氢费由客户结算 |
| 我司承担 | 氢费由司承担 |
| 未付款 / 已付款 / 部分付款 | 系统带出的付款进度,业务只读 |
| 客户承担 | 氢费由客户承担 |
| 我司承担 | 氢费由司承担 |
| 客户自行结算 | 由客户自行与加氢站等方结算 |
| 其他结算 | 其他结算方式 |
| 加氢站付款状态 | 加氢站侧是否已付款,已付款 / 未付款 |
| 客户收款状态 | 客户侧是否已收款,已付款 / 未付款 |
| 收票日期 | 财务收票日期,系统带出 |
| 标准价 | 公司维护的、按加氢站与生效时段确定的参考单价 |
---

View File

@@ -2,11 +2,11 @@
| 项目 | 内容 |
|------|------|
| 文档版本 | v2.0(业务版) |
| 文档版本 | v2.1(业务版) |
| 产品模块 | 台账数据 → 车辆氢费明细 |
| 文档类型 | 产品需求说明 |
| 适用读者 | 产品、业务、运营、测试、项目 |
| 修订说明 | 从业务与使用场景出发描述需求,不展开技术实现细节 |
| 修订说明 | 同步列表字段顺序、顶部合计条、承担方式、系统带出字段等现行页面行为 |
---
@@ -75,14 +75,14 @@
### 3.1 端到端流程(推荐操作顺序)
```mermaid
\`\`\`mermaid
flowchart LR
A[录入/复制] --> B[待保存]
B --> C[点击保存]
C[保存/导入] --> D[未对账]
D --> E[勾选并完成对账]
E --> F[已对账]
```
\`\`\`
**说明:**
@@ -129,9 +129,9 @@ flowchart LR
| 客户名称 | 从客户库选择 |
| 加氢站名称 | 从加氢站库选择 |
| 业务员 | 按客户归属业务员筛选 |
| 结算状态 | 客户承担 / 我司承担 |
| 款状态 | 未付款 / 已付款 / 部分付款 |
| 开票公司 | 从开票主体选择 |
| 承担方式 | 客户承担 / 我司承担 / 客户自行结算 / 其他结算 |
| 客户收款状态 | 已付款 / 未付款(系统带出,用于筛选) |
| 开票公司 | 从开票主体选择(系统带出字段对应公司) |
查询成功给予简短成功提示。
@@ -153,7 +153,17 @@ flowchart LR
不同区域之间有明显分隔,已对账区背景略灰、待保存区背景略黄,降低误操作概率。
**部合计:** 对当前列表可见记录,合计 **加氢量、成本总价、加氢总价**(随筛选与「仅看异常」变化)
**部合计** 表格上方独立展示 **加氢量(kg)、成本总价(元)、加氢总价(元)** 三项合计
- **统计范围:** 与当前列表可见行一致。
- **随查询重算:** 点击「查询」应用筛选条件后,合计立即按新结果重新汇总。
- **随「仅显示异常数据」变化:** 开关开启时,仅对当前可见的异常记录(及本人待保存行)合计。
- **说明:** 合计条含「待保存 / 未对账 / 已对账」等当前列表中的全部可见记录,与导出范围(仅未对账+已对账)不同。
**列表列顺序(左→右,业务视角):**
序号 → 加氢日期 → 加氢时间 → 加氢站名称 → 客户名称 → 车牌号 → 加氢量 → 成本单价 → 成本总价 → 加氢单价 → 加氢总价 → 行驶里程 → 备注 → 业务员 → 承担方式 → 对账日期 → **收票日期****加氢站付款状态****开票日期****客户收款状态****开票公司** → 状态 → 订单编号 → 操作(操作列固定右侧)。
**系统带出字段(不可手工维护):** 收票日期、加氢站付款状态、开票日期、客户收款状态、开票公司;选择/变更客户等信息后由系统自动刷新,单元格为浅灰底只读样式。
**列表勾选:****未对账** 记录可勾选,用于批量「完成对账」。
@@ -177,28 +187,30 @@ flowchart LR
| 信息项 | 用户是否填写 | 业务说明 |
|--------|--------------|----------|
| 状态 | 否 | 展示「未对账」「已对账」;待保存不显示标签 |
| 订单编号 | 否 | 系统按规则自动生成,不可改 |
| 加氢时间 | 是* | 精确到秒,必填 |
| 加氢日期 | 否 | 由加氢时间自动得出 |
| 加氢日期 | 否 | 由加氢时间自动得出,只读 |
| 加氢站名称 | 是* | 必选 |
| 客户名称 | 是* | 必选,位于加氢站右侧;决定业务员与系统带出信息 |
| 车牌号 | 是* | 必选,须为公司登记车辆 |
| 行驶里程 | 否 | 选填,用于业务留痕 |
| 加氢量(kg) | 是* | 必填,用于算总价 |
| 成本单价 | 是* | 必填,向站点的采购成本价 |
| 成本总价 | 否 | 加氢量 × 成本单价,自动计算 |
| 客户名称 | 是* | 必选,决定业务员与财务带出信息 |
| 加氢单价 | 是* | 必填,对客户的销售单价 |
| 加氢总价 | 否 | 加氢量 × 加氢单价,自动计算 |
| 业务员 | 否 | 选客户后自动带出 |
| 结算状态 | 是* | 客户承担 / 我司承担 |
| 开票日期 | 否 | 系统按客户等业务规则带出 |
| 对账日期 | 否 | 系统带出 |
| 付款状态 | 否 | 系统带出 |
| 开票公司 | 否 | 系统带出 |
| 行驶里程 | 否 | 选填,位于加氢总价之后 |
| 备注 | 否 | 自由文本 |
| 业务员 | 否 | 选客户后自动带出 |
| 承担方式 | 是* | 客户承担 / 我司承担 / 客户自行结算 / 其他结算 |
| 对账日期 | 否 | 完成对账等流程带出,只读 |
| 收票日期 | 否 | 由财务/收票模块自动带出,不可编辑 |
| 加氢站付款状态 | 否 | 已付款 / 未付款,由加氢站打款管理带出 |
| 开票日期 | 否 | 由开票模块自动带出 |
| 客户收款状态 | 否 | 已付款 / 未付款,由客户收款模块带出 |
| 开票公司 | 否 | 显示开票公司名称,系统自动带出 |
| 状态 | 否 | 展示「未对账」「已对账」;待保存不显示标签;列在列表末尾 |
| 订单编号 | 否 | 系统按规则自动生成,不可改;列在列表末尾 |
\* 保存时校验,缺失则标红提示。
\\* 保存时校验,缺失则标红提示。
### 5.2 订单编号规则(业务口径)
@@ -266,7 +278,7 @@ flowchart LR
### 8.1 表头批量改价 / 改结算
-**成本单价**、**加氢单价** 列表头可批量填入统一单价,作用于当前可编辑的所有记录(待保存 + 未对账)。
-**结算状态** 列表头可批量改为「客户承担」或「我司承担」
-**承担方式** 列表头可批量改为四种承担方式之一
- **已对账** 记录不参与批量修改。
### 8.2 批量导入
@@ -276,11 +288,11 @@ flowchart LR
**流程:**
1. 点击「批量导入」→ 下载标准模板
2. 按模板填写(无需填开票日期、对账日期、付款状态、开票公司
2. 按模板填写(无需填对账日期及收票/开票/付款等系统带出字段
3. 上传文件 → 系统生成多条 **待保存** 记录
4. 业务核对列表 → 点击 **保存** 进入未对账
**模板包含列:** 加氢时间、加氢站名称、车牌号、行驶里程、加氢量、成本单价、客户名称、加氢单价、结算状态、备注
**模板包含列:** 加氢时间、加氢站名称、车牌号、加氢量、成本单价、客户名称、加氢单价、承担方式、备注(行驶里程可在备注前按业务需要填写,导入模板列顺序以页面下载为准)
**导入后状态:** 均为待保存,与手工新增一致,须保存后才参与对账与导出。
@@ -296,7 +308,7 @@ flowchart LR
- 弹窗文案:「请选择导出的列」;支持全选/取消全选;默认勾选全部可导出列。
- 无符合条件数据时提示:暂无未对账或已对账数据可导出。
**可导出列(业务名称):** 序号、状态、订单编号、加氢时间、加氢日期、加氢站名称、车牌号、行驶里程、加氢量、成本单价、成本总价、客户名称、加氢单价、加氢总价、业务员、结算状态、开票日期、对账日期、付款状态、开票公司、备注
**可导出列(业务名称):** 序号、状态、订单编号、加氢时间、加氢日期、加氢站名称、车牌号、加氢量、成本单价、成本总价、客户名称、加氢单价、加氢总价、行驶里程、业务员、承担方式、对账日期、备注、收票日期、加氢站付款状态、开票日期、客户收款状态、开票公司。
---
@@ -325,7 +337,7 @@ flowchart LR
### 10.1 保存
- 一次保存处理页面上**全部**待保存记录。
- 必填:加氢时间、加氢站、车牌(须为登记车辆)、客户、加氢量、成本单价、加氢单价、结算状态
- 必填:加氢时间、加氢站、车牌(须为登记车辆)、客户、加氢量、成本单价、加氢单价、承担方式
- 校验失败:仅格子标红,无「保存成功」类提示。
### 10.2 完成对账
@@ -337,8 +349,8 @@ flowchart LR
### 10.3 筛选与列表
- 查询点击后生效;重置恢复。
- 筛选后合计与所见列表一致
- 本人待保存新增行在筛选后仍可见。
- **顶部合计条** 对当前列表可见行实时汇总;修改筛选并点击「查询」后重新计算;「仅显示异常数据」开启时仅统计可见行
- 本人待保存新增行在筛选后仍可见(合计亦包含这些行,若其在列表中展示)
### 10.4 导出
@@ -374,9 +386,13 @@ flowchart LR
| 待保存 | 草稿,未进入对账流程 |
| 未对账 | 已保存,等待业务确认并完成对账 |
| 已对账 | 对账完成,业务员不可随意改动 |
| 客户承担 | 氢费由客户结算 |
| 我司承担 | 氢费由司承担 |
| 未付款 / 已付款 / 部分付款 | 系统带出的付款进度,业务只读 |
| 客户承担 | 氢费由客户承担 |
| 我司承担 | 氢费由司承担 |
| 客户自行结算 | 由客户自行与加氢站等方结算 |
| 其他结算 | 其他结算方式 |
| 加氢站付款状态 | 加氢站侧是否已付款,已付款 / 未付款 |
| 客户收款状态 | 客户侧是否已收款,已付款 / 未付款 |
| 收票日期 | 财务收票日期,系统带出 |
| 标准价 | 公司维护的、按加氢站与生效时段确定的参考单价 |
---

File diff suppressed because it is too large Load Diff