Files
ONE-OS/web端/业务管理/租赁账单.jsx

363 lines
19 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 作为组件变量名
// 租赁账单2026年3月10日版本
const Component = function () {
var useState = React.useState;
var useMemo = React.useMemo;
var antd = window.antd;
var Breadcrumb = antd.Breadcrumb;
var Card = antd.Card;
var Table = antd.Table;
var Button = antd.Button;
var Select = antd.Select;
var Input = antd.Input;
var Space = antd.Space;
var Popover = antd.Popover;
var message = antd.message;
var filterContractCode = useState(undefined);
var filterProjectName = useState(undefined);
var filterCustomerName = useState(undefined);
var filterBusinessDept = useState(undefined);
var filterBusinessPerson = useState(undefined);
var filterDeliveryTaskCode = useState('');
var filterExpanded = useState(false);
var expandedRowKeysState = useState([]);
var deliveryPopoverOpen = useState(null);
var expandedRowKeys = expandedRowKeysState[0];
var setExpandedRowKeys = expandedRowKeysState[1];
// 主表数据:按合同聚合,每条主表下有多条账单(子表)
var mainListData = [
{
contractCode: 'HT-ZL-2025-001',
deliveryTaskCode: 'JT-2025-001-A',
contractType: '正式合同',
projectName: '嘉兴氢能示范项目',
customerName: '嘉兴某某物流有限公司',
contractEffectiveDate: '2025-01-15',
businessDept: '业务1部',
businessPerson: '张经理',
children: [
{ period: 1, billStartDate: '2025-01-01', billEndDate: '2025-01-31', deliveryCount: 3, deliveryVehicles: [{ brand: '东风', model: 'DFH1180', plateNo: '浙A12345' }, { brand: '福田', model: 'BJ1180', plateNo: '浙A23456' }, { brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567' }], receivableTotal: 45800.00, actualTotal: 45500.00, discountTotal: 300.00, arrivalAmount: 45500.00, isInvoiced: '已开票', invoiceAmount: 45500.00 },
{ period: 2, billStartDate: '2025-02-01', billEndDate: '2025-02-28', deliveryCount: 2, deliveryVehicles: [{ brand: '陕汽', model: 'SX1313', plateNo: '浙A45678' }, { brand: '解放', model: 'J6P', plateNo: '浙A56789' }], receivableTotal: 45800.00, actualTotal: 45800.00, discountTotal: 0.00, arrivalAmount: 45800.00, isInvoiced: '已开票', invoiceAmount: 45800.00 },
{ period: 3, billStartDate: '2025-03-01', billEndDate: '2025-03-31', deliveryCount: 2, deliveryVehicles: [{ brand: '江淮', model: '格尔发K5', plateNo: '浙A67890' }, { brand: '东风', model: 'DFH1250', plateNo: '浙A11111' }], receivableTotal: 45800.00, actualTotal: 45000.00, discountTotal: 800.00, arrivalAmount: 42000.00, isInvoiced: '部分开票', invoiceAmount: 42000.00 }
]
},
{
contractCode: 'HT-ZL-2025-002',
deliveryTaskCode: 'JT-2025-002-B',
contractType: '试用合同',
projectName: '上海物流租赁项目',
customerName: '上海某某运输公司',
contractEffectiveDate: '2025-02-01',
businessDept: '业务2部',
businessPerson: '李专员',
children: [
{ period: 1, billStartDate: '2025-02-01', billEndDate: '2025-02-28', deliveryCount: 2, deliveryVehicles: [{ brand: '江淮', model: '格尔发K5', plateNo: '沪B11111' }, { brand: '东风', model: 'DFH1250', plateNo: '沪B22222' }], receivableTotal: 33400.00, actualTotal: 33400.00, discountTotal: 0.00, arrivalAmount: 33400.00, isInvoiced: '已开票', invoiceAmount: 33400.00 },
{ period: 2, billStartDate: '2025-03-01', billEndDate: '2025-03-31', deliveryCount: 1, deliveryVehicles: [{ brand: '解放', model: 'JH6', plateNo: '沪B33333' }], receivableTotal: 33400.00, actualTotal: 33400.00, discountTotal: 0.00, arrivalAmount: 0.00, isInvoiced: '未开票', invoiceAmount: 0.00 }
]
},
{
contractCode: 'HT-ZL-2025-003',
deliveryTaskCode: 'JT-2025-003-C',
contractType: '正式合同',
projectName: '杭州城配租赁项目',
customerName: '杭州某某租赁有限公司',
contractEffectiveDate: '2025-02-10',
businessDept: '业务3部',
businessPerson: '王专员',
children: [
{ period: 1, billStartDate: '2025-02-10', billEndDate: '2025-03-09', deliveryCount: 1, deliveryVehicles: [{ brand: '福田', model: '欧曼EST', plateNo: '浙C33333' }], receivableTotal: 41200.00, actualTotal: 41200.00, discountTotal: 0.00, arrivalAmount: 41200.00, isInvoiced: '已开票', invoiceAmount: 41200.00 }
]
}
];
var mainList = mainListData;
var filterOptions = useMemo(function () {
var codes = [], projects = [], customers = [], depts = [], persons = [];
mainList.forEach(function (r) {
if (r.contractCode && codes.indexOf(r.contractCode) === -1) codes.push(r.contractCode);
if (r.projectName && projects.indexOf(r.projectName) === -1) projects.push(r.projectName);
if (r.customerName && customers.indexOf(r.customerName) === -1) customers.push(r.customerName);
if (r.businessDept && depts.indexOf(r.businessDept) === -1) depts.push(r.businessDept);
if (r.businessPerson && persons.indexOf(r.businessPerson) === -1) persons.push(r.businessPerson);
});
return {
contractCode: codes.map(function (v) { return { value: v, label: v }; }),
projectName: projects.map(function (v) { return { value: v, label: v }; }),
customerName: customers.map(function (v) { return { value: v, label: v }; }),
businessDept: depts.map(function (v) { return { value: v, label: v }; }),
businessPerson: persons.map(function (v) { return { value: v, label: v }; })
};
}, [mainList]);
var filteredMainList = useMemo(function () {
var list = mainList;
var code = filterContractCode[0];
var project = filterProjectName[0];
var customer = filterCustomerName[0];
var dept = filterBusinessDept[0];
var person = filterBusinessPerson[0];
var taskCode = (filterDeliveryTaskCode[0] || '').trim().toLowerCase();
if (code) list = list.filter(function (r) { return r.contractCode === code; });
if (project) list = list.filter(function (r) { return r.projectName === project; });
if (customer) list = list.filter(function (r) { return r.customerName === customer; });
if (dept) list = list.filter(function (r) { return r.businessDept === dept; });
if (person) list = list.filter(function (r) { return r.businessPerson === person; });
if (taskCode) list = list.filter(function (r) { return (r.deliveryTaskCode || '').toLowerCase().indexOf(taskCode) !== -1; });
return list;
}, [mainList, filterContractCode[0], filterProjectName[0], filterCustomerName[0], filterBusinessDept[0], filterBusinessPerson[0], filterDeliveryTaskCode[0]]);
var mainTableDataSource = useMemo(function () {
return filteredMainList.map(function (r) {
var o = {};
for (var k in r) if (k !== 'children') o[k] = r[k];
o._detailList = r.children || [];
return o;
});
}, [filteredMainList]);
function fmtMoney(v) {
if (v === null || v === undefined) return '0.00元';
var n = typeof v === 'number' ? v : parseFloat(v);
return (isNaN(n) ? '0.00' : n.toFixed(2)) + '元';
}
function goView(record, parent) {
message.info('查看账单详情(原型)');
}
function goChargeDetail(record, parent) {
message.info('收费明细(原型)');
}
// 子表提车数量气泡:列表显示 品牌、型号、车牌号
function renderDeliveryPopover(record) {
var vehicles = record.deliveryVehicles || [];
var parentCode = (record._parentRecord && record._parentRecord.contractCode) || '';
var popoverKey = parentCode + '-' + (record.period != null ? record.period : '');
var listStyle = { width: '100%', borderCollapse: 'collapse', fontSize: 13 };
var thStyle = { padding: '6px 10px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', backgroundColor: '#fafafa', fontWeight: 600 };
var tdStyle = { padding: '6px 10px', borderBottom: '1px solid #f0f0f0' };
var content = vehicles.length === 0 ? React.createElement('div', { style: { padding: 8 } }, '—') : React.createElement('div', { style: { padding: 0, minWidth: 200 } },
React.createElement('table', { style: listStyle },
React.createElement('thead', null,
React.createElement('tr', null,
React.createElement('th', { style: thStyle }, '品牌'),
React.createElement('th', { style: thStyle }, '型号'),
React.createElement('th', { style: thStyle }, '车牌号')
)
),
React.createElement('tbody', null,
vehicles.map(function (v, i) {
return React.createElement('tr', { key: i },
React.createElement('td', { style: tdStyle }, v.brand || '—'),
React.createElement('td', { style: tdStyle }, v.model || '—'),
React.createElement('td', { style: tdStyle }, v.plateNo || '—')
);
})
)
)
);
var count = record.deliveryCount != null && record.deliveryCount !== '' ? Number(record.deliveryCount) : 0;
return React.createElement(Popover, {
content: content,
title: '车辆详情',
trigger: 'click',
open: deliveryPopoverOpen[0] === popoverKey,
onOpenChange: function (open) { deliveryPopoverOpen[1](open ? popoverKey : null); }
}, React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 } }, (isNaN(count) ? 0 : count) + '辆'));
}
var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
var cardStyle = { marginBottom: 16 };
var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
var filterItemStyle = { marginBottom: 12 };
var filterControlStyle = { width: '100%' };
var expandColumnWidth = 48;
var mainColumns = [
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 130, ellipsis: true, render: function (v) { return v || '—'; } },
{ title: '合同类型', dataIndex: 'contractType', key: 'contractType', width: 100, ellipsis: true, render: function (v) { return v || '—'; } },
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true, render: function (v) { return v || '—'; } },
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140, ellipsis: true, render: function (v) { return v || '—'; } },
{ title: '合同生效日期', dataIndex: 'contractEffectiveDate', key: 'contractEffectiveDate', width: 118, render: function (v) { return v || '—'; } },
{ title: '交车任务编码', dataIndex: 'deliveryTaskCode', key: 'deliveryTaskCode', width: 130, ellipsis: true, render: function (v) { return v || '—'; } },
{ title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 100, render: function (v) { return v || '—'; } },
{ title: '业务负责人', dataIndex: 'businessPerson', key: 'businessPerson', width: 100, render: function (v) { return v || '—'; } }
];
var subColumns = [
{ title: '账单编码', dataIndex: 'billNo', key: 'billNo', width: 200, ellipsis: true, render: function (v, record) { var p = record._parentRecord; var code = (p && p.contractCode) || ''; var period = record.period != null ? record.period : ''; var suffix = period !== '' ? ('0000' + period).slice(-4) : ''; return code + suffix || '—'; } },
{ title: '账单期数', dataIndex: 'period', key: 'period', width: 90, align: 'center', render: function (v) { return v != null ? v : '—'; } },
{ title: '账单开始日期', dataIndex: 'billStartDate', key: 'billStartDate', width: 120, render: function (v) { return v || '—'; } },
{ title: '账单结束日期', dataIndex: 'billEndDate', key: 'billEndDate', width: 120, render: function (v) { return v || '—'; } },
{ title: '提车数量', key: 'deliveryCount', width: 88, align: 'center', render: function (_, record) { return renderDeliveryPopover(record); } },
{ title: '应收款总额', dataIndex: 'receivableTotal', key: 'receivableTotal', width: 110, align: 'right', render: function (v) { return fmtMoney(v); } },
{ title: '实收款总额', dataIndex: 'actualTotal', key: 'actualTotal', width: 110, align: 'right', render: function (v) { return fmtMoney(v); } },
{ title: '减免总金额', dataIndex: 'discountTotal', key: 'discountTotal', width: 100, align: 'right', render: function (v) { return fmtMoney(v); } },
{ title: '实际到账金额', dataIndex: 'arrivalAmount', key: 'arrivalAmount', width: 118, align: 'right', render: function (v) { return fmtMoney(v); } },
{ title: '是否已开票', dataIndex: 'isInvoiced', key: 'isInvoiced', width: 96, render: function (v) { return v === '已开票' ? '已开票' : v === '部分开票' ? '部分开票' : (v === '未开票' ? '未开票' : (v || '—')); } },
{ title: '开票金额', dataIndex: 'invoiceAmount', key: 'invoiceAmount', width: 100, align: 'right', render: function (v) { return fmtMoney(v); } },
{
title: '操作',
key: 'action',
width: 160,
fixed: 'right',
render: function (_, record, rowIndex, extra) {
var parentRecord = (extra && extra._parentRecord) || record._parentRecord;
return React.createElement(Space, { size: 'small' },
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record, parentRecord); } }, '查看'),
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goChargeDetail(record, parentRecord); } }, '收费明细')
);
}
}
];
// 子表 rowKey 唯一contractCode + period
var _expandRowRender = function (record) {
var rows = (record._detailList || []).map(function (r, idx) {
var o = {}; for (var k in r) o[k] = r[k]; o._parentRecord = record; o._rowIndex = idx; return o;
});
return React.createElement('div', {
style: { marginBottom: 0, paddingLeft: expandColumnWidth, boxSizing: 'border-box' },
draggable: false,
onDragStart: function (e) { e.preventDefault(); }
},
React.createElement(Table, {
rowKey: function (r) { return (record.contractCode || '') + '-' + (r.period != null ? r.period : r._rowIndex); },
columns: subColumns.map(function (col) {
if (col.key !== 'action') return col;
return {
title: col.title,
key: col.key,
width: col.width,
fixed: col.fixed,
render: function (val, row) {
return React.createElement(Space, { size: 'small' },
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(row, record); } }, '查看'),
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goChargeDetail(row, record); } }, '收费明细')
);
}
};
}),
dataSource: rows,
pagination: false,
size: 'small',
bordered: true,
scroll: { x: 1300 }
})
);
};
return React.createElement('div', { style: layoutStyle },
React.createElement('div', { style: { marginBottom: 16 } },
React.createElement(Breadcrumb, {
items: [
{ title: '业务管理' },
{ title: '租赁账单' }
]
})
),
React.createElement(Card, { title: '筛选', style: cardStyle },
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' } },
React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '合同编码'),
React.createElement(Select, {
placeholder: '请选择或输入合同编码',
allowClear: true,
showSearch: true,
style: filterControlStyle,
value: filterContractCode[0],
onChange: function (v) { filterContractCode[1](v); },
options: filterOptions.contractCode,
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
})
),
React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '项目名称'),
React.createElement(Select, {
placeholder: '请选择或输入项目名称',
allowClear: true,
showSearch: true,
style: filterControlStyle,
value: filterProjectName[0],
onChange: function (v) { filterProjectName[1](v); },
options: filterOptions.projectName,
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
})
),
React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '客户名称'),
React.createElement(Select, {
placeholder: '请选择或输入客户名称',
allowClear: true,
showSearch: true,
style: filterControlStyle,
value: filterCustomerName[0],
onChange: function (v) { filterCustomerName[1](v); },
options: filterOptions.customerName,
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
})
),
filterExpanded[0] ? React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '业务部门'),
React.createElement(Select, {
placeholder: '请选择业务部门',
allowClear: true,
style: filterControlStyle,
value: filterBusinessDept[0],
onChange: function (v) { filterBusinessDept[1](v); },
options: filterOptions.businessDept
})
) : null,
filterExpanded[0] ? React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '业务负责人'),
React.createElement(Select, {
placeholder: '请选择业务负责人',
allowClear: true,
style: filterControlStyle,
value: filterBusinessPerson[0],
onChange: function (v) { filterBusinessPerson[1](v); },
options: filterOptions.businessPerson
})
) : null,
filterExpanded[0] ? React.createElement('div', { style: filterItemStyle },
React.createElement('div', { style: filterLabelStyle }, '交车任务编码'),
React.createElement(Input, {
placeholder: '请输入交车任务编码,支持模糊搜索',
allowClear: true,
style: filterControlStyle,
value: filterDeliveryTaskCode[0],
onChange: function (e) { filterDeliveryTaskCode[1](e.target.value); }
})
) : null
),
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } },
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { filterExpanded[1](!filterExpanded[0]); } }, filterExpanded[0] ? '收起' : '展开'),
React.createElement(Button, { onClick: function () { filterContractCode[1](undefined); filterProjectName[1](undefined); filterCustomerName[1](undefined); filterBusinessDept[1](undefined); filterBusinessPerson[1](undefined); filterDeliveryTaskCode[1](''); } }, '重置'),
React.createElement(Button, { type: 'primary' }, '查询')
)
),
React.createElement(Card, { title: '租赁账单列表', style: cardStyle },
React.createElement(Table, {
rowKey: 'contractCode',
columns: mainColumns,
dataSource: mainTableDataSource,
expandable: {
expandedRowKeys: expandedRowKeys,
onExpandedRowsChange: function (keys) { setExpandedRowKeys(keys || []); },
expandedRowRender: _expandRowRender,
rowExpandable: function (record) { return (record._detailList && record._detailList.length > 0); },
columnWidth: expandColumnWidth
},
pagination: { pageSize: 10, showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } },
size: 'middle',
bordered: true,
scroll: { x: 958 }
})
)
);
};