Files
ONE-OS/web端/台账数据/保险分摊明细.jsx
王冕 d432d51eed feat(web): 同步 web 端目录更新至 Gitea
包含加氢站站点信息、运维交车/故障、台账与数据分析等页面新增与改动。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 19:57:30 +08:00

516 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 【重要】必须使用 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
})
)
)
)
);
};