363 lines
19 KiB
JavaScript
363 lines
19 KiB
JavaScript
// 【重要】必须使用 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 }
|
||
})
|
||
)
|
||
);
|
||
};
|