diff --git a/web端/业务管理/ETC管理.jsx b/web端/业务管理/ETC管理.jsx new file mode 100644 index 0000000..ba509dc --- /dev/null +++ b/web端/业务管理/ETC管理.jsx @@ -0,0 +1,458 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 业务管理 - ETC管理(2026年3月10日版本) +// 模块:充值明细 / ETC账单明细 / ETC余额管理 + +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 Tabs = antd.Tabs; + var Button = antd.Button; + var Select = antd.Select; + var Input = antd.Input; + var DatePicker = antd.DatePicker; + var Modal = antd.Modal; + var message = antd.message; + var Tag = antd.Tag; + var Upload = antd.Upload; + + var activeTab = useState('recharge'); + var setActiveTab = activeTab[1]; + + // ========== 充值明细 Tab ========== + var filterCustomer = useState(undefined); + var filterRemitStart = useState(null); + var filterRemitEnd = useState(null); + var filterReceiveCompany = useState(undefined); + var filterEntryType = useState(undefined); + var filterRechargeNo = useState(''); + var filterAuditStatus = useState(undefined); + var addRechargeVisible = useState(false); + var deleteConfirmVisible = useState(false); + var deleteConfirmRecord = useState(null); + var rechargeList = useState([ + { id: 'r1', auditStatus: '已入账', rechargeNo: 'ETC202603010001', customerName: '嘉兴某某物流有限公司', etcBalance: 12580.00, remitAccount: '嘉兴某某物流有限公司', remitDate: '2026-03-01', receiveCompany: '浙江羚牛氢能科技有限公司', entryType: '合同预付', receiveBank: '工商银行杭州分行', amount: 10000.00, creator: '张经理', createTime: '2026-03-01 10:00', attachment: '凭证.pdf', updateTime: '2026-03-01 10:00' }, + { id: 'r2', auditStatus: '待审核', rechargeNo: 'ETC202603050002', customerName: '上海某某运输公司', etcBalance: 8600.00, remitAccount: '上海某某运输公司', remitDate: '2026-03-05', receiveCompany: '羚牛新能源科技上海有限公司', entryType: '线下充值', receiveBank: '建设银行上海分行', amount: 5000.00, creator: '李专员', createTime: '2026-03-05 14:30', attachment: '', updateTime: '2026-03-05 14:30' }, + { id: 'r3', auditStatus: '已驳回', rechargeNo: 'ETC202603080003', customerName: '杭州某某租赁有限公司', etcBalance: 3200.00, remitAccount: '杭州某某租赁有限公司', remitDate: '2026-03-08', receiveCompany: '浙江羚牛氢能科技有限公司', entryType: '资金冲红', receiveBank: '农业银行杭州分行', amount: -500.00, creator: '王专员', createTime: '2026-03-08 09:15', attachment: '冲红说明.pdf', updateTime: '2026-03-08 11:00' } + ]); + var rechargeData = rechargeList[0]; + var setRechargeList = rechargeList[1]; + + // 新增充值表单 + var addFormCustomer = useState(undefined); + var addFormRemitAccount = useState(''); + var addFormEtcBalance = useState(''); + var addFormRemitDate = useState(null); + var addFormReceiveCompany = useState(undefined); + var addFormOpType = useState(undefined); + var addFormAmount = useState(''); + var addFormReceiveBank = useState(undefined); + var addFormFiles = useState([]); + + var receiveCompanyOptions = [ + { label: '羚牛新能源科技上海有限公司', value: '羚牛新能源科技上海有限公司' }, + { label: '浙江羚牛氢能科技有限公司', value: '浙江羚牛氢能科技有限公司' } + ]; + var entryTypeOptions = [ + { label: '合同预付', value: '合同预付' }, + { label: '线下充值', value: '线下充值' }, + { label: '资金冲红', value: '资金冲红' } + ]; + var opTypeOptions = [ + { label: '普通充值', value: '普通充值' }, + { label: '资金冲红', value: '资金冲红' } + ]; + + var filteredRecharge = useMemo(function () { + var list = rechargeData; + if (filterCustomer[0]) list = list.filter(function (r) { return r.customerName === filterCustomer[0]; }); + if (filterReceiveCompany[0]) list = list.filter(function (r) { return r.receiveCompany === filterReceiveCompany[0]; }); + if (filterEntryType[0]) list = list.filter(function (r) { return r.entryType === filterEntryType[0]; }); + if (filterAuditStatus[0]) list = list.filter(function (r) { return r.auditStatus === filterAuditStatus[0]; }); + if ((filterRechargeNo[0] || '').trim()) { + var kw = (filterRechargeNo[0] || '').trim().toLowerCase(); + list = list.filter(function (r) { return (r.rechargeNo || '').toLowerCase().indexOf(kw) !== -1; }); + } + return list; + }, [rechargeData, filterCustomer[0], filterReceiveCompany[0], filterEntryType[0], filterAuditStatus[0], filterRechargeNo[0]]); + + var rechargeColumns = [ + { title: '审核状态', dataIndex: 'auditStatus', key: 'auditStatus', width: 88, render: function (v) { var color = v === '已入账' ? 'success' : v === '待审核' ? 'processing' : 'error'; return React.createElement(Tag, { color: color }, v); } }, + { title: '充值编号', dataIndex: 'rechargeNo', key: 'rechargeNo', width: 160, ellipsis: true }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true }, + { title: 'ETC账户余额', dataIndex: 'etcBalance', key: 'etcBalance', width: 120, align: 'right', render: function (v) { return (v != null ? Number(v).toFixed(2) : '0.00') + ' 元'; } }, + { title: '汇款账户名', dataIndex: 'remitAccount', key: 'remitAccount', width: 140, ellipsis: true }, + { title: '汇款日期', dataIndex: 'remitDate', key: 'remitDate', width: 110 }, + { title: '收款公司', dataIndex: 'receiveCompany', key: 'receiveCompany', width: 200, ellipsis: true }, + { title: '入账类型', dataIndex: 'entryType', key: 'entryType', width: 100 }, + { title: '收款银行', dataIndex: 'receiveBank', key: 'receiveBank', width: 140, ellipsis: true }, + { title: '充值金额', dataIndex: 'amount', key: 'amount', width: 100, align: 'right', render: function (v) { return (v != null ? Number(v).toFixed(2) : '0.00') + ' 元'; } }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 90 }, + { title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 160 }, + { title: '附件', dataIndex: 'attachment', key: 'attachment', width: 90, render: function (v) { return v ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('预览:' + v); } }, '预览') : '—'; } }, + { title: '更新时间', dataIndex: 'updateTime', key: 'updateTime', width: 160 }, + { title: '操作', key: 'action', width: 160, fixed: 'right', render: function (_, record) { + return React.createElement(React.Fragment, null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('查看 ' + record.rechargeNo); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('编辑 ' + record.rechargeNo); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { deleteConfirmRecord[1](record); deleteConfirmVisible[1](true); } }, '删除') + ); + } } + ]; + + var handleDeleteRecharge = function () { + var r = deleteConfirmRecord[0]; + if (r) setRechargeList(function (prev) { return prev.filter(function (x) { return x.id !== r.id; }); }); + deleteConfirmVisible[1](false); + deleteConfirmRecord[1](null); + message.success('已删除'); + }; + + var handleAddRechargeSubmit = function () { + message.success('充值信息已提交'); + addRechargeVisible[1](false); + }; + + // ========== ETC账单明细 Tab ========== + var billFilterYear = useState(2026); + var billFilterDateRange = useState(null); + var billFilterBillDateRange = useState(null); + var billFilterPassDate = useState(null); + var billFilterPlate = useState(''); + var billFilterVehicleNature = useState(undefined); + var billFilterCustomer = useState(''); + var billFilterReconciled = useState(undefined); + var invoiceConfirmVisible = useState(false); + var billList = useState([ + { id: 'b1', year: 2026, month: 3, week: 2, salesman: '张经理', billDate: '2026/3/10', plateNo: '浙A12345', passTime: '2026-03-10 08:56:48', entry: '杭州南', exit: '嘉兴东', fee: 45.50, vehicleType: '18T', nature: '租赁', customerName: '嘉兴某某物流有限公司', isInvoiced: '否', invoiceAmount: 0, remark: '' }, + { id: 'b2', year: 2026, month: 3, week: 2, salesman: '张经理', billDate: '2026/3/10', plateNo: '浙A23456', passTime: '2026-03-10 09:12:00', entry: '嘉兴东', exit: '上海枫泾', fee: 128.00, vehicleType: '18T', nature: '租赁', customerName: '嘉兴某某物流有限公司', isInvoiced: '是', invoiceAmount: 128.00, remark: '' }, + { id: 'b3', year: 2026, month: 3, week: 2, salesman: '李专员', billDate: '2026/3/9', plateNo: '沪B11111', passTime: '2026-03-09 14:30:22', entry: '上海徐泾', exit: '苏州城区', fee: 86.00, vehicleType: '4.5T', nature: '自营', customerName: '上海某某运输公司', isInvoiced: '否', invoiceAmount: 0, remark: '' } + ]); + var billData = billList[0]; + var totalEtcFee = useMemo(function () { return billData.reduce(function (acc, r) { return acc + (Number(r.fee) || 0); }, 0); }, [billData]); + + var billColumns = [ + { title: '年份', dataIndex: 'year', key: 'year', width: 72 }, + { title: '月份', dataIndex: 'month', key: 'month', width: 64 }, + { title: '周', dataIndex: 'week', key: 'week', width: 56 }, + { title: '业务员', dataIndex: 'salesman', key: 'salesman', width: 90 }, + { title: '账单日期', dataIndex: 'billDate', key: 'billDate', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '通行时间', dataIndex: 'passTime', key: 'passTime', width: 172 }, + { title: '入口', dataIndex: 'entry', key: 'entry', width: 100 }, + { title: '出口', dataIndex: 'exit', key: 'exit', width: 100 }, + { title: '通行费用', dataIndex: 'fee', key: 'fee', width: 96, align: 'right', render: function (v) { return (v != null ? Number(v).toFixed(2) : '0.00') + ' 元'; } }, + { title: '车型', dataIndex: 'vehicleType', key: 'vehicleType', width: 72 }, + { title: '性质', dataIndex: 'nature', key: 'nature', width: 72 }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true }, + { title: '是否已开票', dataIndex: 'isInvoiced', key: 'isInvoiced', width: 96 }, + { title: '开票金额', dataIndex: 'invoiceAmount', key: 'invoiceAmount', width: 96, align: 'right', render: function (v) { return (v != null ? Number(v).toFixed(2) : '0.00') + ' 元'; } }, + { title: '备注', dataIndex: 'remark', key: 'remark', width: 100, ellipsis: true }, + { title: '操作', key: 'action', width: 160, fixed: 'right', render: function (_, record) { + return React.createElement(React.Fragment, null, + React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { message.info('删除 ' + record.id); } }, '删除'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('编辑 ' + record.id); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('查看 ' + record.id); } }, '查看') + ); + } } + ]; + + // ========== ETC余额管理 Tab ========== + var balanceFilterCustomer = useState(undefined); + var balanceFilterStatus = useState(undefined); + var balanceFilterProject = useState(undefined); + var balanceFilterMin = useState(''); + var balanceFilterMax = useState(''); + var thresholdModalVisible = useState(false); + var thresholdModalRecord = useState(null); + var balanceList = useState([ + { id: 'c1', type: 'customer', customerName: '嘉兴某某物流有限公司', projectCount: 2, status: '正常', totalBalance: 12580.00, rechargeTotal: 20000.00, consumeTotal: 7420.00, lastRemitDate: '2026-03-01', threshold: 1000, projects: [ + { id: 'c1-p1', type: 'project', projectName: '嘉兴氢能示范项目' }, + { id: 'c1-p2', type: 'project', projectName: '嘉兴城配项目' } + ]}, + { id: 'c2', type: 'customer', customerName: '上海某某运输公司', projectCount: 1, status: '预警', totalBalance: 860.00, rechargeTotal: 5000.00, consumeTotal: 4140.00, lastRemitDate: '2026-03-05', threshold: 1000, projects: [ + { id: 'c2-p1', type: 'project', projectName: '上海物流租赁项目' } + ]}, + { id: 'c3', type: 'customer', customerName: '杭州某某租赁有限公司', projectCount: 1, status: '欠费', totalBalance: -200.00, rechargeTotal: 3000.00, consumeTotal: 3200.00, lastRemitDate: '2026-02-20', threshold: 500, projects: [ + { id: 'c3-p1', type: 'project', projectName: '杭州城配租赁项目' } + ]} + ]); + var balanceData = balanceList[0]; + + var balanceColumns = [ + { title: '客户名称/项目名称', key: 'name', width: 220, render: function (_, record) { + if (record.type === 'customer') return React.createElement('span', { style: { fontWeight: 600 } }, record.customerName); + return React.createElement('span', { style: { paddingLeft: 24 } }, record.projectName); + } }, + { title: '项目数量', dataIndex: 'projectCount', key: 'projectCount', width: 92 }, + { title: '状态值', dataIndex: 'status', key: 'status', width: 88, render: function (v) { + if (!v) return '—'; + var color = v === '正常' ? 'success' : v === '预警' ? 'warning' : 'error'; + return React.createElement(Tag, { color: color }, v); + } }, + { title: '总账户余额', dataIndex: 'totalBalance', key: 'totalBalance', width: 120, align: 'right', render: function (v) { return v != null ? (Number(v).toFixed(2) + ' 元') : '—'; } }, + { title: '客户充值余额', dataIndex: 'rechargeTotal', key: 'rechargeTotal', width: 120, align: 'right', render: function (v) { return v != null ? (Number(v).toFixed(2) + ' 元') : '—'; } }, + { title: '客户消耗ETC费用', dataIndex: 'consumeTotal', key: 'consumeTotal', width: 130, align: 'right', render: function (v) { return v != null ? (Number(v).toFixed(2) + ' 元') : '—'; } }, + { title: '最近汇款日期', dataIndex: 'lastRemitDate', key: 'lastRemitDate', width: 118 }, + { title: '操作', key: 'action', width: 180, fixed: 'right', render: function (_, record) { + if (record.type === 'customer') { + return React.createElement(React.Fragment, null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { thresholdModalRecord[1](record); thresholdModalVisible[1](true); } }, '阈值设置'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转账单明细:' + record.customerName); } }, '账单明细') + ); + } + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('项目账单明细:' + record.projectName); } }, '账单明细'); + } } + ]; + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var labelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var formRowStyle = { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px 24px', marginBottom: 16 }; + var formItemStyle = { marginBottom: 12 }; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '业务管理' }, + { title: 'ETC管理' } + ] + }) + ), + React.createElement(Card, { style: cardStyle }, + React.createElement(Tabs, { + activeKey: activeTab[0], + onChange: setActiveTab, + items: [ + { + key: 'recharge', + label: '充值明细', + children: React.createElement(React.Fragment, null, + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户名称'), + React.createElement(Select, { placeholder: '请选择客户', allowClear: true, style: { width: '100%' }, value: filterCustomer[0], onChange: filterCustomer[1], options: [{ label: '嘉兴某某物流有限公司', value: '嘉兴某某物流有限公司' }, { label: '上海某某运输公司', value: '上海某某运输公司' }, { label: '杭州某某租赁有限公司', value: '杭州某某租赁有限公司' }] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '汇款日期'), + React.createElement(DatePicker.RangePicker, { style: { width: '100%' }, value: [filterRemitStart[0], filterRemitEnd[0]], onChange: function (dates) { filterRemitStart[1](dates && dates[0] || null); filterRemitEnd[1](dates && dates[1] || null); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '收款公司'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: filterReceiveCompany[0], onChange: filterReceiveCompany[1], options: receiveCompanyOptions }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '入账类型'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: filterEntryType[0], onChange: filterEntryType[1], options: entryTypeOptions }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '充值编号'), + React.createElement(Input, { placeholder: '请输入充值编号', value: filterRechargeNo[0], onChange: function (e) { filterRechargeNo[1](e.target.value); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '审核状态'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: filterAuditStatus[0], onChange: filterAuditStatus[1], options: [{ label: '待审核', value: '待审核' }, { label: '已驳回', value: '已驳回' }, { label: '已入账', value: '已入账' }] }) + ) + ), + React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 8 } }, + React.createElement(Button, { onClick: function () { filterCustomer[1](undefined); filterRemitStart[1](null); filterRemitEnd[1](null); filterReceiveCompany[1](undefined); filterEntryType[1](undefined); filterRechargeNo[1](''); filterAuditStatus[1](undefined); } }, '重置'), + React.createElement(Button, { type: 'primary', onClick: function () { message.info('查询'); } }, '查询'), + React.createElement(Button, { type: 'primary', onClick: function () { addRechargeVisible[1](true); } }, '新增') + ), + React.createElement(Table, { rowKey: 'id', columns: rechargeColumns, dataSource: filteredRecharge, scroll: { x: 1800 }, pagination: { showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } } }) + ) + }, + { + key: 'bill', + label: 'ETC账单明细', + children: React.createElement(React.Fragment, null, + React.createElement('div', { style: { marginBottom: 16, padding: 16, background: '#e6f7ff', borderRadius: 8, border: '1px solid #91d5ff' } }, + React.createElement('span', { style: { fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, '总ETC通行金额:'), + React.createElement('span', { style: { fontSize: 20, fontWeight: 600, color: '#1890ff', marginLeft: 8 } }, totalEtcFee.toFixed(2), ' 元') + ), + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '年份'), + React.createElement(Select, { style: { width: '100%' }, value: billFilterYear[0], onChange: billFilterYear[1], options: [{ label: '2026', value: 2026 }, { label: '2027', value: 2027 }] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '时间区间'), + React.createElement(DatePicker.RangePicker, { style: { width: '100%' }, value: billFilterDateRange[0], onChange: billFilterDateRange[1] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单日期'), + React.createElement(DatePicker.RangePicker, { style: { width: '100%' }, value: billFilterBillDateRange[0], onChange: billFilterBillDateRange[1] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '通行日期'), + React.createElement(DatePicker, { style: { width: '100%' }, value: billFilterPassDate[0], onChange: billFilterPassDate[1] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '车牌号'), + React.createElement(Input, { placeholder: '请输入车牌号', value: billFilterPlate[0], onChange: function (e) { billFilterPlate[1](e.target.value); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '车辆性质'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: billFilterVehicleNature[0], onChange: billFilterVehicleNature[1], options: [{ label: '租赁', value: '租赁' }, { label: '自营', value: '自营' }, { label: '异动', value: '异动' }, { label: '维修', value: '维修' }, { label: '库存', value: '库存' }] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户名称'), + React.createElement(Input, { placeholder: '请输入客户名称', value: billFilterCustomer[0], onChange: function (e) { billFilterCustomer[1](e.target.value); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '是否对账'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: billFilterReconciled[0], onChange: billFilterReconciled[1], options: [{ label: '是', value: '是' }, { label: '否', value: '否' }] }) + ) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Button, { type: 'primary', onClick: function () { invoiceConfirmVisible[1](true); } }, '提交开票') + ), + React.createElement(Table, { rowKey: 'id', columns: billColumns, dataSource: billData, scroll: { x: 1900 }, pagination: { showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } } }) + ) + }, + { + key: 'balance', + label: 'ETC余额管理', + children: React.createElement(React.Fragment, null, + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户名称'), + React.createElement(Input, { placeholder: '请输入客户名称', value: balanceFilterCustomer[0], onChange: function (e) { balanceFilterCustomer[1](e.target.value); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '状态值'), + React.createElement(Select, { placeholder: '请选择', allowClear: true, style: { width: '100%' }, value: balanceFilterStatus[0], onChange: balanceFilterStatus[1], options: [{ label: '正常', value: '正常' }, { label: '预警', value: '预警' }, { label: '欠费', value: '欠费' }] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '项目名称'), + React.createElement(Input, { placeholder: '请输入项目名称', value: balanceFilterProject[0], onChange: function (e) { balanceFilterProject[1](e.target.value); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '余额范围'), + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement(Input, { placeholder: '最小值', value: balanceFilterMin[0], onChange: function (e) { balanceFilterMin[1](e.target.value); } }), + React.createElement('span', null, '-'), + React.createElement(Input, { placeholder: '最大值', value: balanceFilterMax[0], onChange: function (e) { balanceFilterMax[1](e.target.value); } }) + ) + ) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Button, { onClick: function () { message.info('导出'); } }, '导出'), + React.createElement(Button, { style: { marginLeft: 8 } }, '批量阈值设置') + ), + React.createElement(Table, { + rowKey: 'id', + columns: balanceColumns, + dataSource: balanceData, + scroll: { x: 1200 }, + pagination: false, + expandable: { + expandedRowRender: function (record) { + if (!record.projects || !record.projects.length) return null; + return React.createElement('div', { style: { padding: '8px 24px', background: '#fafafa' } }, + record.projects.map(function (p) { return React.createElement('div', { key: p.id, style: { padding: '4px 0' } }, p.projectName); }) + ); + }, + rowExpandable: function (record) { return record.projects && record.projects.length > 0; } + } + }) + ) + } + ] + }) + ), + // 新增充值信息弹窗 + React.createElement(Modal, { + title: '新增充值信息', + open: addRechargeVisible[0], + onCancel: function () { addRechargeVisible[1](false); }, + width: 640, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: function () { addRechargeVisible[1](false); } }, '取消'), + React.createElement(Button, { key: 'submit', type: 'primary', onClick: handleAddRechargeSubmit }, '提交') + ] + }, React.createElement(React.Fragment, null, + React.createElement('div', { style: { fontSize: 15, fontWeight: 600, marginBottom: 12, paddingBottom: 8, borderBottom: '1px solid #f0f0f0' } }, '客户信息'), + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '客户名称'), + React.createElement(Select, { placeholder: '请选择/录入客户', allowClear: true, style: { width: '100%' }, value: addFormCustomer[0], onChange: function (v) { addFormCustomer[1](v); addFormRemitAccount[1](v ? v + '(自动带出)' : ''); addFormEtcBalance[1](v ? '12580.00' : ''); }, options: [{ label: '嘉兴某某物流有限公司', value: '嘉兴某某物流有限公司' }, { label: '上海某某运输公司', value: '上海某某运输公司' }] }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '汇款账户名'), + React.createElement(Input, { value: addFormRemitAccount[0], disabled: true, placeholder: '选择客户后自动带出' }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, 'ETC账户余额'), + React.createElement(Input, { value: addFormEtcBalance[0], disabled: true, placeholder: '选择客户后同步展示' }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '汇款日期'), + React.createElement(DatePicker, { style: { width: '100%' }, value: addFormRemitDate[0], onChange: addFormRemitDate[1] }) + ) + ), + React.createElement('div', { style: { fontSize: 15, fontWeight: 600, marginBottom: 12, marginTop: 20, paddingBottom: 8, borderBottom: '1px solid #f0f0f0' } }, '我司信息'), + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '收款公司'), + React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: addFormReceiveCompany[0], onChange: addFormReceiveCompany[1], options: receiveCompanyOptions }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '操作类型'), + React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: addFormOpType[0], onChange: addFormOpType[1], options: opTypeOptions }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '充值金额(¥)'), + React.createElement(Input, { type: 'number', placeholder: '请输入', value: addFormAmount[0], onChange: function (e) { addFormAmount[1](e.target.value); } }), + React.createElement('div', { style: { fontSize: 12, color: '#666', marginTop: 4 } }, '当前账户余额 + 本次变动金额 = 预计账户余额(选择客户后展示)') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, React.createElement('span', { style: { color: '#ff4d4f' } }, '*'), '收款银行'), + React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: addFormReceiveBank[0], onChange: addFormReceiveBank[1], options: [{ label: '工商银行杭州分行', value: '工商银行杭州分行' }, { label: '建设银行上海分行', value: '建设银行上海分行' }, { label: '农业银行杭州分行', value: '农业银行杭州分行' }] }) + ) + ), + React.createElement('div', { style: { fontSize: 15, fontWeight: 600, marginBottom: 12, marginTop: 20, paddingBottom: 8, borderBottom: '1px solid #f0f0f0' } }, '凭证上传'), + React.createElement('div', { style: formItemStyle }, + React.createElement(Upload, { multiple: true, fileList: addFormFiles[0], beforeUpload: function () { return false; }, onChange: function (info) { addFormFiles[1](info.fileList.slice(-5)); } }, + React.createElement(Button, null, '点击上传或拖拽文件') + ) + ) + )), + // 删除二次确认 + React.createElement(Modal, { + title: '确认删除', + open: deleteConfirmVisible[0], + onOk: handleDeleteRecharge, + onCancel: function () { deleteConfirmVisible[1](false); deleteConfirmRecord[1](null); }, + okText: '确认', + cancelText: '取消' + }, '确认删除该条充值记录?'), + // 提交开票二次确认 + React.createElement(Modal, { + title: '确认开票', + open: invoiceConfirmVisible[0], + onOk: function () { invoiceConfirmVisible[1](false); message.success('开票操作已提交'); }, + onCancel: function () { invoiceConfirmVisible[1](false); }, + okText: '确认', + cancelText: '取消' + }, '确认无误后执行开票操作?'), + // 阈值设置弹窗 + React.createElement(Modal, { + title: '阈值设置', + open: thresholdModalVisible[0], + onOk: function () { thresholdModalVisible[1](false); thresholdModalRecord[1](null); message.success('阈值已保存'); }, + onCancel: function () { thresholdModalVisible[1](false); thresholdModalRecord[1](null); }, + okText: '确定', + cancelText: '取消' + }, thresholdModalRecord[0] ? React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户:' + (thresholdModalRecord[0].customerName || '')), + React.createElement('div', { style: labelStyle }, '余额阈值(需大于0)'), + React.createElement(Input, { type: 'number', placeholder: '请输入阈值', defaultValue: thresholdModalRecord[0].threshold }) + ) : null) + ); +}; diff --git a/web端/业务管理/交车任务.jsx b/web端/业务管理/交车任务.jsx index a9517da..18f747d 100644 --- a/web端/业务管理/交车任务.jsx +++ b/web端/业务管理/交车任务.jsx @@ -1,6 +1,1124 @@ // 【重要】必须使用 const Component 作为组件变量名 // 数字化资产ONEOS运管平台 - 业务管理 - 交车任务 +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 Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Space = antd.Space; + var Popover = antd.Popover; + var Modal = antd.Modal; + var DatePicker = antd.DatePicker; + var message = antd.message; + var RangePicker = DatePicker.RangePicker; + + // 筛选条件 + var filterDeliveryTaskCode = useState(undefined); + var filterContractCode = useState(undefined); + var filterProjectName = useState(undefined); + var filterCustomerName = useState(undefined); + + // 分页(主表) + var pageState = useState(1); + var pageSizeState = useState(10); + var currentPage = pageState[0]; + var setCurrentPage = pageState[1]; + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + // 主表展开:只展开一行 + var expandedRowKeysState = useState([]); + var expandedRowKeys = expandedRowKeysState[0]; + var setExpandedRowKeys = expandedRowKeysState[1]; + + // 子表交车数量气泡(只开一个) + var deliveryPopoverOpen = useState(null); + + // 筛选展开(默认显示一行) + var filterExpanded = useState(false); + + // 需求说明弹窗 + var reqSpecOpen = useState(false); + // 激活弹窗:重新设置预计交车日期、开始计费日期 + var activateModalOpen = useState(false); + var activateRecord = useState(null); + var activatePlanRange = useState(null); + var activateBillingDate = useState(null); + + function buildDeliveryTaskCode(contractCode, seq) { + var suffix = 'JC' + ('0000' + String(seq != null ? seq : '')).slice(-4); + return String(contractCode || '') + suffix; + } + + function getDeliveryStatus(detailRecord) { + var vehicles = (detailRecord && detailRecord.vehicles) || []; + if (!vehicles.length) return '未交车'; + var allDelivered = vehicles.every(function (v) { return v && v.actualDeliveryDate; }); + return allDelivered ? '已交车' : '未交车'; + } + + // 模拟:主表按合同聚合,子表为交车任务 + var initialMainList = useMemo(function () { + return [ + { + contractCode: 'JXZL20260216YW101235A', + projectName: '嘉兴氢能运输项目', + customerName: '嘉兴某某物流有限公司', + businessDept: '业务1部', + businessPerson: '张经理', + contractEffectiveDate: '2026-02-16', + contractEndDate: '2027-02-15', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-03-01至2026-03-05', + deliveryCount: 2, + vehicles: [ + { brand: '东风', model: 'DFH1180', plateNo: '浙A12345', actualDeliveryDate: '2026-03-02', deliverer: '运维李' }, + { brand: '福田', model: 'BJ1180', plateNo: '浙A23456', actualDeliveryDate: '2026-03-03', deliverer: '运维王' } + ], + billingStartDate: '2026-03-01', + needReturnCar: '需要', + creator: '张经理', + createdAt: '2026-02-20', + enabled: true + }, + { + seq: 2, + planDeliveryDisplay: '2026-03-08', + deliveryCount: 1, + vehicles: [{ brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567', actualDeliveryDate: '', deliverer: '' }], + billingStartDate: '2026-03-08', + needReturnCar: '不需要', + creator: '张经理', + createdAt: '2026-02-22', + enabled: false + } + ] + }, + { + contractCode: 'SHZL20260210YW101200A', + projectName: '上海物流租赁项目', + customerName: '上海某某运输公司', + businessDept: '业务2部', + businessPerson: '李专员', + contractEffectiveDate: '2026-02-10', + contractEndDate: '2027-02-09', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-03-10', + deliveryCount: 1, + vehicles: [{ brand: '解放', model: 'JH6', plateNo: '沪A30003', actualDeliveryDate: '2026-03-10', deliverer: '运维李' }], + billingStartDate: '2026-03-10', + needReturnCar: '不需要', + creator: '李专员', + createdAt: '2026-02-21', + enabled: true + } + ] + }, + { + contractCode: 'JXZL20260215YW101234A', + projectName: '杭州城配租赁项目', + customerName: '杭州某某租赁有限公司', + businessDept: '业务3部', + businessPerson: '王专员', + contractEffectiveDate: '2026-02-15', + contractEndDate: '2027-02-14', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-02-28至2026-03-02', + deliveryCount: 3, + vehicles: [ + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', actualDeliveryDate: '2026-02-28', deliverer: '运维李' }, + { brand: '品牌B', model: '型号B1', plateNo: '浙B20002', actualDeliveryDate: '2026-03-01', deliverer: '运维王' }, + { brand: '品牌C', model: '型号C1', plateNo: '浙C50003', actualDeliveryDate: '2026-03-02', deliverer: '运维赵' } + ], + billingStartDate: '2026-02-28', + needReturnCar: '需要', + creator: '王专员', + createdAt: '2026-02-18', + enabled: true + } + ] + } + ]; + }, []); + + var mainListState = useState(initialMainList); + var mainList = mainListState[0]; + var setMainList = mainListState[1]; + + // 下拉 options(合同编码/项目/客户) + var filterOptions = useMemo(function () { + var codes = []; + var deliveryTaskCodes = []; + var projects = []; + var customers = []; + 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); + (r.children || []).forEach(function (c) { + var full = buildDeliveryTaskCode(r.contractCode, c.seq); + if (full && deliveryTaskCodes.indexOf(full) === -1) deliveryTaskCodes.push(full); + }); + }); + return { + deliveryTaskCode: deliveryTaskCodes.map(function (v) { return { value: v, label: v }; }), + 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 }; }) + }; + }, [mainList]); + + function selectFuzzy(input, opt) { + return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; + } + + function resetFilters() { + filterDeliveryTaskCode[1](undefined); + filterContractCode[1](undefined); + filterProjectName[1](undefined); + filterCustomerName[1](undefined); + setCurrentPage(1); + } + + function handleQuery() { + setCurrentPage(1); + message.info('已按筛选条件查询'); + } + + function goView(record) { + message.info('查看交车任务(原型)'); + } + + function goEdit(record) { + message.info('编辑交车任务(原型)'); + } + + function toggleEnable(record) { + if (record.enabled) { + Modal.confirm({ + title: '确认挂起', + content: '挂起交车任务会释放清空该交车任务车牌号以释放车辆,是否确定', + okText: '确定', + cancelText: '取消', + onOk: function () { + var parent = record._parentRecord; + setMainList(function (prev) { + return prev.map(function (p) { + if (!parent || p.contractCode !== parent.contractCode) return p; + var next = {}; + for (var k in p) next[k] = p[k]; + next.children = (p.children || []).map(function (c) { + if (c.seq !== record.seq) return c; + var vehicles = (c.vehicles || []).map(function (v) { + return Object.assign({}, v, { plateNo: '', actualDeliveryDate: '', deliverer: '' }); + }); + return Object.assign({}, c, { enabled: false, vehicles: vehicles }); + }); + return next; + }); + }); + message.success('已挂起'); + } + }); + } else { + activateRecord[1](record); + activatePlanRange[1](null); + activateBillingDate[1](null); + activateModalOpen[1](true); + } + } + + function handleActivateConfirm() { + var record = activateRecord[0]; + var planRange = activatePlanRange[0]; + var billingDate = activateBillingDate[0]; + if (!record) { activateModalOpen[1](false); return; } + var toDateStr = function (d) { return d && typeof d.format === 'function' ? d.format('YYYY-MM-DD') : (d && typeof d === 'string' ? d : ''); }; + var planStr = planRange && planRange.length >= 1 && planRange[0] + ? (planRange.length >= 2 && planRange[1] ? toDateStr(planRange[0]) + '至' + toDateStr(planRange[1]) : toDateStr(planRange[0])) + : ''; + var billingStr = billingDate ? toDateStr(billingDate) : ''; + if (!planStr || !billingStr) { message.warning('请填写预计交车日期和开始计费日期'); return; } + var parent = record._parentRecord; + setMainList(function (prev) { + return prev.map(function (p) { + if (!parent || p.contractCode !== parent.contractCode) return p; + var next = {}; + for (var k in p) next[k] = p[k]; + next.children = (p.children || []).map(function (c) { + if (c.seq !== record.seq) return c; + return Object.assign({}, c, { enabled: true, planDeliveryDisplay: planStr, billingStartDate: billingStr }); + }); + return next; + }); + }); + activateModalOpen[1](false); + activateRecord[1](null); + message.success('已激活'); + } + + // 筛选后的主表 + var filteredMainList = useMemo(function () { + var list = mainList.slice(); + var contractCode = filterContractCode[0]; + var projectName = filterProjectName[0]; + var customerName = filterCustomerName[0]; + var deliveryTaskCode = filterDeliveryTaskCode[0]; + + if (contractCode) list = list.filter(function (r) { return r.contractCode === contractCode; }); + if (projectName) list = list.filter(function (r) { return r.projectName === projectName; }); + if (customerName) list = list.filter(function (r) { return r.customerName === customerName; }); + + // 交车任务编码:选择器(同合同编码交互),筛选时命中任一子表编码即可 + if (deliveryTaskCode) { + list = list.filter(function (r) { + var children = r.children || []; + return children.some(function (c) { + return buildDeliveryTaskCode(r.contractCode, c.seq) === deliveryTaskCode; + }); + }); + } + + return list; + }, [mainList, filterContractCode[0], filterProjectName[0], filterCustomerName[0], filterDeliveryTaskCode[0]]); + + // 主表 dataSource 去掉 children,避免 Table 误用 treeData 产生空白行 + 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]); + + var paginatedMain = useMemo(function () { + var start = (currentPage - 1) * pageSize; + return mainTableDataSource.slice(start, start + pageSize); + }, [mainTableDataSource, currentPage, pageSize]); + + function renderDeliveryPopover(record) { + var vehicles = record.vehicles || []; + var parentCode = (record._parentRecord && record._parentRecord.contractCode) || ''; + var key = parentCode + '-' + (record.seq != null ? record.seq : ''); + + 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: 520 } }, + 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('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 || '—'), + React.createElement('td', { style: tdStyle }, v.actualDeliveryDate ? v.actualDeliveryDate : '-'), + React.createElement('td', { style: tdStyle }, v.deliverer ? v.deliverer : '-') + ); + }) + ) + ) + ); + + var count = record.deliveryCount != null ? Number(record.deliveryCount) : 0; + return React.createElement(Popover, { + content: content, + title: '车辆明细', + trigger: 'click', + open: deliveryPopoverOpen[0] === key, + onOpenChange: function (open) { deliveryPopoverOpen[1](open ? key : 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: 200, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 110, render: function (v) { return v || '—'; } }, + { title: '业务负责人', dataIndex: 'businessPerson', key: 'businessPerson', width: 110, render: function (v) { return v || '—'; } }, + { title: '合同生效日期', dataIndex: 'contractEffectiveDate', key: 'contractEffectiveDate', width: 120, render: function (v) { return v || '—'; } }, + { title: '合同结束日期', dataIndex: 'contractEndDate', key: 'contractEndDate', width: 120, render: function (v) { return v || '—'; } } + ]; + + var subColumns = [ + { + title: '交车任务编码', + key: 'deliveryTaskCode', + width: 260, + ellipsis: true, + render: function (_, record) { + var p = record._parentRecord; + return buildDeliveryTaskCode(p && p.contractCode, record.seq) || '—'; + } + }, + { title: '任务状态', key: 'taskStatus', width: 88, render: function (_, record) { return record.enabled !== false ? '激活' : '挂起'; } }, + { title: '交车状态', key: 'deliveryStatus', width: 100, render: function (_, record) { return getDeliveryStatus(record); } }, + { title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 160, render: function (v) { return v || '—'; } }, + { title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, record) { return renderDeliveryPopover(record); } }, + { title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120, render: function (v) { return v || '—'; } }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 100, render: function (v) { return v || '—'; } }, + { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 110, render: function (v) { return v || '—'; } }, + { + title: '操作', + key: 'action', + width: 200, + fixed: 'right', + render: function (_, record) { + return React.createElement(Space, { size: 'small' }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goEdit(record); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { toggleEnable(record); } }, record.enabled ? '挂起' : '激活') + ); + } + } + ]; + + function expandRowRender(record) { + var rows = (record._detailList || []).map(function (r) { + var o = {}; + for (var k in r) o[k] = r[k]; + o._parentRecord = record; + return o; + }); + + return React.createElement('div', { style: { marginBottom: 0, paddingLeft: expandColumnWidth, boxSizing: 'border-box' } }, + React.createElement(Table, { + rowKey: function (r) { return (record.contractCode || '') + '-' + (r.seq != null ? r.seq : Math.random()); }, + columns: subColumns, + dataSource: rows, + pagination: false, + size: 'small', + bordered: true, + scroll: { x: 1380 } + }) + ); + } + + var reqSpecText = + "交车任务\n" + + "一个「数字化资产ONEOS运管平台」中的「交车任务」模块\n" + + "1.面包屑:\n" + + "1.1.业务管理-交车任务\n\n" + + "2.筛选:\n" + + "支持通过合同编码、项目名称、客户名称进行筛选,右侧为查询、重置按钮;\n" + + "2.1.交车任务编码:选择器,默认所有交车任务编码,提示信息为:请输入或选择交车任务编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n" + + " 编码规则为:[合同编码][交车任务编码],主要用于后期与用友YS系统打通时获取财务收款及发票相关数据;\n" + + " 前缀为合同编码,后缀为交车任务编码,规则为:JC+4位编号,为该合同下第x份交车任务,例如:JC0001为该合同下第1份交车任务,依次类推;\n" + + " 例如:JXZL20260216YW101235AJC0001即为JXZL20260216YW101235A合同下第1份账单;\n" + + "2.2.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n" + + "2.3.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n" + + "2.4.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n" + + "2.5.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;\n" + + "2.6.重置:点击清空查询条件至默认;\n\n" + + "3.列表:\n" + + "#嵌套子表格,分为主表和子表,右侧为新增按钮;\n" + + "3.1.主表展示以下内容:合同编码、项目名称、客户名称、业务部门、业务负责人、合同生效日期、合同结束日期;\n" + + "3.1.1合同编码:显示对应合同编码;\n" + + "3.1.2.项目名称:显示对应项目名称;\n" + + "3.1.3.客户名称:显示对应客户名称;\n" + + "3.1.4.业务部门:显示业务部门名称;\n" + + "3.1.5.业务负责人:显示业务负责人名称;\n" + + "3.1.6.合同生效日期:显示合同生效日期,格式为:YYYY-MM-DD;\n" + + "3.1.7.合同结束日期:显示合同结束日期,格式为:YYYY-MM-DD;\n\n" + + "3.2.子表展示以下内容:交车任务编码、预计交车日期、交车数量、开始计费日期、是否需要退还车、创建人、创建时间;\n" + + "3.2.1.交车任务编码:编码规则为:[合同编码][交车任务编码],主要用于后期与用友YS系统打通时获取财务收款及发票相关数据;\n" + + " 前缀为合同编码,后缀为交车任务编码,规则为:JC+4位编号,为该合同下第x份交车任务,例如:JC0001为该合同下第1份交车任务,依次类推;\n" + + " 例如:JXZL20260216YW101235AJC0001即为JXZL20260216YW101235A合同下第1份账单;\n" + + "3.2.2.任务状态:显示挂起/激活,挂起的数据操作列对应激活、激活的数据操作列对应挂起;\n" + + "3.2.3.交车状态:显示交车任务状态,分为:已交车、未交车;\n" + + "3.2.4.预计交车日期:显示该交车任务预计交车日期,可能是某天,也可能是开始-结束时间段,格式分别为:YYYY-MM-DD、YYYY-MM-DD至YYYY-MM-DD/;\n" + + "3.2.5.交车数量:显示实际交车数量,格式为:xx辆,点击数量弹出气泡卡片,列表显示:品牌、型号、车牌号、实际交车日期、交车人,其中实际交车日期和交车人没交车时显示为-;\n" + + "3.2.6.开始计费日期:显示该交车任务开始计费日期,格式为:YYYY-MM-DD;\n" + + "3.2.7.创建人:显示交车任务的创建人用户姓名;\n" + + "3.2.8.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD;\n" + + "3.2.9.操作:支持查看、编辑、挂起/激活;\n" + + " 3.2.9.1.查看:跳转交车任务-查看页;\n" + + " 3.2.9.2.编辑:跳转交车任务-编辑页,已交车的任务不支持编辑;\n" + + " 3.2.9.3.挂起/激活:点击挂起,进行二次确认提示,提示文案:挂起交车任务会释放清空该交车任务车牌号以释放车辆,是否确定?;点击激活,弹框中重新设置预计交车日期、开始计费日期,设置成功后提示交车任务激活成功;\n\n" + + "4.列表右下方为分页功能,支持单页显示条目选择;"; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: [{ title: '业务管理' }, { title: '交车任务' }] }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqSpecOpen[1](true); } }, '查看需求说明') + ), + 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: filterDeliveryTaskCode[0], + onChange: function (v) { filterDeliveryTaskCode[1](v); }, + options: filterOptions.deliveryTaskCode, + filterOption: selectFuzzy + }) + ), + 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: selectFuzzy + }) + ), + 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: selectFuzzy + }) + ), + filterExpanded[0] ? 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: selectFuzzy + }) + ) : null + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { type: 'link', onClick: function () { filterExpanded[1](!filterExpanded[0]); } }, filterExpanded[0] ? '收起' : '展开'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { onClick: resetFilters }, '重置') + ) + ), + React.createElement(Card, { + title: '交车任务', + style: cardStyle, + extra: React.createElement(Button, { + type: 'primary', + onClick: function () { message.info('请参照原型:业务管理-交车任务-新增交车任务'); } + }, '新增') + }, + React.createElement(Table, { + rowKey: 'contractCode', + columns: mainColumns, + dataSource: paginatedMain, + expandable: { + expandedRowKeys: expandedRowKeys, + onExpandedRowsChange: function (keys) { setExpandedRowKeys(keys && keys.length ? [keys[keys.length - 1]] : []); }, + expandedRowRender: expandRowRender, + rowExpandable: function (record) { return record._detailList && record._detailList.length > 0; }, + columnWidth: expandColumnWidth + }, + pagination: { + current: currentPage, + pageSize: pageSize, + total: mainTableDataSource.length, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50', '100'], + onChange: function (p, ps) { setCurrentPage(p); setPageSize(ps); }, + onShowSizeChange: function () { setCurrentPage(1); } + }, + size: 'middle', + bordered: true, + scroll: { x: 1110 } + }) + ) + , + React.createElement(Modal, { + title: '激活交车任务', + open: activateModalOpen[0], + onCancel: function () { activateModalOpen[1](false); activateRecord[1](null); }, + width: 520, + okText: '确定', + cancelText: '取消', + onOk: handleActivateConfirm, + destroyOnClose: true + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, '预计交车日期'), + React.createElement(RangePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'], + value: activatePlanRange[0], + onChange: function (dates) { activatePlanRange[1](dates && dates.length === 2 ? dates : null); } + }), + React.createElement('div', { style: { marginTop: 4, fontSize: 12, color: '#999' } }, '支持单日或开始-结束时间段,单日时开始与结束选同一天即可') + ), + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('div', { style: { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, '开始计费日期'), + React.createElement(DatePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: '请选择日期', + value: (function () { + var v = activateBillingDate[0]; + if (v == null) return null; + if (typeof v.isValid === 'function') return v; + if (typeof v === 'string' && window.dayjs) return window.dayjs(v); + return null; + })(), + onChange: function (date) { activateBillingDate[1](date); } + }) + ) + )), + React.createElement(Modal, { + title: '需求说明', + open: reqSpecOpen[0], + onCancel: function () { reqSpecOpen[1](false); }, + width: 820, + footer: React.createElement(Button, { onClick: function () { reqSpecOpen[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', lineHeight: 1.7, padding: '4px 2px' } }, reqSpecText)) + ); +}; + +/* +// 【重要】必须使用 const Component 作为组件变量名 +// 数字化资产ONEOS运管平台 - 业务管理 - 交车任务 + +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 Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Space = antd.Space; + var Popover = antd.Popover; + var Modal = antd.Modal; + var message = antd.message; + + // 筛选条件 + var filterDeliveryTaskCode = useState(''); + var filterContractCode = useState(undefined); + var filterProjectName = useState(undefined); + var filterCustomerName = useState(undefined); + + // 分页(主表) + var pageState = useState(1); + var pageSizeState = useState(10); + var currentPage = pageState[0]; + var setCurrentPage = pageState[1]; + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + // 主表展开:只展开一行 + var expandedRowKeysState = useState([]); + var expandedRowKeys = expandedRowKeysState[0]; + var setExpandedRowKeys = expandedRowKeysState[1]; + + // 子表交车数量气泡(只开一个) + var deliveryPopoverOpen = useState(null); + + function buildDeliveryTaskCode(contractCode, seq) { + var suffix = 'JC' + ('0000' + String(seq != null ? seq : '')).slice(-4); + return String(contractCode || '') + suffix; + } + + // 模拟:主表按合同聚合,子表为交车任务 + var initialMainList = useMemo(function () { + return [ + { + contractCode: 'JXZL20260216YW101235A', + projectName: '嘉兴氢能运输项目', + customerName: '嘉兴某某物流有限公司', + businessDept: '业务1部', + businessPerson: '张经理', + contractEffectiveDate: '2026-02-16', + contractEndDate: '2027-02-15', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-03-01至2026-03-05', + deliveryCount: 2, + vehicles: [ + { brand: '东风', model: 'DFH1180', plateNo: '浙A12345', actualDeliveryDate: '2026-03-02', deliverer: '运维李' }, + { brand: '福田', model: 'BJ1180', plateNo: '浙A23456', actualDeliveryDate: '2026-03-03', deliverer: '运维王' } + ], + billingStartDate: '2026-03-01', + needReturnCar: '需要', + creator: '张经理', + createdAt: '2026-02-20', + enabled: true + }, + { + seq: 2, + planDeliveryDisplay: '2026-03-08', + deliveryCount: 1, + vehicles: [{ brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567', actualDeliveryDate: '2026-03-08', deliverer: '运维赵' }], + billingStartDate: '2026-03-08', + needReturnCar: '不需要', + creator: '张经理', + createdAt: '2026-02-22', + enabled: false + } + ] + }, + { + contractCode: 'SHZL20260210YW101200A', + projectName: '上海物流租赁项目', + customerName: '上海某某运输公司', + businessDept: '业务2部', + businessPerson: '李专员', + contractEffectiveDate: '2026-02-10', + contractEndDate: '2027-02-09', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-03-10', + deliveryCount: 1, + vehicles: [{ brand: '解放', model: 'JH6', plateNo: '沪A30003', actualDeliveryDate: '2026-03-10', deliverer: '运维李' }], + billingStartDate: '2026-03-10', + needReturnCar: '不需要', + creator: '李专员', + createdAt: '2026-02-21', + enabled: true + } + ] + }, + { + contractCode: 'JXZL20260215YW101234A', + projectName: '杭州城配租赁项目', + customerName: '杭州某某租赁有限公司', + businessDept: '业务3部', + businessPerson: '王专员', + contractEffectiveDate: '2026-02-15', + contractEndDate: '2027-02-14', + children: [ + { + seq: 1, + planDeliveryDisplay: '2026-02-28至2026-03-02', + deliveryCount: 3, + vehicles: [ + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', actualDeliveryDate: '2026-02-28', deliverer: '运维李' }, + { brand: '品牌B', model: '型号B1', plateNo: '浙B20002', actualDeliveryDate: '2026-03-01', deliverer: '运维王' }, + { brand: '品牌C', model: '型号C1', plateNo: '浙C50003', actualDeliveryDate: '2026-03-02', deliverer: '运维赵' } + ], + billingStartDate: '2026-02-28', + needReturnCar: '需要', + creator: '王专员', + createdAt: '2026-02-18', + enabled: true + } + ] + } + ]; + }, []); + + var mainListState = useState(initialMainList); + var mainList = mainListState[0]; + var setMainList = mainListState[1]; + + // 下拉 options(合同编码/项目/客户) + var filterOptions = useMemo(function () { + var codes = []; + var projects = []; + var customers = []; + 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); + }); + 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 }; }) + }; + }, [mainList]); + + function selectFuzzy(input, opt) { + return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; + } + + function resetFilters() { + filterDeliveryTaskCode[1](''); + filterContractCode[1](undefined); + filterProjectName[1](undefined); + filterCustomerName[1](undefined); + setCurrentPage(1); + } + + function handleQuery() { + setCurrentPage(1); + message.info('已按筛选条件查询'); + } + + function goView(record) { + message.info('查看交车任务(原型)'); + } + + function goEdit(record) { + message.info('编辑交车任务(原型)'); + } + + function toggleEnable(record) { + if (record.enabled) { + Modal.confirm({ + title: '确认挂起', + content: '挂起交车任务会释放清空该交车任务车牌号以释放车辆,是否确定', + okText: '确定', + cancelText: '取消', + onOk: function () { + var parent = record._parentRecord; + setMainList(function (prev) { + return prev.map(function (p) { + if (!parent || p.contractCode !== parent.contractCode) return p; + var next = {}; + for (var k in p) next[k] = p[k]; + next.children = (p.children || []).map(function (c) { + if (c.seq !== record.seq) return c; + var vehicles = (c.vehicles || []).map(function (v) { + return Object.assign({}, v, { plateNo: '', actualDeliveryDate: '', deliverer: '' }); + }); + return Object.assign({}, c, { enabled: false, vehicles: vehicles }); + }); + return next; + }); + }); + message.success('已挂起'); + } + }); + } else { + activateRecord[1](record); + activatePlanRange[1](null); + activateBillingDate[1](null); + activateModalOpen[1](true); + } + } + + function handleActivateConfirm() { + var record = activateRecord[0]; + var planRange = activatePlanRange[0]; + var billingDate = activateBillingDate[0]; + if (!record) { activateModalOpen[1](false); return; } + var toDateStr = function (d) { return d && typeof d.format === 'function' ? d.format('YYYY-MM-DD') : (d && typeof d === 'string' ? d : ''); }; + var planStr = planRange && planRange.length >= 1 && planRange[0] + ? (planRange.length >= 2 && planRange[1] ? toDateStr(planRange[0]) + '至' + toDateStr(planRange[1]) : toDateStr(planRange[0])) + : ''; + var billingStr = billingDate ? toDateStr(billingDate) : ''; + if (!planStr || !billingStr) { message.warning('请填写预计交车日期和开始计费日期'); return; } + var parent = record._parentRecord; + setMainList(function (prev) { + return prev.map(function (p) { + if (!parent || p.contractCode !== parent.contractCode) return p; + var next = {}; + for (var k in p) next[k] = p[k]; + next.children = (p.children || []).map(function (c) { + if (c.seq !== record.seq) return c; + return Object.assign({}, c, { enabled: true, planDeliveryDisplay: planStr, billingStartDate: billingStr }); + }); + return next; + }); + }); + activateModalOpen[1](false); + activateRecord[1](null); + message.success('已激活'); + } + + // 筛选后的主表 + var filteredMainList = useMemo(function () { + var list = mainList.slice(); + var contractCode = filterContractCode[0]; + var projectName = filterProjectName[0]; + var customerName = filterCustomerName[0]; + var taskCodeKw = (filterDeliveryTaskCode[0] || '').trim().toLowerCase(); + + if (contractCode) list = list.filter(function (r) { return r.contractCode === contractCode; }); + if (projectName) list = list.filter(function (r) { return r.projectName === projectName; }); + if (customerName) list = list.filter(function (r) { return r.customerName === customerName; }); + + // 交车任务编码:支持模糊搜索(匹配完整编码:合同编码 + JCxxxx) + if (taskCodeKw) { + list = list.filter(function (r) { + var children = r.children || []; + return children.some(function (c) { + var full = buildDeliveryTaskCode(r.contractCode, c.seq); + return full.toLowerCase().indexOf(taskCodeKw) !== -1; + }); + }); + } + return list; + }, [mainList, filterContractCode[0], filterProjectName[0], filterCustomerName[0], filterDeliveryTaskCode[0]]); + + // 主表 dataSource 去掉 children,避免 Table 误用 treeData 产生空白行 + 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]); + + var paginatedMain = useMemo(function () { + var start = (currentPage - 1) * pageSize; + return mainTableDataSource.slice(start, start + pageSize); + }, [mainTableDataSource, currentPage, pageSize]); + + function renderDeliveryPopover(record) { + var vehicles = record.vehicles || []; + var parentCode = (record._parentRecord && record._parentRecord.contractCode) || ''; + var key = parentCode + '-' + (record.seq != null ? record.seq : ''); + + 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: 520 } }, + 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('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 || '—'), + React.createElement('td', { style: tdStyle }, v.actualDeliveryDate || '—'), + React.createElement('td', { style: tdStyle }, v.deliverer || '—') + ); + }) + ) + ) + ); + + var count = record.deliveryCount != null ? Number(record.deliveryCount) : 0; + return React.createElement(Popover, { + content: content, + title: '车辆明细', + trigger: 'click', + open: deliveryPopoverOpen[0] === key, + onOpenChange: function (open) { deliveryPopoverOpen[1](open ? key : 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: 200, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 110, render: function (v) { return v || '—'; } }, + { title: '业务负责人', dataIndex: 'businessPerson', key: 'businessPerson', width: 110, render: function (v) { return v || '—'; } }, + { title: '合同生效日期', dataIndex: 'contractEffectiveDate', key: 'contractEffectiveDate', width: 120, render: function (v) { return v || '—'; } }, + { title: '合同结束日期', dataIndex: 'contractEndDate', key: 'contractEndDate', width: 120, render: function (v) { return v || '—'; } } + ]; + + var subColumns = [ + { + title: '交车任务编码', + key: 'deliveryTaskCode', + width: 260, + ellipsis: true, + render: function (_, record) { + var p = record._parentRecord; + return buildDeliveryTaskCode(p && p.contractCode, record.seq) || '—'; + } + }, + { title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 160, render: function (v) { return v || '—'; } }, + { title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, record) { return renderDeliveryPopover(record); } }, + { title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120, render: function (v) { return v || '—'; } }, + { title: '是否需要退还车', dataIndex: 'needReturnCar', key: 'needReturnCar', width: 120, render: function (v) { return v || '—'; } }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 100, render: function (v) { return v || '—'; } }, + { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 110, render: function (v) { return v || '—'; } }, + { + title: '操作', + key: 'action', + width: 200, + fixed: 'right', + render: function (_, record) { + return React.createElement(Space, { size: 'small' }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goEdit(record); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { toggleEnable(record); } }, record.enabled ? '挂起' : '激活') + ); + } + } + ]; + + function expandRowRender(record) { + var rows = (record._detailList || []).map(function (r) { + var o = {}; + for (var k in r) o[k] = r[k]; + o._parentRecord = record; + return o; + }); + + return React.createElement('div', { style: { marginBottom: 0, paddingLeft: expandColumnWidth, boxSizing: 'border-box' } }, + React.createElement(Table, { + rowKey: function (r) { return (record.contractCode || '') + '-' + (r.seq != null ? r.seq : Math.random()); }, + columns: subColumns, + dataSource: rows, + pagination: false, + size: 'small', + bordered: true, + scroll: { x: 1400 } + }) + ); + } + + 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(Input, { + placeholder: '请输入交车任务编码,支持模糊搜索', + allowClear: true, + style: filterControlStyle, + value: filterDeliveryTaskCode[0], + onChange: function (e) { filterDeliveryTaskCode[1](e.target.value); } + }) + ), + 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: selectFuzzy + }) + ), + 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: selectFuzzy + }) + ), + 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: selectFuzzy + }) + ) + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { onClick: resetFilters }, '重置') + ) + ), + React.createElement(Card, { title: '列表', style: cardStyle }, + React.createElement(Table, { + rowKey: 'contractCode', + columns: mainColumns, + dataSource: paginatedMain, + expandable: { + expandedRowKeys: expandedRowKeys, + onExpandedRowsChange: function (keys) { setExpandedRowKeys(keys && keys.length ? [keys[keys.length - 1]] : []); }, + expandedRowRender: expandRowRender, + rowExpandable: function (record) { return record._detailList && record._detailList.length > 0; }, + columnWidth: expandColumnWidth + }, + pagination: { + current: currentPage, + pageSize: pageSize, + total: mainTableDataSource.length, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50', '100'], + onChange: function (p, ps) { setCurrentPage(p); setPageSize(ps); }, + onShowSizeChange: function () { setCurrentPage(1); } + }, + size: 'middle', + bordered: true, + scroll: { x: 1110 } + }) + ) + ); +}; + +// 【重要】必须使用 const Component 作为组件变量名 +// 数字化资产ONEOS运管平台 - 业务管理 - 交车任务 + const Component = function() { var useState = React.useState; var useMemo = React.useMemo; @@ -575,3 +1693,4 @@ const Component = function() { ) ); }; +*/ diff --git a/web端/业务管理/新增交车任务.jsx b/web端/业务管理/新增交车任务.jsx index 17cc43f..0962fcd 100644 --- a/web端/业务管理/新增交车任务.jsx +++ b/web端/业务管理/新增交车任务.jsx @@ -7,6 +7,7 @@ const Component = function() { var Select = antd.Select; var Button = antd.Button; var DatePicker = antd.DatePicker; + var Modal = antd.Modal; var message = antd.message; var Option = Select.Option; var Checkbox = antd.Checkbox; @@ -25,6 +26,11 @@ const Component = function() { var billingDateValue = billingDate[0]; var setBillingDate = billingDate[1]; + // 续签合同:是否需要还车(勾选“不需要还车”则可选已交车车辆),续签时默认不勾选 + var noReturnCarCheckedState = React.useState(false); + var noReturnCarChecked = noReturnCarCheckedState[0]; + var setNoReturnCarChecked = noReturnCarCheckedState[1]; + var selectedRowKeys = React.useState([]); var checkedRowKeys = selectedRowKeys[0]; var setCheckedRowKeys = selectedRowKeys[1]; @@ -33,9 +39,7 @@ const Component = function() { var errors = formErrors[0]; var setErrors = formErrors[1]; - var reqSpecState = React.useState(false); - var reqSpecOpen = reqSpecState[0]; - var setReqSpecOpen = reqSpecState[1]; + var reqModalOpen = React.useState(false); // Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示 var projectList = [ @@ -46,6 +50,13 @@ const Component = function() { { key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' }, { key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' } ]}, + { id: 'p1_r', name: '嘉兴某某物流氢能运输项目(续签合同)', isRenewal: true, contractCode: 'JXZL20270116YW101235B', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [ + { key: 'v1r_1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '续签合同车辆', deliveryStatus: 'none' }, + { key: 'v1r_0', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A09999', vin: 'LFV2BJCH8K3000000', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_0b', brand: '福田', model: '智蓝氢能轻卡', plateNo: '浙A08888', vin: 'LZYTBACR2M1000001', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_0c', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙A07777', vin: 'ZZ4257N386FZ00002', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_2', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '续签待上牌', deliveryStatus: 'none' } + ]}, { id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [ { key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' }, { key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' }, @@ -60,13 +71,31 @@ const Component = function() { ]; var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; }); + var isRenewalProject = !!(selectedProject && selectedProject.isRenewal); var vehicleListRaw = selectedProject ? selectedProject.vehicles : []; - var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; }); - var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; }); + // 2.8.1 勾选「不需要还车」:只显示已交未还车辆;2.8.2 不勾选:显示已交未还+续签新增车辆,已交未还不可勾选。非续签:已完成交车不显示,已提交不可选。 + var vehicleList = vehicleListRaw.filter(function(v) { + if (v.deliveryStatus === 'completed') { + if (!isRenewalProject) return false; + return true; + } + if (v.deliveryStatus === 'submitted') return true; + if (isRenewalProject && !!noReturnCarChecked) return false; + return true; + }); + var selectableVehicles = vehicleList.filter(function(v) { + if (v.deliveryStatus === 'submitted') return false; + if (v.deliveryStatus === 'completed') return isRenewalProject && !!noReturnCarChecked; + return true; + }); var handleProjectChange = function(id) { - setSelectedProjectId(id || ''); + var nextId = id || ''; + setSelectedProjectId(nextId); + var nextProject = projectList.find(function(p) { return p.id === nextId; }); + var isRenewal = !!(nextProject && nextProject.isRenewal); setCheckedRowKeys([]); + setNoReturnCarChecked(false); }; var todayStr = (function() { @@ -84,14 +113,15 @@ const Component = function() { return null; }; - var expectedDeliveryError = validateExpectedDelivery(); + var needExpectedDelivery = !(isRenewalProject && noReturnCarChecked); + var expectedDeliveryError = needExpectedDelivery ? validateExpectedDelivery() : null; var billingDateError = !billingDateValue ? '请选择开始计费日期' : null; var handleSubmit = function() { var err = {}; if (!selectedProjectId) err.projectName = '请选择项目名称'; - if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; + if (needExpectedDelivery && expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; if (billingDateError) err.billingDate = billingDateError; if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车'; setErrors(err); @@ -114,6 +144,17 @@ const Component = function() { else setCheckedRowKeys(function(prev) { return prev.filter(function(k) { return k !== record.key; }); }); }; + var onToggleNoReturnCar = function(e) { + var next = !!(e && e.target ? e.target.checked : e); + setNoReturnCarChecked(next); + if (!next) { + setCheckedRowKeys(function(prev) { + var completedKeys = vehicleList.filter(function(v) { return v && v.deliveryStatus === 'completed'; }).map(function(v) { return v.key; }); + return prev.filter(function(k) { return completedKeys.indexOf(k) === -1; }); + }); + } + }; + var styles = { page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, breadcrumb: { marginBottom: 16, color: '#666' }, @@ -139,37 +180,6 @@ const Component = function() { modalBody: { padding: 20, overflow: 'auto', flex: 1 } }; - var reqSpecBlock = { marginBottom: 16 }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 }; - var reqSpecP = { fontSize: 13, color: '#666', marginBottom: 4 }; - var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 }; - - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-新增交车任务')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'), - React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:必选项,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;'), - React.createElement('div', { style: reqSpecLi }, '2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码;'), - React.createElement('div', { style: reqSpecLi }, '2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称;'), - React.createElement('div', { style: reqSpecLi }, '2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务;'), - React.createElement('div', { style: reqSpecLi }, '2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点;'), - React.createElement('div', { style: reqSpecLi }, '2.6.预计交车日期:必填项,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期;'), - React.createElement('div', { style: reqSpecLi }, '2.7.开始计费日期:必填项,日期选择器,支持单日选择,格式为YYYY-MM-DD;'), - React.createElement('div', { style: reqSpecLi }, '2.8.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注;'), - React.createElement('div', { style: reqSpecLi }, '2.8.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中;'), - React.createElement('div', { style: reqSpecLi }, '2.8.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌;'), - React.createElement('div', { style: reqSpecLi }, '2.8.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号;'), - React.createElement('div', { style: reqSpecLi }, '2.8.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-;'), - React.createElement('div', { style: reqSpecLi }, '2.8.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码;'), - React.createElement('div', { style: reqSpecLi }, '2.8.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元;'), - React.createElement('div', { style: reqSpecLi }, '2.8.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元;'), - React.createElement('div', { style: reqSpecLi }, '2.8.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元;'), - React.createElement('div', { style: reqSpecLi }, '2.8.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-;'), - React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;') - )); - - var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; - var FormItem = function(props) { return React.createElement('div', { style: styles.formCol }, React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), @@ -183,7 +193,7 @@ const Component = function() { var formRow1 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName }, React.createElement(Select, { - placeholder: '请选择或输入项目名称', + placeholder: '请选择或输入项目名称(对应自营合同、租赁合同-项目名称)', style: { width: '100%' }, value: selectedProjectId || undefined, onChange: handleProjectChange, @@ -199,7 +209,7 @@ const Component = function() { ); var formRow2 = React.createElement('div', { style: styles.formRow }, - React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, + needExpectedDelivery ? React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, React.createElement(RangePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', @@ -207,7 +217,7 @@ const Component = function() { value: expectedDeliveryValue, onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); }, status: errors.expectedDelivery ? 'error' : undefined - })), + })) : null, React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate }, React.createElement(DatePicker, { style: { width: '100%' }, @@ -216,7 +226,17 @@ const Component = function() { value: billingDateValue, onChange: function(d, dateStr) { setBillingDate(d); }, status: errors.billingDate ? 'error' : undefined - })) + })), + isRenewalProject ? React.createElement(FormItem, { label: '已交车辆是否需要还车' }, + React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 8, lineHeight: 1.6 } }, + React.createElement(Checkbox, { checked: !!noReturnCarChecked, onChange: onToggleNoReturnCar }, '不需要还车'), + React.createElement('span', { style: { color: '#8c8c8c', fontSize: 12, marginTop: 2 } }, + '勾选不需要还车,则车辆无需交车,', + React.createElement('br', null), + '只设置开始计费日期即可重新生成账单' + ) + ) + ) : null ); var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); @@ -245,14 +265,25 @@ const Component = function() { ? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '请先选择项目名称,将自动带出该合同下车辆信息')) : vehicleList.map(function(row) { var isSubmitted = row.deliveryStatus === 'submitted'; + var isCompleted = row.deliveryStatus === 'completed'; + var completedSelectable = isRenewalProject && !!noReturnCarChecked; return React.createElement('tr', { key: row.key }, React.createElement('td', { style: styles.td }, isSubmitted - ? React.createElement(Tooltip, { title: '该车辆已有交车任务' }, + ? React.createElement(Tooltip, { title: '该车辆已创建交车任务' }, React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } }, React.createElement(Checkbox, { disabled: true, checked: false }), React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交'))) - : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), + : isCompleted + ? React.createElement(Tooltip, { title: '该车辆已完成交车' }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement(Checkbox, { + disabled: !completedSelectable, + checked: completedSelectable ? (checkedRowKeys.indexOf(row.key) !== -1) : true, + onChange: function(e) { onSelectRow(row, e.target.checked); } + }) + )) + : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })), @@ -276,6 +307,8 @@ const Component = function() { ); } + var reqSpecText = '新增交车任务\n一个「数字化资产ONEOS运管平台」中的「新增交车任务」模块\n1.面包屑:\n1.1.业务管理-交车任务-新增交车任务\n\n2.表单:\n2.1.选择项目名称:必选项,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;;\n2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码;\n2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称;\n2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务;\n2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点;\n2.6.预计交车日期:必填项,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期;\n 2.6.1.如勾选不需要还车,则预计交车日期隐藏,不需要设置;\n2.7.开始计费日期:必填项,日期选择器,支持单日选择,格式为YYYY-MM-DD;\n2.8.已交车辆是否需要还车:勾选框,内容为不需要还车,默认不勾选,不勾选时,无法在车辆列表勾选已交未还的车辆,勾选后,已交未还的车辆也可以勾选,但不会生成新交车任务,只是按照开始计费日期生成账单;\n 2.8.1.勾选时,车辆列表只显示已交未还的车辆清单,不能添加新车(避免已交未还部分车辆开始计费日期计算不精确);\n 2.8.2.不勾选时,车辆列表显示已交未还的车辆清单及续签合同新增车辆,但是已交未还部分的车辆不可勾选;\n2.9.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注;\n 2.9.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务和已交车的车辆不可选,多选框为禁用状态,悬浮时提示:该车辆已完成交车/该车辆已创建交车任务;\n 2.9.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌;\n 2.9.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号;\n 2.9.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-;\n 2.9.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码;\n 2.9.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元;\n 2.9.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元;\n 2.9.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元;\n 2.9.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-;\n2.10.页面底部为提交、取消;'; + return React.createElement('div', { style: styles.page }, React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, @@ -284,7 +317,7 @@ const Component = function() { React.createElement('span', null, '交车任务'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '新增交车任务')), - React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { reqModalOpen[1](true); } }, '查看需求说明')), React.createElement('div', { style: styles.card }, React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), React.createElement('div', { style: styles.cardBody }, @@ -292,9 +325,17 @@ const Component = function() { formRow2, tableEl)), React.createElement('div', { style: { height: 60 } }), - reqSpecModalContent, React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), - React.createElement(Button, { onClick: handleCancel }, '取消')) + React.createElement(Button, { onClick: handleCancel }, '取消')), + React.createElement(Modal, { + title: '需求说明', + open: reqModalOpen[0], + onCancel: function() { reqModalOpen[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function() { reqModalOpen[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, reqSpecText))) ); }; diff --git a/web端/业务管理/租赁账单-收费明细.jsx b/web端/业务管理/租赁账单-收费明细.jsx new file mode 100644 index 0000000..22b0099 --- /dev/null +++ b/web端/业务管理/租赁账单-收费明细.jsx @@ -0,0 +1,449 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 租赁账单-收费明细(2026年3月11日版本) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Button = antd.Button; + var Popover = antd.Popover; + var Modal = antd.Modal; + var message = antd.message; + + var servicePopoverRowIndex = useState(null); + var submitConfirmVisible = useState(false); + var cancelConfirmVisible = useState(false); + var requirementModalVisible = useState(false); + + // 模拟:当前账单信息(由路由/接口传入,此处为第1期账单) + var billInfo = useMemo(function () { + return { + contractCode: 'HT-ZL-2025-001', + contractType: '正式合同', + projectName: '嘉兴氢能示范项目', + customerName: '嘉兴某某物流有限公司', + deliveryTaskCode: 'JT-2025-001-A', + billCode: 'HT-ZL-2025-0010001', + period: 1, + billStartDate: '2025-01-01', + billEndDate: '2025-01-31' + }; + }, []); + + var isFirstPeriod = billInfo.period === 1; + + // 车辆明细(该账单对应车辆及费用) + var vehicleRows = useState([ + { key: 'v1', index: 1, brand: '东风', model: 'DFH1180', plateNo: '浙A12345', receivableRent: 30000, actualRent: '29800.00', rentRemark: '首期六期一次性付清', discountAmount: '200.00', discountRemark: '首月优惠', discountProof: [{ name: '优惠审批单.pdf' }], receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 200, actual: '200.00', discount: '0.00', remark: '' }, { name: '保险上浮', receivable: 500, actual: '480.00', discount: '20.00', remark: '客户协商' }], receivableService: 700, actualService: '680.00' }, + { key: 'v2', index: 2, brand: '福田', model: 'BJ1180', plateNo: '浙A23456', receivableRent: 27000, actualRent: '27000.00', rentRemark: '', discountAmount: '0.00', discountRemark: '', discountProof: [], receivableDeposit: 8000, serviceItems: [{ name: '保养费用', receivable: 300, actual: '300.00', discount: '0.00', remark: '含首保' }], receivableService: 300, actualService: '300.00' }, + { key: 'v3', index: 3, brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567', receivableRent: 31200, actualRent: '31200.00', rentRemark: '', discountAmount: '0.00', discountRemark: '', discountProof: [], receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 180, actual: '180.00', discount: '0.00', remark: '' }, { name: '上牌服务', receivable: 400, actual: '400.00', discount: '0.00', remark: '' }], receivableService: 580, actualService: '580.00' } + ]); + var vehicles = vehicleRows[0]; + var setVehicles = vehicleRows[1]; + + var hydrogenRow = useState({ receivable: '3580.00', actual: '3500.00', discount: '80.00', discountRemark: '预付款批量减免' }); + var hydrogen = hydrogenRow[0]; + var setHydrogen = hydrogenRow[1]; + + var updateVehicle = useCallback(function (key, field, value) { + setVehicles(function (prev) { + return prev.map(function (r) { + if (r.key !== key) return r; + var next = Object.assign({}, r); + next[field] = value; + if (field === 'serviceItems') { + var total = 0; + (value || []).forEach(function (s) { total += parseFloat(s.actual) || 0; }); + next.actualService = total.toFixed(2); + } + return next; + }); + }); + }, []); + + var updateServiceItem = useCallback(function (vehicleKey, itemIndex, field, value) { + setVehicles(function (prev) { + return prev.map(function (r) { + if (r.key !== vehicleKey) return r; + var items = (r.serviceItems || []).slice(); + if (!items[itemIndex]) return r; + items[itemIndex] = Object.assign({}, items[itemIndex], { [field]: value }); + var total = 0; + items.forEach(function (s) { total += parseFloat(s.actual) || 0; }); + return Object.assign({}, r, { serviceItems: items, actualService: total.toFixed(2) }); + }); + }); + }, []); + + // 应收款总额 = 应收车辆月租金总和 + 应收车辆保证金总和 + 应收服务费总和 + 氢费预付款应收金额 + var totals = useMemo(function () { + var receivableRent = 0, actualRent = 0, receivableDeposit = 0, receivableService = 0, actualService = 0, discountTotal = 0, serviceDiscountTotal = 0; + vehicles.forEach(function (v) { + receivableRent += Number(v.receivableRent) || 0; + actualRent += parseFloat(v.actualRent) || 0; + receivableDeposit += Number(v.receivableDeposit) || 0; + receivableService += Number(v.receivableService) || 0; + actualService += parseFloat(v.actualService) || 0; + discountTotal += parseFloat(v.discountAmount) || 0; + (v.serviceItems || []).forEach(function (s) { serviceDiscountTotal += parseFloat(s.discount) || 0; }); + }); + var hydrogenReceivable = parseFloat(hydrogen.receivable) || 0; + var hydrogenActual = parseFloat(hydrogen.actual) || 0; + var hydrogenDiscount = parseFloat(hydrogen.discount) || 0; + return { + receivableRent: receivableRent.toFixed(2), + actualRent: actualRent.toFixed(2), + receivableDeposit: receivableDeposit.toFixed(2), + receivableService: receivableService.toFixed(2), + actualService: actualService.toFixed(2), + discountTotal: discountTotal.toFixed(2), + serviceDiscountTotal: serviceDiscountTotal.toFixed(2), + hydrogenReceivable: hydrogenReceivable.toFixed(2), + hydrogenActual: hydrogenActual.toFixed(2), + hydrogenDiscount: hydrogenDiscount.toFixed(2) + }; + }, [vehicles, hydrogen]); + + var receivableTotal = useMemo(function () { + return (parseFloat(totals.receivableRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.receivableService) + (isFirstPeriod ? parseFloat(totals.hydrogenReceivable) : 0)).toFixed(2); + }, [totals, isFirstPeriod]); + + var actualTotal = useMemo(function () { + var base = parseFloat(totals.actualRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal) - parseFloat(totals.serviceDiscountTotal); + if (isFirstPeriod) base += parseFloat(totals.hydrogenActual) - parseFloat(totals.hydrogenDiscount); + return base.toFixed(2); + }, [totals, isFirstPeriod]); + + var invoiceTotal = useMemo(function () { + // 开票金额:同“实收款总额”展示格式,但不包含应收车辆保证金 + var base = parseFloat(totals.actualRent) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal) - parseFloat(totals.serviceDiscountTotal); + if (isFirstPeriod) base += parseFloat(totals.hydrogenActual) - parseFloat(totals.hydrogenDiscount); + return base.toFixed(2); + }, [totals, isFirstPeriod]); + + var receivablePopoverContent = useMemo(function () { + var rows = [ + ['总计应收车辆月租金', totals.receivableRent + ' 元'], + ['总计应收车辆保证金', totals.receivableDeposit + ' 元'], + ['总计应收服务费', totals.receivableService + ' 元'] + ]; + if (isFirstPeriod) rows.push(['氢费预付款应收金额', totals.hydrogenReceivable + ' 元']); + return React.createElement('div', { style: { padding: 8, minWidth: 220 } }, + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') + ) + ), + React.createElement('tbody', null, + rows.map(function (r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: { padding: '6px 8px' } }, r[0]), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, r[1])); }) + ) + ) + ); + }, [totals, isFirstPeriod]); + + var actualPopoverContent = useMemo(function () { + var rows = [ + ['总计实收车辆月租金', totals.actualRent + ' 元'], + ['总计应收车辆保证金', totals.receivableDeposit + ' 元'], + ['总计实收服务费', totals.actualService + ' 元'], + ['总计减免金额', totals.discountTotal + ' 元'], + ['服务费各项减免费用总和', totals.serviceDiscountTotal + ' 元'] + ]; + if (isFirstPeriod) { + rows.push(['氢费预付款实收金额', totals.hydrogenActual + ' 元']); + rows.push(['氢费预付款减免金额', totals.hydrogenDiscount + ' 元']); + } + return React.createElement('div', { style: { padding: 8, minWidth: 240 } }, + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') + ) + ), + React.createElement('tbody', null, + rows.map(function (r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: { padding: '6px 8px' } }, r[0]), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, r[1])); }) + ) + ) + ); + }, [totals, isFirstPeriod]); + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var labelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 }; + var formItemStyle = { marginBottom: 12 }; + var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, lineHeight: '22px', minHeight: 22 }; + var thBase = { padding: '10px 12px', border: '1px solid #f0f0f0', whiteSpace: 'nowrap' }; + var highlightStyle = { color: '#1890ff', fontWeight: 600, cursor: 'pointer' }; + var requiredStar = { color: '#ff4d4f', marginRight: 2 }; + + var activeServiceIndex = servicePopoverRowIndex[0]; + var setServicePopoverRowIndex = servicePopoverRowIndex[1]; + + function handleSubmit() { + submitConfirmVisible[1](true); + } + function handleSubmitConfirm() { + submitConfirmVisible[1](false); + message.success('提交成功'); + if (window.__billChargeDetailBack) window.__billChargeDetailBack(); + } + function handleSave() { + message.success('保存成功'); + if (window.__billChargeDetailBack) window.__billChargeDetailBack(); + } + function handleCancel() { + cancelConfirmVisible[1](true); + } + function handleCancelConfirm() { + cancelConfirmVisible[1](false); + if (window.__billChargeDetailBack) window.__billChargeDetailBack(); + else message.info('返回租赁账单列表(原型)'); + } + + var requirementContent = '租赁账单-收费明细(2026年3月11日版本)\n一个「数字化资产ONEOS运管平台」中的「租赁账单」「收费明细」模块\n#面包屑:业务管理-租赁账单-收费明细;\n1.账单信息:\n#显示该账单对应合同及账单信息,显示以下字段:\n 1.1.合同编码:显示该账单对应合同编码;\n 1.2.合同类型:显示该账单对应合同类型,包括正式合同、试用合同;\n 1.3.项目名称:显示该账单对应合同中项目名称;\n 1.4.客户名称:显示该账单对应合同中客户名称;\n 1.5.交车任务编码:显示交车任务对应编码;\n 1.6.账单编码:显示该账单编码,账单编码规则为:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据;\n 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推;\n 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单;;\n 1.7.账单期数:显示该账单期数;\n 1.8.账单开始日期:显示账单开始日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD;\n 1.9.账单结束日期:显示账单结束日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD;\n2.账单明细:\n#顶部显示应收款总额、实收款总额;\n 2.1.应收款总额:「应收车辆月租金总和」+「应收车辆保证金总和」+「应收服务费总和」+「氢费预付款应收金额」\n 2.2.实收款总额:「实收车辆月租金总和」+「应收车辆保证金总和」+「实收服务费总和」+「氢费预付款实收金额」-「减免费用总和」「服务费各项减免费用总和」\n 2.3.开票金额:「实收车辆月租金总和」+「实收服务费总和」-「减免费用总和」「服务费各项减免费用总和」\n\n#显示该账单对应车辆列表及相关费用信息,显示以下字段;\n 2.3.序号:与租赁合同及交车任务中该车辆序号一致;\n 2.4.品牌:与租赁合同及交车任务中该车辆品牌一致;\n 2.5.型号:与租赁合同及交车任务中该车辆型号一致;\n 2.6.车牌号:与租赁合同及交车任务中该车辆车牌号一致;\n 2.7.应收车辆月租金:与租赁合同中该车辆车辆月租金一致,格式为:xx.xx元;\n 2.8.实收车辆月租金:输入框,2位小数,后缀为元,默认为与应收车辆月租金一致;\n 2.9.车辆租金备注:输入框,支持自定义输入;\n 2.10.减免金额:输入框,2位小数,后缀为元,默认为0.00元;\n 2.11.减免金额备注:输入框,支持自定义输入;\n 2.12.减免证明:附件上传按钮,点击支持多文件上传,已上传文件一行显示一条,支持点击预览,格式支持各类图片、doc、docx、pdf等格式;\n 2.13.应收车辆保证金:首期与合同中车辆保证金一致,第二期开始为0.00,格式为:xx.xx元;\n 2.14.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;\n 2.14.1.服务项目:显示租赁合同中所有服务项目名称;\n 2.14.2.应收费用:显示租赁合同中所有服务项目对应费用,格式为:xx.xx元;\n 2.14.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元,默认与应收费用一致;\n 2.14.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;\n 2.14.4.备注:选填项,输入框,由业务员自行输入备注信息;\n 2.15.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;\n 2.16.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;\n#列表下方为氢费预付款情况,首期账单显示,第二期账单开始无此区域内容显示;\n 2.17.氢费预付款应收金额:显示合同中氢费预付款金额;\n 2.18.氢费预付款实收金额:输入框,2位小数,后缀为元,默认与氢费预付款应收金额一致;\n 2.19.减免金额:输入框,2位小数,后缀为元;\n 2.20.减免金额备注:输入框,支持自定义输入;\n\n3.下方为提交审核、保存、取消按钮\n 3.1.提交审核:点击提交审核按钮,进入审核流程;\n 3.2.保存:点击保存按钮,保存当前表单已填数据,不做校验;\n'; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { marginBottom: 16, display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, + React.createElement(Breadcrumb, { + items: [ + { title: '业务管理' }, + { title: '租赁账单' }, + { title: '收费明细' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { requirementModalVisible[1](true); } }, '查看需求说明') + ), + React.createElement(Card, { title: '账单信息', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '合同编码'), + React.createElement('div', { style: valueStyle }, billInfo.contractCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '合同类型'), + React.createElement('div', { style: valueStyle }, billInfo.contractType || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '项目名称'), + React.createElement('div', { style: valueStyle }, billInfo.projectName || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户名称'), + React.createElement('div', { style: valueStyle }, billInfo.customerName || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '交车任务编码'), + React.createElement('div', { style: valueStyle }, billInfo.deliveryTaskCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单编码'), + React.createElement('div', { style: valueStyle }, billInfo.billCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单期数'), + React.createElement('div', { style: valueStyle }, billInfo.period != null ? billInfo.period : '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单开始日期'), + React.createElement('div', { style: valueStyle }, billInfo.billStartDate || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单结束日期'), + React.createElement('div', { style: valueStyle }, billInfo.billEndDate || '—') + ) + ) + ), + React.createElement(Card, { title: '账单明细', style: cardStyle }, + React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'center' } }, + React.createElement(Popover, { content: receivablePopoverContent, title: '应收款明细', trigger: 'click' }, + React.createElement('span', { style: { cursor: 'pointer' } }, '应收款总额:', React.createElement('span', { style: highlightStyle }, receivableTotal, ' 元')) + ), + React.createElement(Popover, { content: actualPopoverContent, title: '实收款明细', trigger: 'click' }, + React.createElement('span', { style: { cursor: 'pointer' } }, '实收款总额:', React.createElement('span', { style: highlightStyle }, actualTotal, ' 元')) + ) + , + React.createElement('span', { style: { cursor: 'default' } }, '开票金额:', React.createElement('span', { style: highlightStyle }, invoiceTotal, ' 元')) + ), + React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, + React.createElement('table', { style: { width: '100%', minWidth: 1600, borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' } }, + React.createElement('thead', null, + React.createElement('tr', { style: { backgroundColor: '#fafafa' } }, + React.createElement('th', { style: Object.assign({}, thBase, { width: 50 }) }, '序号'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 80 }) }, '品牌'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '型号'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '车牌号'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 120 }) }, '应收车辆月租金'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 130 }) }, '实收车辆月租金'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '车辆租金备注'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '减免金额'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '减免金额备注'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 130 }) }, '减免证明'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 110 }) }, '应收车辆保证金'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '服务费项目'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '应收服务费'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '实收服务费') + ) + ), + React.createElement('tbody', null, + vehicles.map(function (row) { + var servicePopover = React.createElement('div', { style: { padding: 8, minWidth: 360 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 8 } }, '服务费项目'), + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '服务项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '应收费用'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, React.createElement('span', null, React.createElement('span', { style: requiredStar }, '*'), '实收费用')), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '减免费用'), + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '备注') + ) + ), + React.createElement('tbody', null, + (row.serviceItems || []).map(function (s, si) { + return React.createElement('tr', { key: si }, + React.createElement('td', { style: { padding: '6px 8px' } }, s.name), + React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.receivable != null ? s.receivable : '') + ' 元'), + React.createElement('td', { style: { padding: '6px 8px' } }, + React.createElement(Input, { size: 'small', value: s.actual, suffix: '元', onChange: function (e) { updateServiceItem(row.key, si, 'actual', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '6px 8px' } }, + React.createElement(Input, { size: 'small', value: s.discount, suffix: '元', onChange: function (e) { updateServiceItem(row.key, si, 'discount', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '6px 8px' } }, + React.createElement(Input, { size: 'small', value: s.remark, placeholder: '备注', onChange: function (e) { updateServiceItem(row.key, si, 'remark', e.target.value); } }) + ) + ); + }) + ) + ) + ); + var proofList = row.discountProof || []; + var onProofUpload = function (e) { + var files = e.target.files; + if (!files || !files.length) return; + var next = proofList.slice(); + for (var i = 0; i < files.length; i++) { next.push({ name: files[i].name }); } + updateVehicle(row.key, 'discountProof', next); + e.target.value = ''; + }; + var removeProof = function (idx) { + var next = (row.discountProof || []).slice(); + next.splice(idx, 1); + updateVehicle(row.key, 'discountProof', next); + }; + var depositDisplay = isFirstPeriod ? (row.receivableDeposit || 0) : '0.00'; + return React.createElement('tr', { key: row.key }, + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.index), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.brand), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.model), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.plateNo || '—'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.receivableRent != null ? row.receivableRent : 0) + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Input, { size: 'small', value: row.actualRent, suffix: '元', onChange: function (e) { updateVehicle(row.key, 'actualRent', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Input, { size: 'small', value: row.rentRemark, onChange: function (e) { updateVehicle(row.key, 'rentRemark', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Input, { size: 'small', value: row.discountAmount, suffix: '元', onChange: function (e) { updateVehicle(row.key, 'discountAmount', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Input, { size: 'small', value: row.discountRemark, onChange: function (e) { updateVehicle(row.key, 'discountRemark', e.target.value); } }) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12 } }, + proofList.map(function (p, pidx) { + return React.createElement('div', { key: pidx, style: { display: 'flex', alignItems: 'center', gap: 4 } }, + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0, height: 'auto' }, onClick: function () { message.info('预览:' + (p.name || '附件')); } }, p.name || '附件'), + React.createElement(Button, { type: 'link', size: 'small', danger: true, style: { padding: 0, minWidth: 'auto' }, onClick: function () { removeProof(pidx); } }, '删除') + ); + }), + React.createElement('span', null, + React.createElement('input', { type: 'file', multiple: true, accept: '.jpg,.jpeg,.png,.pdf,.doc,.docx', style: { display: 'none' }, id: 'proof-' + row.key, onChange: onProofUpload }), + React.createElement(Button, { type: 'default', size: 'small', onClick: function () { var el = document.getElementById('proof-' + row.key); if (el) el.click(); } }, '附件上传') + ) + ) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (depositDisplay !== undefined && depositDisplay !== null ? depositDisplay : '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Popover, { + content: servicePopover, + title: null, + trigger: 'click', + open: activeServiceIndex === row.key, + onOpenChange: function (open) { setServicePopoverRowIndex(open ? row.key : null); } + }, + React.createElement(Button, { type: 'link', size: 'small' }, '管理') + ) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.receivableService != null ? row.receivableService : '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.actualService || '0.00') + ' 元') + ); + }) + ) + ) + ), + isFirstPeriod ? React.createElement('div', { style: { marginTop: 16 } }, + React.createElement('div', { style: { fontSize: 14, fontWeight: 500, marginBottom: 8 } }, '氢费预付款情况'), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, maxWidth: 800 } }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '氢费预付款应收金额'), + React.createElement(Input, { value: hydrogen.receivable + ' 元', disabled: true }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '氢费预付款实收金额'), + React.createElement(Input, { value: hydrogen.actual, suffix: '元', onChange: function (e) { setHydrogen(function (p) { return Object.assign({}, p, { actual: e.target.value }); }); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '减免金额'), + React.createElement(Input, { value: hydrogen.discount, suffix: '元', onChange: function (e) { setHydrogen(function (p) { return Object.assign({}, p, { discount: e.target.value }); }); } }) + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '减免金额备注'), + React.createElement(Input, { value: hydrogen.discountRemark, onChange: function (e) { setHydrogen(function (p) { return Object.assign({}, p, { discountRemark: e.target.value }); }); } }) + ) + ) + ) : null + ), + React.createElement('div', { style: { display: 'flex', gap: 8, marginTop: 24 } }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleSave }, '保存'), + React.createElement(Button, { onClick: handleCancel }, '取消') + ), + React.createElement(Modal, { + title: '确认', + open: submitConfirmVisible[0], + onCancel: function () { submitConfirmVisible[1](false); }, + onOk: handleSubmitConfirm, + okText: '确认', + cancelText: '取消' + }, '请确认账单金额无误'), + React.createElement(Modal, { + title: '确认取消', + open: cancelConfirmVisible[0], + onCancel: function () { cancelConfirmVisible[1](false); }, + onOk: handleCancelConfirm, + okText: '确认', + cancelText: '取消' + }, '取消将会丢失未保存的修改,是否确认?'), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalVisible[0], + onCancel: function () { requirementModalVisible[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function () { requirementModalVisible[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent) + )) + ); +}; diff --git a/web端/业务管理/租赁账单-查看.jsx b/web端/业务管理/租赁账单-查看.jsx new file mode 100644 index 0000000..8a6a1e7 --- /dev/null +++ b/web端/业务管理/租赁账单-查看.jsx @@ -0,0 +1,345 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 租赁账单-查看(2026年3月11日版本) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Button = antd.Button; + var Popover = antd.Popover; + var Table = antd.Table; + var Modal = antd.Modal; + var message = antd.message; + + var requirementModalVisible = useState(false); + var servicePopoverRowIndex = useState(null); + + // 模拟:当前账单信息(由路由/接口传入) + var billInfo = useMemo(function () { + return { + contractCode: 'HT-ZL-2025-001', + contractType: '正式合同', + projectName: '嘉兴氢能示范项目', + customerName: '嘉兴某某物流有限公司', + deliveryTaskCode: 'JT-2025-001-A', + billCode: 'HT-ZL-2025-0010001', + period: 1, + billStartDate: '2025-01-01', + billEndDate: '2025-01-31' + }; + }, []); + + var isFirstPeriod = billInfo.period === 1; + + // 车辆明细(只读展示) + var vehicles = useMemo(function () { + return [ + { key: 'v1', index: 1, brand: '东风', model: 'DFH1180', plateNo: '浙A12345', receivableRent: 30000, actualRent: '29800.00', rentRemark: '首期六期一次性付清', discountAmount: '200.00', discountRemark: '首月优惠', discountProof: [{ name: '优惠审批单.pdf' }], receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 200, actual: '200.00', discount: '0.00', remark: '' }, { name: '保险上浮', receivable: 500, actual: '480.00', discount: '20.00', remark: '客户协商' }], receivableService: 700, actualService: '680.00' }, + { key: 'v2', index: 2, brand: '福田', model: 'BJ1180', plateNo: '浙A23456', receivableRent: 27000, actualRent: '27000.00', rentRemark: '', discountAmount: '0.00', discountRemark: '', discountProof: [], receivableDeposit: 8000, serviceItems: [{ name: '保养费用', receivable: 300, actual: '300.00', discount: '0.00', remark: '含首保' }], receivableService: 300, actualService: '300.00' }, + { key: 'v3', index: 3, brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567', receivableRent: 31200, actualRent: '31200.00', rentRemark: '', discountAmount: '0.00', discountRemark: '', discountProof: [], receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 180, actual: '180.00', discount: '0.00', remark: '' }, { name: '上牌服务', receivable: 400, actual: '400.00', discount: '0.00', remark: '' }], receivableService: 580, actualService: '580.00' } + ]; + }, []); + + var hydrogen = useMemo(function () { + return { receivable: '3580.00', actual: '3500.00', discount: '80.00', discountRemark: '预付款批量减免' }; + }, []); + + var totals = useMemo(function () { + var receivableRent = 0, actualRent = 0, receivableDeposit = 0, receivableService = 0, actualService = 0, discountTotal = 0, serviceDiscountTotal = 0; + vehicles.forEach(function (v) { + receivableRent += Number(v.receivableRent) || 0; + actualRent += parseFloat(v.actualRent) || 0; + receivableDeposit += Number(v.receivableDeposit) || 0; + receivableService += Number(v.receivableService) || 0; + actualService += parseFloat(v.actualService) || 0; + discountTotal += parseFloat(v.discountAmount) || 0; + (v.serviceItems || []).forEach(function (s) { serviceDiscountTotal += parseFloat(s.discount) || 0; }); + }); + var hydrogenReceivable = parseFloat(hydrogen.receivable) || 0; + var hydrogenActual = parseFloat(hydrogen.actual) || 0; + var hydrogenDiscount = parseFloat(hydrogen.discount) || 0; + return { + receivableRent: receivableRent.toFixed(2), + actualRent: actualRent.toFixed(2), + receivableDeposit: receivableDeposit.toFixed(2), + receivableService: receivableService.toFixed(2), + actualService: actualService.toFixed(2), + discountTotal: discountTotal.toFixed(2), + serviceDiscountTotal: serviceDiscountTotal.toFixed(2), + hydrogenReceivable: hydrogenReceivable.toFixed(2), + hydrogenActual: hydrogenActual.toFixed(2), + hydrogenDiscount: hydrogenDiscount.toFixed(2) + }; + }, [vehicles, hydrogen]); + + var receivableTotal = useMemo(function () { + return (parseFloat(totals.receivableRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.receivableService) + (isFirstPeriod ? parseFloat(totals.hydrogenReceivable) : 0)).toFixed(2); + }, [totals, isFirstPeriod]); + + var actualTotal = useMemo(function () { + var base = parseFloat(totals.actualRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal) - parseFloat(totals.serviceDiscountTotal); + if (isFirstPeriod) base += parseFloat(totals.hydrogenActual) - parseFloat(totals.hydrogenDiscount); + return base.toFixed(2); + }, [totals, isFirstPeriod]); + + var invoiceTotal = useMemo(function () { + // 开票总额:同“实收款总额”展示格式,但不包含应收车辆保证金 + var base = parseFloat(totals.actualRent) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal) - parseFloat(totals.serviceDiscountTotal); + if (isFirstPeriod) base += parseFloat(totals.hydrogenActual) - parseFloat(totals.hydrogenDiscount); + return base.toFixed(2); + }, [totals, isFirstPeriod]); + + var receivablePopoverContent = useMemo(function () { + var rows = [ + ['总计应收车辆月租金', totals.receivableRent + ' 元'], + ['总计应收车辆保证金', totals.receivableDeposit + ' 元'], + ['总计应收服务费', totals.receivableService + ' 元'] + ]; + if (isFirstPeriod) rows.push(['氢费预付款应收金额', totals.hydrogenReceivable + ' 元']); + return React.createElement('div', { style: { padding: 8, minWidth: 220 } }, + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') + ) + ), + React.createElement('tbody', null, + rows.map(function (r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: { padding: '6px 8px' } }, r[0]), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, r[1])); }) + ) + ) + ); + }, [totals, isFirstPeriod]); + + var actualPopoverContent = useMemo(function () { + var rows = [ + ['总计实收车辆月租金', totals.actualRent + ' 元'], + ['总计应收车辆保证金', totals.receivableDeposit + ' 元'], + ['总计实收服务费', totals.actualService + ' 元'], + ['总计减免金额', totals.discountTotal + ' 元'], + ['服务费各项减免费用总和', totals.serviceDiscountTotal + ' 元'] + ]; + if (isFirstPeriod) { + rows.push(['氢费预付款实收金额', totals.hydrogenActual + ' 元']); + rows.push(['氢费预付款减免金额', totals.hydrogenDiscount + ' 元']); + } + return React.createElement('div', { style: { padding: 8, minWidth: 240 } }, + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') + ) + ), + React.createElement('tbody', null, + rows.map(function (r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: { padding: '6px 8px' } }, r[0]), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, r[1])); }) + ) + ) + ); + }, [totals, isFirstPeriod]); + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var labelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 }; + var formItemStyle = { marginBottom: 12 }; + var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, lineHeight: '22px', minHeight: 22 }; + var thBase = { padding: '10px 12px', border: '1px solid #f0f0f0', whiteSpace: 'nowrap' }; + var highlightStyle = { color: '#1890ff', fontWeight: 600, cursor: 'pointer' }; + + var activeServiceIndex = servicePopoverRowIndex[0]; + var setServicePopoverRowIndex = servicePopoverRowIndex[1]; + + function handleBack() { + if (window.__billViewBack) window.__billViewBack(); + else message.info('返回租赁账单列表(原型)'); + } + + var requirementContent = '租赁账单-查看(2026年3月11日版本)\n一个「数字化资产ONEOS运管平台」中的「租赁账单」「查看」模块\n#面包屑:业务管理-租赁账单-收费明细;\n1.账单信息:\n#显示该账单对应合同及账单信息,显示以下字段:\n 1.1.合同编码:显示该账单对应合同编码;\n 1.2.合同类型:显示该账单对应合同类型,包括正式合同、试用合同;\n 1.3.项目名称:显示该账单对应合同中项目名称;\n 1.4.客户名称:显示该账单对应合同中客户名称;\n 1.5.交车任务编码:显示交车任务对应编码;\n 1.6.账单编码:显示该账单编码,账单编码规则为:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据;\n 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推;\n 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单;\n 1.7.账单期数:显示该账单期数;\n 1.8.账单开始日期:显示账单开始日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD;\n 1.9.账单结束日期:显示账单结束日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD;\n2.账单明细:\n#顶部显示应收款总额、实收款总额;\n 2.1.应收款总额:「应收车辆月租金总和」+「应收车辆保证金总和」+「应收服务费总和」+「氢费预付款应收金额」\n 2.2.实收款总额:「实收车辆月租金总和」+「应收车辆保证金总和」+「实收服务费总和」+「氢费预付款实收金额」-「减免费用总和」「服务费各项减免费用总和」\n\n#显示该账单对应车辆列表及相关费用信息,显示以下字段;\n 2.3.序号:与租赁合同及交车任务中该车辆序号一致;\n 2.4.品牌:与租赁合同及交车任务中该车辆品牌一致;\n 2.5.型号:与租赁合同及交车任务中该车辆型号一致;\n 2.6.车牌号:与租赁合同及交车任务中该车辆车牌号一致;\n 2.7.应收车辆月租金:与租赁合同中该车辆车辆月租金一致,格式为:xx.xx元;\n 2.8.实收车辆月租金:与收费明细中实收车辆月租金一致,格式为:xx.xx元;\n 2.9.车辆租金备注:显示收费明细中已填写备注信息;\n 2.10.减免金额:显示收费明细中已填写减免金额;\n 2.11.减免金额备注:显示收费明细中减免金额备注信息;\n 2.12.减免证明:显示已上传减免证明文件,已上传文件一行显示一条,支持点击预览,格式支持各类图片、doc、docx、pdf等格式;\n 2.13.应收车辆保证金:首期与合同中车辆保证金一致,第二期开始为0.00,格式为:xx.xx元;\n 2.14.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;\n 2.14.1.服务项目:显示租赁合同中所有服务项目名称;\n 2.14.2.应收费用:显示租赁合同中所有服务项目对应费用,格式为:xx.xx元;\n 2.14.3.实收费用:显示收费明细中填写的实收费用金额,精确至2位小数,后缀为元,默认与应收费用一致;\n 2.14.4.减免费用:显示收费明细中填写的减免费用金额,精确至2位小数,后缀为元;\n 2.14.4.备注:显示收费明细中填写的备注信息;\n 2.15.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;\n 2.16.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;\n#列表下方为氢费预付款情况,首期账单显示,第二期账单开始无此区域内容显示;\n 2.17.氢费预付款应收金额:显示合同中氢费预付款金额;\n 2.18.氢费预付款实收金额:显示收费明细中填写的氢费预付款实收金额,格式为:xx.xx元;\n 2.19.减免金额:显示收费明细中填写的减免金额,格式为:xx.xx元;\n 2.20.减免金额备注:显示收费明细中填写的减免金额备注信息;\n\n3.下方为返回按钮\n 3.1.返回:点击返回按钮,返回租赁账单列表页面;\n'; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '业务管理' }, + { title: '租赁账单' }, + { title: '查看' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { requirementModalVisible[1](true); } }, '查看需求说明') + ), + React.createElement(Card, { title: '账单信息', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '合同编码'), + React.createElement('div', { style: valueStyle }, billInfo.contractCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '合同类型'), + React.createElement('div', { style: valueStyle }, billInfo.contractType || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '项目名称'), + React.createElement('div', { style: valueStyle }, billInfo.projectName || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '客户名称'), + React.createElement('div', { style: valueStyle }, billInfo.customerName || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '交车任务编码'), + React.createElement('div', { style: valueStyle }, billInfo.deliveryTaskCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单编码'), + React.createElement('div', { style: valueStyle }, billInfo.billCode || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单期数'), + React.createElement('div', { style: valueStyle }, billInfo.period != null ? billInfo.period : '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单开始日期'), + React.createElement('div', { style: valueStyle }, billInfo.billStartDate || '—') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '账单结束日期'), + React.createElement('div', { style: valueStyle }, billInfo.billEndDate || '—') + ) + ) + ), + React.createElement(Card, { title: '账单明细', style: cardStyle }, + React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'center' } }, + React.createElement(Popover, { content: receivablePopoverContent, title: '应收款明细', trigger: 'click' }, + React.createElement('span', { style: { cursor: 'pointer' } }, '应收款总额:', React.createElement('span', { style: highlightStyle }, receivableTotal, ' 元')) + ), + React.createElement(Popover, { content: actualPopoverContent, title: '实收款明细', trigger: 'click' }, + React.createElement('span', { style: { cursor: 'pointer' } }, '实收款总额:', React.createElement('span', { style: highlightStyle }, actualTotal, ' 元')) + ), + React.createElement('span', { style: { cursor: 'default' } }, '开票总额:', React.createElement('span', { style: highlightStyle }, invoiceTotal, ' 元')) + ), + React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, + React.createElement('table', { style: { width: '100%', minWidth: 1500, borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' } }, + React.createElement('thead', null, + React.createElement('tr', { style: { backgroundColor: '#fafafa' } }, + React.createElement('th', { style: Object.assign({}, thBase, { width: 50 }) }, '序号'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 80 }) }, '品牌'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '型号'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '车牌号'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 110 }) }, '应收车辆月租金'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 110 }) }, '实收车辆月租金'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 110 }) }, '车辆租金备注'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '减免金额'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '减免金额备注'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '减免证明'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 110 }) }, '应收车辆保证金'), + React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '服务费项目'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '应收服务费'), + React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '实收服务费') + ) + ), + React.createElement('tbody', null, + vehicles.map(function (row) { + var servicePopover = React.createElement('div', { style: { padding: 8, minWidth: 340 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 8 } }, '服务费项目'), + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '服务项目'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '应收费用'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '实收费用'), + React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '减免费用'), + React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '备注') + ) + ), + React.createElement('tbody', null, + (row.serviceItems || []).map(function (s, si) { + return React.createElement('tr', { key: si }, + React.createElement('td', { style: { padding: '6px 8px' } }, s.name), + React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.receivable != null ? s.receivable : '') + ' 元'), + React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.actual != null ? s.actual : '') + ' 元'), + React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.discount != null ? s.discount : '0.00') + ' 元'), + React.createElement('td', { style: { padding: '6px 8px' } }, s.remark || '—') + ); + }) + ) + ) + ); + var proofList = row.discountProof || []; + var depositDisplay = isFirstPeriod ? (row.receivableDeposit || 0) : '0.00'; + return React.createElement('tr', { key: row.key }, + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.index), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.brand), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.model), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.plateNo || '—'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.receivableRent != null ? row.receivableRent : 0) + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.actualRent || '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.rentRemark || '—'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.discountAmount || '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, row.discountRemark || '—'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + proofList.length === 0 ? '—' : React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 2, fontSize: 12 } }, + proofList.map(function (p, pidx) { + return React.createElement(Button, { key: pidx, type: 'link', size: 'small', style: { padding: 0, height: 'auto', textAlign: 'left' }, onClick: function () { message.info('预览:' + (p.name || '附件')); } }, p.name || '附件'); + }) + ) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (depositDisplay !== undefined && depositDisplay !== null ? depositDisplay : '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0' } }, + React.createElement(Popover, { + content: servicePopover, + title: null, + trigger: 'click', + open: activeServiceIndex === row.key, + onOpenChange: function (open) { setServicePopoverRowIndex(open ? row.key : null); } + }, + React.createElement(Button, { type: 'link', size: 'small' }, '管理') + ) + ), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.receivableService != null ? row.receivableService : '0.00') + ' 元'), + React.createElement('td', { style: { padding: '8px 12px', border: '1px solid #f0f0f0', textAlign: 'right' } }, (row.actualService || '0.00') + ' 元') + ); + }) + ) + ) + ), + isFirstPeriod ? React.createElement('div', { style: { marginTop: 16 } }, + React.createElement('div', { style: { fontSize: 14, fontWeight: 500, marginBottom: 8 } }, '氢费预付款情况'), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, maxWidth: 800 } }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '氢费预付款应收金额'), + React.createElement('div', { style: valueStyle }, hydrogen.receivable + ' 元') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '氢费预付款实收金额'), + React.createElement('div', { style: valueStyle }, hydrogen.actual + ' 元') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '减免金额'), + React.createElement('div', { style: valueStyle }, hydrogen.discount + ' 元') + ), + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '减免金额备注'), + React.createElement('div', { style: valueStyle }, hydrogen.discountRemark || '—') + ) + ) + ) : null + ), + React.createElement('div', { style: { marginTop: 24 } }, + React.createElement(Button, { onClick: handleBack }, '返回') + ), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalVisible[0], + onCancel: function () { requirementModalVisible[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function () { requirementModalVisible[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent)) + ) + ); +}; diff --git a/web端/业务管理/租赁账单.jsx b/web端/业务管理/租赁账单.jsx index 5192a9c..98a5500 100644 --- a/web端/业务管理/租赁账单.jsx +++ b/web端/业务管理/租赁账单.jsx @@ -14,8 +14,10 @@ const Component = function () { var Input = antd.Input; var Space = antd.Space; var Popover = antd.Popover; + var Modal = antd.Modal; var message = antd.message; + var requirementModalVisible = useState(false); var filterContractCode = useState(undefined); var filterProjectName = useState(undefined); var filterCustomerName = useState(undefined); @@ -25,6 +27,15 @@ const Component = function () { var filterExpanded = useState(false); var expandedRowKeysState = useState([]); var deliveryPopoverOpen = useState(null); + var costEditsState = useState({}); + var costEdits = costEditsState[0]; + var setCostEdits = costEditsState[1]; + var editingCostState = useState(null); // { key, field } + var editingCost = editingCostState[0]; + var setEditingCost = editingCostState[1]; + var editingCostValueState = useState(''); + var editingCostValue = editingCostValueState[0]; + var setEditingCostValue = editingCostValueState[1]; var expandedRowKeys = expandedRowKeysState[0]; var setExpandedRowKeys = expandedRowKeysState[1]; @@ -40,9 +51,9 @@ const Component = function () { 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 } + { period: 1, status: '已提交', 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, hydrogenCost: 1200.00, otherCost: 300.00 }, + { period: 2, status: '待提交', 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, hydrogenCost: 900.00, otherCost: 180.00 }, + { period: 3, status: '待提交', 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, hydrogenCost: 650.00, otherCost: 220.00 } ] }, { @@ -55,8 +66,8 @@ const Component = function () { 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 } + { period: 1, status: '已提交', 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, hydrogenCost: 780.00, otherCost: 160.00 }, + { period: 2, status: '待提交', 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, hydrogenCost: 0.00, otherCost: 0.00 } ] }, { @@ -69,7 +80,7 @@ const Component = function () { 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 } + { period: 1, status: '已提交', 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, hydrogenCost: 500.00, otherCost: 120.00 } ] } ]; @@ -125,6 +136,92 @@ const Component = function () { return (isNaN(n) ? '0.00' : n.toFixed(2)) + '元'; } + function pad4(n) { + var s = String(n == null ? '' : n); + return ('0000' + s).slice(-4); + } + + function calcDays(startStr, endStr) { + if (!startStr || !endStr) return 0; + var start = new Date(startStr + 'T00:00:00'); + var end = new Date(endStr + 'T00:00:00'); + var ms = end.getTime() - start.getTime(); + if (!isFinite(ms)) return 0; + var days = Math.floor(ms / 86400000) + 1; + return days < 0 ? 0 : days; + } + + var modelCostPerDayMap = useMemo(function () { + return { + 'DFH1180': 120, + 'BJ1180': 95, + 'ZZ1187': 130, + 'SX1313': 110, + 'J6P': 125, + '格尔发K5': 90, + 'DFH1250': 140, + 'JH6': 118, + '欧曼EST': 150 + }; + }, []); + + function calcVehicleCost(record) { + var days = calcDays(record.billStartDate, record.billEndDate); + var vehicles = record.deliveryVehicles || []; + var sum = 0; + for (var i = 0; i < vehicles.length; i++) { + var m = vehicles[i] && vehicles[i].model; + var costPerDay = modelCostPerDayMap[m] || 0; + sum += costPerDay * days; + } + return sum; + } + + function getCostKey(record) { + var p = record._parentRecord; + var code = (p && p.contractCode) || ''; + var period = record.period != null ? record.period : ''; + return code + '-ZD' + pad4(period); + } + + function renderEditableCost(field, record) { + var key = getCostKey(record); + var map = costEdits[key] || {}; + var v = map[field]; + if (v === undefined) v = record[field]; + var isEditing = editingCost && editingCost.key === key && editingCost.field === field; + if (isEditing) { + return React.createElement(Input, { + autoFocus: true, + value: editingCostValue, + onChange: function (e) { setEditingCostValue(e.target.value); }, + onBlur: function () { + var raw = (editingCostValue || '').trim(); + var num = raw === '' ? 0 : parseFloat(raw); + var next = isNaN(num) ? 0 : Math.round(num * 100) / 100; + setCostEdits(function (prev) { + var p = Object.assign({}, prev || {}); + var row = Object.assign({}, p[key] || {}); + row[field] = next; + p[key] = row; + return p; + }); + setEditingCost(null); + setEditingCostValue(''); + }, + onPressEnter: function () { if (document && document.activeElement) document.activeElement.blur(); }, + suffix: '元' + }); + } + return React.createElement('span', { + style: { cursor: 'pointer' }, + onClick: function () { + setEditingCost({ key: key, field: field }); + setEditingCostValue((v == null || v === '') ? '' : String(Number(v).toFixed(2))); + } + }, fmtMoney(v)); + } + function goView(record, parent) { message.info('查看账单详情(原型)'); } @@ -132,6 +229,15 @@ const Component = function () { message.info('收费明细(原型)'); } + function renderSubActions(record, parentRecord) { + var status = record && record.status; + var isSubmitted = status === '已提交'; + return React.createElement(Space, { size: 'small' }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record, parentRecord); } }, '查看'), + isSubmitted ? null : React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goChargeDetail(record, parentRecord); } }, '收费明细') + ); + } + // 子表提车数量气泡:列表显示 品牌、型号、车牌号 function renderDeliveryPopover(record) { var vehicles = record.deliveryVehicles || []; @@ -189,17 +295,18 @@ const Component = function () { ]; 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: 'billNo', key: 'billNo', width: 220, ellipsis: true, render: function (v, record) { var p = record._parentRecord; var code = (p && p.contractCode) || ''; var period = record.period != null ? record.period : ''; return code ? (code + 'ZD' + pad4(period)) : '—'; } }, { title: '账单期数', dataIndex: 'period', key: 'period', width: 90, align: 'center', render: function (v) { return v != null ? v : '—'; } }, + { title: '状态', dataIndex: 'status', key: 'status', width: 90, render: function (v) { return 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: 'vehicleCost', width: 110, align: 'right', render: function (_, record) { return fmtMoney(calcVehicleCost(record)); } }, + { title: '氢费成本', dataIndex: 'hydrogenCost', key: 'hydrogenCost', width: 110, align: 'right', render: function (_, record) { return renderEditableCost('hydrogenCost', record); } }, + { title: '其他成本', dataIndex: 'otherCost', key: 'otherCost', width: 110, align: 'right', render: function (_, record) { return renderEditableCost('otherCost', record); } }, { title: '操作', key: 'action', @@ -207,10 +314,7 @@ const Component = function () { 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); } }, '收费明细') - ); + return renderSubActions(record, parentRecord); } } ]; @@ -225,7 +329,7 @@ const Component = function () { draggable: false, onDragStart: function (e) { e.preventDefault(); } }, - React.createElement(Table, { + 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; @@ -235,10 +339,7 @@ const Component = function () { 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); } }, '收费明细') - ); + return renderSubActions(row, record); } }; }), @@ -246,19 +347,22 @@ const Component = function () { pagination: false, size: 'small', bordered: true, - scroll: { x: 1300 } + scroll: { x: 1450 } }) ); }; + var requirementContent = '租赁账单(2026年3月10日版本)\n一个「数字化资产ONEOS运管平台」中的「租赁账单」模块\n#面包屑:业务管理-租赁账单;\n1.筛选:\n#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式;\n1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.4.业务部门:选择器,支持选择所有业务部门;\n1.5.业务负责人:选择器,支持选择所有业务负责人;\n1.6.交车任务编码:输入框,支持模糊搜索;\n1.7.右侧为重置、查询按钮;\n\n2.租赁账单列表:\n列表展示方式为:嵌套子表格,分为主表和子表\n2.1.主表:显示以下字段:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人;\n 2.1.1.合同编码:显示车辆租赁合同编码;\n 2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同;\n 2.1.3.项目名称:显示该租赁合同对应项目名称;\n 2.1.4.客户名称:显示该租赁合同对应客户名称;\n 2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD;\n 2.1.6.交车任务编码:显示交车任务编码;\n 2.1.7.业务部门:显示该租赁合同对应业务部门名称;\n 2.1.8.业务负责人:显示该租赁合同对应业务负责人;\n 2.1.9.主表右下方为分页符,支持分页和选择单页显示数据条数;\n\n2.2.子表:显示以下字段:账单编号、账单期数、账单开始日期、账单结束日期、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、开票金额、操作;\n 2.2.1.账单编号:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据;\n 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推;\n 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单;\n 2.2.2.账单期数:显示该笔账单对应期数;\n 2.2.3.处理状态:已提交、待提交、已结清;\n 2.2.3.1.已提交:业务人员已通过点击收费明细,对收费项进行过维护并经过二次确认后提交;\n 2.2.3.2.待提交:业务人员未完成收费明细填报;\n 2.2.3.3.已结清:(等对接财务系统后,到账金额等于实收金额总额后算作已结清);\n 2.2.3.账单开始日期:显示该笔账单开始日期,格式为:YYYY-MM-DD;\n 2.2.4.账单结束日期:显示该笔账单结束日期,格式为:YYYY-MM-DD;\n 2.2.5.提车数量:显示提车数量,格式为:xx辆,点击弹出气泡卡片,卡片内为列表,显示:品牌、型号、车牌号;\n 2.2.6.应收款总额:显示该笔账单应收款总额,格式为:xx.xx元,计算方式:「所有车辆月租金总和」+「所有车辆服务费总和」;\n 2.2.7.实收款总额:显示该笔账单实收款总额,格式为:xx.xx元,计算方式:「所有车辆月租金总和」+「所有车辆服务费总和」-「减免总金额」;\n 2.2.8.减免总金额:显示该笔账单减免总金额,格式为:xx.xx元,计算方式:「所有减免金额总和」;\n 2.2.9.车辆成本:根据型号成本表中对应型号车辆成本,租赁订单包含车辆型号*实际账单天数计算得出;\n 2.2.10.氢费成本:显示格式为:xx.xx元,点击变为输入框,后缀为元,支持2位小数,失焦后保存;\n 2.2.11.其他成本:显示格式为:xx.xx元,点击变为输入框,后缀为元,支持2位小数,失焦后保存;\n\n 2.2.12.操作:查看、收费明细;\n 2.2.12.1.查看:点击跳转租赁账单-查看页;\n 2.2.12.2.收费明细:提交后收费明细将不可修改;\n\n'; + return React.createElement('div', { style: layoutStyle }, - React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, React.createElement(Breadcrumb, { items: [ - { title: '业务管理' }, - { title: '租赁账单' } + { title: '业务管理' }, + { title: '租赁账单' } ] - }) + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { requirementModalVisible[1](true); } }, '查看需求说明') ), React.createElement(Card, { title: '筛选', style: cardStyle }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' } }, @@ -357,6 +461,16 @@ const Component = function () { bordered: true, scroll: { x: 958 } }) + ), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalVisible[0], + onCancel: function () { requirementModalVisible[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function () { requirementModalVisible[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent)) ) ); }; diff --git a/web端/业务管理/编辑交车任务.jsx b/web端/业务管理/编辑交车任务.jsx index 6269bba..90d9a62 100644 --- a/web端/业务管理/编辑交车任务.jsx +++ b/web端/业务管理/编辑交车任务.jsx @@ -7,6 +7,7 @@ const Component = function() { var Select = antd.Select; var Button = antd.Button; var DatePicker = antd.DatePicker; + var Modal = antd.Modal; var message = antd.message; var Option = Select.Option; var Checkbox = antd.Checkbox; @@ -17,14 +18,28 @@ const Component = function() { var selectedProjectId = projectId[0]; var setSelectedProjectId = projectId[1]; - var expectedDelivery = React.useState(null); + var expectedDelivery = React.useState(function() { + if (typeof window !== 'undefined' && window.dayjs) { + return [window.dayjs('2026-03-01'), window.dayjs('2026-03-05')]; + } + return null; + }); var expectedDeliveryValue = expectedDelivery[0]; var setExpectedDelivery = expectedDelivery[1]; - var billingDate = React.useState(null); + var billingDate = React.useState(function() { + if (typeof window !== 'undefined' && window.dayjs) { + return window.dayjs('2026-03-06'); + } + return null; + }); var billingDateValue = billingDate[0]; var setBillingDate = billingDate[1]; + var noReturnCarCheckedState = React.useState(false); + var noReturnCarChecked = noReturnCarCheckedState[0]; + var setNoReturnCarChecked = noReturnCarCheckedState[1]; + var selectedRowKeys = React.useState(['v3', 'v4']); var checkedRowKeys = selectedRowKeys[0]; var setCheckedRowKeys = selectedRowKeys[1]; @@ -33,11 +48,9 @@ const Component = function() { var errors = formErrors[0]; var setErrors = formErrors[1]; - var reqSpecState = React.useState(false); - var reqSpecOpen = reqSpecState[0]; - var setReqSpecOpen = reqSpecState[1]; + var reqModalOpen = React.useState(false); - // Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示 + // Mock:项目列表及车辆(与新增交车任务一致,含续签项目) var projectList = [ { id: 'p1', name: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [ { key: 'v1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliveryStatus: 'submitted' }, @@ -46,6 +59,13 @@ const Component = function() { { key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' }, { key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' } ]}, + { id: 'p1_r', name: '嘉兴某某物流氢能运输项目(续签合同)', isRenewal: true, contractCode: 'JXZL20270116YW101235B', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [ + { key: 'v1r_1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '续签合同车辆', deliveryStatus: 'none' }, + { key: 'v1r_0', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A09999', vin: 'LFV2BJCH8K3000000', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_0b', brand: '福田', model: '智蓝氢能轻卡', plateNo: '浙A08888', vin: 'LZYTBACR2M1000001', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_0c', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙A07777', vin: 'ZZ4257N386FZ00002', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '续签:已交车车辆样例', deliveryStatus: 'completed' }, + { key: 'v1r_2', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '续签待上牌', deliveryStatus: 'none' } + ]}, { id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [ { key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' }, { key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' }, @@ -60,13 +80,24 @@ const Component = function() { ]; var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; }); + var isRenewalProject = !!(selectedProject && selectedProject.isRenewal); var vehicleListRaw = selectedProject ? selectedProject.vehicles : []; - var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; }); - var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; }); + var vehicleList = vehicleListRaw.filter(function(v) { + if (v.deliveryStatus === 'completed') { if (!isRenewalProject) return false; return true; } + if (v.deliveryStatus === 'submitted') return true; + if (isRenewalProject && !!noReturnCarChecked) return false; + return true; + }); + var selectableVehicles = vehicleList.filter(function(v) { + if (v.deliveryStatus === 'submitted') return false; + if (v.deliveryStatus === 'completed') return isRenewalProject && !!noReturnCarChecked; + return true; + }); var handleProjectChange = function(id) { setSelectedProjectId(id || ''); setCheckedRowKeys([]); + setNoReturnCarChecked(false); }; var todayStr = (function() { @@ -84,14 +115,15 @@ const Component = function() { return null; }; - var expectedDeliveryError = validateExpectedDelivery(); + var needExpectedDelivery = !(isRenewalProject && noReturnCarChecked); + var expectedDeliveryError = needExpectedDelivery ? validateExpectedDelivery() : null; var billingDateError = !billingDateValue ? '请选择开始计费日期' : null; var handleSubmit = function() { var err = {}; if (!selectedProjectId) err.projectName = '请选择项目名称'; - if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; + if (needExpectedDelivery && expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; if (billingDateError) err.billingDate = billingDateError; if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车'; setErrors(err); @@ -99,6 +131,17 @@ const Component = function() { message.success('交车任务已保存。'); }; + var onToggleNoReturnCar = function(e) { + var next = !!(e && e.target ? e.target.checked : e); + setNoReturnCarChecked(next); + if (!next) { + setCheckedRowKeys(function(prev) { + var completedKeys = vehicleList.filter(function(v) { return v && v.deliveryStatus === 'completed'; }).map(function(v) { return v.key; }); + return prev.filter(function(k) { return completedKeys.indexOf(k) === -1; }); + }); + } + }; + var handleCancel = function() { message.info('取消'); }; @@ -138,21 +181,7 @@ const Component = function() { modalBody: { padding: 20, overflow: 'auto', flex: 1 } }; - var reqSpecBlock = { marginBottom: 16 }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 }; - var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 }; - - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-编辑交车任务')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'), - React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:已选择禁用,不可修改;'), - React.createElement('div', { style: reqSpecLi }, '2.2.合同编码、客户名称、交车区域、交车地点、预计交车日期、开始计费日期、车辆列表等可修改;'), - React.createElement('div', { style: reqSpecLi }, '预计交车日期:不能早于当前日期'), - React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;') - )); - - var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + var reqSpecText = '编辑交车任务\n一个「数字化资产ONEOS运管平台」中的「交车任务」「编辑」模块\n1.面包屑:\n1.1.业务管理-交车任务-编辑\n\n2.表单:\n2.1.选择项目名称:必选项,反写新增时写入的数据,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;;\n2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码;\n2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称;\n2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务;\n2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点;\n2.6.预计交车日期:必填项,反写新增时写入的数据,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期;\n 2.6.1.如勾选不需要还车,则预计交车日期隐藏,不需要设置;\n2.7.开始计费日期:必填项,反写新增时写入的数据,日期选择器,支持单日选择,格式为YYYY-MM-DD;\n2.8.已交车辆是否需要还车:勾选框,反写新增时写入的数据,内容为不需要还车,默认不勾选,不勾选时,无法在车辆列表勾选已交未还的车辆,勾选后,已交未还的车辆也可以勾选,但不会生成新交车任务,只是按照开始计费日期生成账单;\n 2.8.1.勾选时,车辆列表只显示已交未还的车辆清单,不能添加新车(避免已交未还部分车辆开始计费日期计算不精确);\n 2.8.2.不勾选时,车辆列表显示已交未还的车辆清单及续签合同新增车辆,但是已交未还部分的车辆不可勾选;\n2.9.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注;\n 2.9.1.全选/多选:支持全选、多选模式,反写新增时写入的数据,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务和已交车的车辆不可选,多选框为禁用状态,悬浮时提示:该车辆已完成交车/该车辆已创建交车任务;\n 2.9.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌;\n 2.9.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号;\n 2.9.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-;\n 2.9.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码;\n 2.9.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元;\n 2.9.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元;\n 2.9.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元;\n 2.9.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-;\n2.10.页面底部为提交、取消;'; var FormItem = function(props) { return React.createElement('div', { style: styles.formCol }, @@ -167,7 +196,7 @@ const Component = function() { var formRow1 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName }, React.createElement(Select, { - placeholder: '请选择或输入项目名称', + placeholder: '请选择或输入项目名称(对应自营合同、租赁合同-项目名称)', style: { width: '100%' }, value: selectedProjectId || undefined, disabled: true, @@ -182,7 +211,7 @@ const Component = function() { ); var formRow2 = React.createElement('div', { style: styles.formRow }, - React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, + needExpectedDelivery ? React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, React.createElement(RangePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', @@ -190,7 +219,7 @@ const Component = function() { value: expectedDeliveryValue, onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); }, status: errors.expectedDelivery ? 'error' : undefined - })), + })) : null, React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate }, React.createElement(DatePicker, { style: { width: '100%' }, @@ -199,7 +228,17 @@ const Component = function() { value: billingDateValue, onChange: function(d, dateStr) { setBillingDate(d); }, status: errors.billingDate ? 'error' : undefined - })) + })), + isRenewalProject ? React.createElement(FormItem, { label: '已交车辆是否需要还车' }, + React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 8, lineHeight: 1.6 } }, + React.createElement(Checkbox, { checked: !!noReturnCarChecked, onChange: onToggleNoReturnCar }, '不需要还车'), + React.createElement('span', { style: { color: '#8c8c8c', fontSize: 12, marginTop: 2 } }, + '勾选不需要还车,则车辆无需交车,', + React.createElement('br', null), + '只设置开始计费日期即可重新生成账单' + ) + ) + ) : null ); var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); @@ -225,17 +264,27 @@ const Component = function() { var tableBody = React.createElement('tbody', null, vehicleList.length === 0 - ? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '该合同下暂无车辆信息')) + ? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '请先选择项目名称,将自动带出该合同下车辆信息')) : vehicleList.map(function(row) { var isSubmitted = row.deliveryStatus === 'submitted'; + var isCompleted = row.deliveryStatus === 'completed'; + var completedSelectable = isRenewalProject && !!noReturnCarChecked; return React.createElement('tr', { key: row.key }, React.createElement('td', { style: styles.td }, isSubmitted - ? React.createElement(Tooltip, { title: '该车辆已有交车任务' }, - React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } }, - React.createElement(Checkbox, { disabled: true, checked: false }), - React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交'))) - : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), + ? React.createElement(Tooltip, { title: '该车辆已创建交车任务' }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', cursor: 'not-allowed' } }, + React.createElement(Checkbox, { disabled: true, checked: false }))) + : isCompleted + ? React.createElement(Tooltip, { title: '该车辆已完成交车' }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement(Checkbox, { + disabled: !completedSelectable, + checked: completedSelectable ? (checkedRowKeys.indexOf(row.key) !== -1) : false, + onChange: function(e) { onSelectRow(row, e.target.checked); } + }) + )) + : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })), React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })), @@ -267,7 +316,7 @@ const Component = function() { React.createElement('span', null, '交车任务'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '编辑交车任务')), - React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { reqModalOpen[1](true); } }, '查看需求说明')), React.createElement('div', { style: styles.card }, React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), React.createElement('div', { style: styles.cardBody }, @@ -275,7 +324,15 @@ const Component = function() { formRow2, tableEl)), React.createElement('div', { style: { height: 60 } }), - reqSpecModalContent, + React.createElement(Modal, { + title: '需求说明', + open: reqModalOpen[0], + onCancel: function() { reqModalOpen[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function() { reqModalOpen[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, reqSpecText))), React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), React.createElement(Button, { onClick: handleCancel }, '取消')) diff --git a/web端/财务管理/提车应收款-提车收款单.jsx b/web端/财务管理/提车应收款-提车收款单.jsx index 35d2f0f..eec6c59 100644 --- a/web端/财务管理/提车应收款-提车收款单.jsx +++ b/web端/财务管理/提车应收款-提车收款单.jsx @@ -39,6 +39,7 @@ const Component = function () { // 模拟:项目信息(实际由路由/接口拉取) var projectInfo = useMemo(function () { return { + collectCode: 'HT-ZL-2025-001TC0001', contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '嘉兴氢能示范项目', @@ -169,15 +170,33 @@ const Component = function () { }; }, [vehicles]); + var hydrogenReceivable = hasHydrogenPrepay ? (parseFloat(hydrogen.receivable) || 0) : 0; + var hydrogenActual = hasHydrogenPrepay ? (parseFloat(hydrogen.actual) || 0) : 0; + var receivableTotal = useMemo(function () { - return (parseFloat(totals.receivableRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.receivableService)).toFixed(2); - }, [totals]); + var base = parseFloat(totals.receivableRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.receivableService); + return (base + hydrogenReceivable).toFixed(2); + }, [totals, hydrogenReceivable]); var actualTotal = useMemo(function () { - return (parseFloat(totals.actualRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal)).toFixed(2); - }, [totals]); + var base = parseFloat(totals.actualRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal); + return (base + hydrogenActual).toFixed(2); + }, [totals, hydrogenActual]); + + var invoiceTotal = useMemo(function () { + var base = parseFloat(totals.actualRent) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal); + return (base + hydrogenActual).toFixed(2); + }, [totals, hydrogenActual]); var receivablePopoverContent = useMemo(function () { + var rows = [ + React.createElement('tr', { key: 'rent' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableRent + ' 元')), + React.createElement('tr', { key: 'deposit' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')), + React.createElement('tr', { key: 'service' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableService + ' 元')) + ]; + if (hasHydrogenPrepay) { + rows.push(React.createElement('tr', { key: 'hydrogen' }, React.createElement('td', { style: { padding: '6px 8px' } }, '氢费预充值应收金额'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (hydrogen.receivable || '0.00') + ' 元'))); + } return React.createElement('div', { style: { padding: 8, minWidth: 220 } }, React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, React.createElement('thead', null, @@ -186,16 +205,21 @@ const Component = function () { React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') ) ), - React.createElement('tbody', null, - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableRent + ' 元')), - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')), - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableService + ' 元')) - ) + React.createElement('tbody', null, rows) ) ); - }, [totals]); + }, [totals, hasHydrogenPrepay, hydrogen.receivable]); var actualPopoverContent = useMemo(function () { + var rows = [ + React.createElement('tr', { key: 'rent' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualRent + ' 元')), + React.createElement('tr', { key: 'deposit' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')), + React.createElement('tr', { key: 'service' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualService + ' 元')), + React.createElement('tr', { key: 'discount' }, React.createElement('td', { style: { padding: '6px 8px' } }, '总计减免金额'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.discountTotal + ' 元')) + ]; + if (hasHydrogenPrepay) { + rows.push(React.createElement('tr', { key: 'hydrogen' }, React.createElement('td', { style: { padding: '6px 8px' } }, '氢费预充值实收金额'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (hydrogen.actual || '0.00') + ' 元'))); + } return React.createElement('div', { style: { padding: 8, minWidth: 220 } }, React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, React.createElement('thead', null, @@ -204,15 +228,10 @@ const Component = function () { React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额') ) ), - React.createElement('tbody', null, - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualRent + ' 元')), - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')), - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualService + ' 元')), - React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计减免金额'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.discountTotal + ' 元')) - ) + React.createElement('tbody', null, rows) ) ); - }, [totals]); + }, [totals, hasHydrogenPrepay, hydrogen.actual]); var handleSubmit = useCallback(function () { setSubmitConfirmVisible(true); @@ -252,7 +271,7 @@ const Component = function () { var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, lineHeight: '22px', minHeight: 22 }; var thBase = { padding: '10px 12px', border: '1px solid #f0f0f0', whiteSpace: 'nowrap' }; - var requirementContent = '提车应收款-提车收款单(2026年3月10日版本)\n一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「提车收款单」模块\n#面包屑:财务管理-提车应收款-提车收款单;\n\n页面分为3个卡片,业务流程请参考流程图;\n1.项目信息:\n#显示项目详细信息,包括:\n1.1.合同编码:显示该租赁合同对应合同编码;\n1.2.合同类型:显示该租赁合同对应合同类型;\n1.3.项目名称:显示该租赁合同对应项目名称;\n1.4.客户名称:显示该租赁合同对应客户名称;\n1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;\n1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;\n1.7.合同生效时间:显示该租赁合同对应合同生效时间;\n1.8.合同结束时间:显示该租赁合同对应合同结束时间;\n1.9.业务部门:显示该租赁合同对应业务部门名称;\n1.10.业务负责人:显示该租赁合同对应业务负责人;\n\n2.提车应收款信息:\n#上方显示应收款总额、实收款总额;\n2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」;\n 点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;\n 2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;\n 2.1.2.金额:显示该项目对应应收金额;\n2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」;\n 点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;\n 2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;\n 2.2.2.金额:显示该项目对应实收金额;\n\n#中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段:\n2.1.全选/多选:多选按钮组,支持表头点击全选,默认为取消勾选(整个车辆清单);\n 选择代表本次提车应收款只收取该部分车辆费用,可以编辑以下相应字段,不选择则代表本次提车应收款不收取该部分车辆费用,不能编辑车辆相应字段;\n 提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款;\n2.2.序号:根据租赁合同中车辆对应序号顺序展示;\n2.3.品牌:显示租赁合同中车辆对应品牌;\n2.4.型号:显示租赁合同中车辆对应型号;\n2.5.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);\n2.6.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;\n2.7.实收车辆月租金:必填项,输入框,默认反写实收车辆月租金,支持修改,由业务员自行输入车辆月租金金额,精确至2位小数,后缀为元;\n2.8.车辆租金备注:选填项,输入框,由业务员自行输入;\n2.9.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;\n2.10.减免金额备注:选填项,输入框,由业务员自行输入备注信息;\n2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额;\n2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;\n 2.12.1.服务项目:显示租赁合同中所有服务项目名称;\n 2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;\n 2.12.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;\n 2.12.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;\n 2.12.4.备注:选填项,输入框,由业务员自行输入备注信息;\n2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;\n2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;\n2.15.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;\n2.16.减免金额备注:选填项,输入框,由业务员自行输入备注信息;\n2.17.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式,支持多附件上传;\n\n列表不支持分页功能;\n#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:\n2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;\n2.20.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;\n2.21.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;\n2.22.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;\n#最底部为开票方式、开票备注;\n2.23.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;\n2.24.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;\n\n3.底部为提交审核,保存,取消;\n3.1.提交审核:点击提交审核,进行二次确认,文案为:请仔细核对提车首付款实收金额,点击确认则进入工作流;\n3.2.保存:点击保存,提示保存成功,跳转至提车应收款列表页,同时该条数据审核状态为:待提交:\n3.3.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;'; + var requirementContent = '提车应收款-提车收款单(2026年3月10日版本)\n一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「提车收款单」模块\n#面包屑:财务管理-提车应收款-提车收款单;\n\n页面分为3个卡片,业务流程请参考流程图;\n1.项目信息:\n#显示项目详细信息,包括:\n1.1.合同编码:显示该租赁合同对应合同编码;\n1.2.合同类型:显示该租赁合同对应合同类型;\n1.3.项目名称:显示该租赁合同对应项目名称;\n1.4.客户名称:显示该租赁合同对应客户名称;\n1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;\n1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;\n1.7.合同生效时间:显示该租赁合同对应合同生效时间;\n1.8.合同结束时间:显示该租赁合同对应合同结束时间;\n1.9.业务部门:显示该租赁合同对应业务部门名称;\n1.10.业务负责人:显示该租赁合同对应业务负责人;\n\n2.提车应收款信息:\n#上方显示应收款总额、实收款总额;\n2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」+「氢费预充值应收金额」;\n 点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;\n 2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;\n 2.1.2.金额:显示该项目对应应收金额;\n2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」+「氢费预充值实收金额」-「选中车辆减免金额总和」;\n 点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;\n 2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;\n 2.2.2.金额:显示该项目对应实收金额;\n2.3.应开票总额:格式为:xx.xx元,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」;\n\n#中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段:\n2.1.全选/多选:多选按钮组,支持表头点击全选,默认为取消勾选(整个车辆清单);\n 选择代表本次提车应收款只收取该部分车辆费用,可以编辑以下相应字段,不选择则代表本次提车应收款不收取该部分车辆费用,不能编辑车辆相应字段;\n 提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款/该车辆已提交提车收款单;\n2.2.序号:根据租赁合同中车辆对应序号顺序展示;\n2.3.品牌:显示租赁合同中车辆对应品牌;\n2.4.型号:显示租赁合同中车辆对应型号;\n2.5.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);\n2.6.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;\n2.7.实收车辆月租金:必填项,输入框,默认反写实收车辆月租金,支持修改,由业务员自行输入车辆月租金金额,精确至2位小数,后缀为元;\n2.8.车辆租金备注:选填项,输入框,由业务员自行输入;\n2.9.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;\n2.10.减免金额备注:选填项,输入框,由业务员自行输入备注信息;\n2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额;\n2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;\n 2.12.1.服务项目:显示租赁合同中所有服务项目名称;\n 2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;\n 2.12.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;\n 2.12.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;\n 2.12.4.备注:选填项,输入框,由业务员自行输入备注信息;\n2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;\n2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;\n2.15.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;\n2.16.减免金额备注:选填项,输入框,由业务员自行输入备注信息;\n2.17.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式,支持多附件上传;\n\n列表不支持分页功能;\n#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:\n2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;\n2.20.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;\n2.21.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;\n2.22.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;\n#最底部为开票方式、开票备注;\n2.23.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;\n2.24.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;\n\n3.底部为提交审核,保存,取消;\n3.1.提交审核:点击提交审核,进行二次确认,文案为:请仔细核对提车首付款实收金额,点击确认则进入工作流;\n3.2.保存:点击保存,提示保存成功,跳转至提车应收款列表页,同时该条数据审核状态为:待提交:\n3.3.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;'; var selectableVehicles = vehicles.filter(function (v) { return !v.receivableCompleted; }); var allSelected = selectableVehicles.length > 0 && selectableVehicles.every(function (v) { return v.selected; }); @@ -279,6 +298,10 @@ const Component = function () { extra: React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setProjectCardExpanded(!projectCardExpanded[0]); } }, projectCardExpanded[0] ? '收起' : '展开') }, projectCardExpanded[0] ? React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '提车收款单编码'), + React.createElement('div', { style: valueStyle }, projectInfo.collectCode || '-') + ), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同编码'), React.createElement('div', { style: valueStyle }, projectInfo.contractCode || '-') @@ -327,13 +350,14 @@ const Component = function () { extra: React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setReceivableCardExpanded(!receivableCardExpanded[0]); } }, receivableCardExpanded[0] ? '收起' : '展开') }, receivableCardExpanded[0] ? React.createElement(React.Fragment, null, - React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'center' } }, + React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'center', flexWrap: 'wrap' } }, React.createElement(Popover, { content: receivablePopoverContent, title: '应收款明细', trigger: 'click' }, React.createElement('span', { style: { cursor: 'pointer' } }, '应收款总额:', React.createElement('span', { style: highlightStyle }, receivableTotal, ' 元')) ), React.createElement(Popover, { content: actualPopoverContent, title: '实收款明细', trigger: 'click' }, React.createElement('span', { style: { cursor: 'pointer' } }, '实收款总额:', React.createElement('span', { style: highlightStyle }, actualTotal, ' 元')) - ) + ), + React.createElement('span', null, '应开票总额:', React.createElement('span', { style: highlightStyle }, invoiceTotal, ' 元')) ), React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16, overflowY: 'visible' } }, React.createElement('table', { style: { width: '100%', minWidth: 1600, borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' } }, diff --git a/web端/财务管理/提车应收款-查看.jsx b/web端/财务管理/提车应收款-查看.jsx index 2eb940d..e47468f 100644 --- a/web端/财务管理/提车应收款-查看.jsx +++ b/web端/财务管理/提车应收款-查看.jsx @@ -19,6 +19,7 @@ const Component = function () { // 模拟:项目信息 var projectInfo = useMemo(function () { return { + collectCode: 'HT-ZL-2025-001TC0001', contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '嘉兴氢能示范项目', @@ -98,17 +99,6 @@ const Component = function () { ) ); - // 客户付款信息:到账/开票历史 - var paymentList = useMemo(function () { - return [ - { id: '1', arrivalTime: '2026-03-01 10:00', arrivalAmount: '50000.00', invoiceTime: '2026-03-02 14:30', invoiceFiles: [{ name: '发票-HT-ZL-2025-001-001.pdf' }], remark: '首笔到账' }, - { id: '2', arrivalTime: '2026-03-10 09:00', arrivalAmount: '35280.00', invoiceTime: '2026-03-11 11:00', invoiceFiles: [{ name: '发票-HT-ZL-2025-001-002.pdf' }], remark: '第二笔' } - ]; - }, []); - var sumArrival = paymentList.reduce(function (s, r) { return s + (parseFloat(r.arrivalAmount) || 0); }, 0); - var unpaidAmount = (parseFloat(actualTotal) - sumArrival).toFixed(2); - if (parseFloat(unpaidAmount) < 0) unpaidAmount = '0.00'; - // 审批情况:竖向步骤条 var approvalSteps = useMemo(function () { return [ @@ -145,6 +135,7 @@ const Component = function () { ), React.createElement(Card, { title: '项目信息', style: cardStyle }, React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '提车收款单编码'), React.createElement('div', { style: valueStyle }, projectInfo.collectCode || '—')), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同编码'), React.createElement('div', { style: valueStyle }, projectInfo.contractCode || '—')), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同类型'), React.createElement('div', { style: valueStyle }, projectInfo.contractType || '—')), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '项目名称'), React.createElement('div', { style: valueStyle }, projectInfo.projectName || '—')), @@ -251,35 +242,6 @@ const Component = function () { ) ) ), - React.createElement(Card, { title: '客户付款信息', style: cardStyle }, - React.createElement('div', { style: { marginBottom: 12, fontSize: 14 } }, '未付金额:', React.createElement('span', { style: highlightStyle }, unpaidAmount, ' 元')), - React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, - React.createElement('thead', null, - React.createElement('tr', null, - React.createElement('th', { style: Object.assign({}, thBase, { width: 160 }) }, '到账时间'), - React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '到账金额'), - React.createElement('th', { style: Object.assign({}, thBase, { width: 160 }) }, '开票时间'), - React.createElement('th', { style: thBase }, '发票附件'), - React.createElement('th', { style: thBase }, '备注') - ) - ), - React.createElement('tbody', null, - paymentList.map(function (r) { - var files = (r.invoiceFiles || []).map(function (f, i) { return React.createElement(Button, { key: i, type: 'link', size: 'small', style: { padding: 0, height: 'auto' }, onClick: function () { message.info('预览:' + (f.name || '附件')); } }, f.name || '附件'); }); - var remarkEl = r.remark && r.remark.length > 20 - ? React.createElement(Tooltip, { title: r.remark }, React.createElement('span', { style: { display: 'inline-block', maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, r.remark)) - : (r.remark || '—'); - return React.createElement('tr', { key: r.id }, - React.createElement('td', { style: tdBase }, r.arrivalTime || '—'), - React.createElement('td', { style: tdBase }, r.arrivalAmount ? r.arrivalAmount + ' 元' : '—'), - React.createElement('td', { style: tdBase }, r.invoiceTime || '—'), - React.createElement('td', { style: tdBase }, files.length ? React.createElement('span', null, files) : '—'), - React.createElement('td', { style: tdBase }, remarkEl) - ); - }) - ) - ) - ), React.createElement(Card, { title: '审批情况', style: cardStyle }, React.createElement(Steps, { direction: 'vertical', diff --git a/web端/财务管理/提车应收款.jsx b/web端/财务管理/提车应收款.jsx index 705afc5..6b3aa0f 100644 --- a/web端/财务管理/提车应收款.jsx +++ b/web端/财务管理/提车应收款.jsx @@ -44,6 +44,7 @@ const Component = function () { businessDept: '业务1部', businessPerson: '张经理', contractEffectiveDate: '2025-01-15', + allVehiclesReceivableCompleted: false, totalReceivable: 256800.00, totalActual: 255700.00, totalDiscount: 1100.00, @@ -64,6 +65,7 @@ const Component = function () { businessDept: '业务2部', businessPerson: '李专员', contractEffectiveDate: '2025-02-01', + allVehiclesReceivableCompleted: true, totalReceivable: 132600.00, totalActual: 132600.00, totalDiscount: 0.00, @@ -82,6 +84,7 @@ const Component = function () { businessDept: '业务3部', businessPerson: '王专员', contractEffectiveDate: '2025-02-10', + allVehiclesReceivableCompleted: false, totalReceivable: 82400.00, totalActual: 82400.00, totalDiscount: 0.00, @@ -156,10 +159,6 @@ const Component = function () { if (window.__receivableToEdit) window.__receivableToEdit(record); else message.info('跳转至提车应收款-编辑(原型)'); } - function goInvoice(record) { - if (window.__receivableToInvoice) window.__receivableToInvoice(record); - else message.info('跳转至开票(原型)'); - } var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; var cardStyle = { marginBottom: 16 }; @@ -182,6 +181,8 @@ const Component = function () { width: 100, fixed: 'right', render: function (_, record) { + // 该合同所有车辆都已完成提车应收款时,不显示“提车收款单” + if (record && record.allVehiclesReceivableCompleted) return '—'; return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goCollect(record); } }, '提车收款单'); } } @@ -227,8 +228,23 @@ const Component = function () { // 子表左缩进与主表展开列宽一致,使序号左侧边框与合同编码左侧边框对齐 var expandColumnWidth = 48; + function buildCollectCode(contractCode, seq) { + var suffix = 'TC' + ('0000' + String(seq != null ? seq : '')).slice(-4); + return String(contractCode || '') + suffix; + } var subColumns = [ { title: '序号', dataIndex: 'seq', key: 'seq', width: 52, align: 'center' }, + { + title: '提车收款单编码', + key: 'collectCode', + width: 180, + ellipsis: true, + render: function (_, record) { + var parent = record && record._parentRecord; + var code = parent ? parent.contractCode : (record._parentCode || ''); + return buildCollectCode(code, record.seq) || '—'; + } + }, { title: '审批状态', dataIndex: 'auditStatus', @@ -248,9 +264,6 @@ const Component = function () { { title: '应收款总额', dataIndex: 'receivableTotal', key: 'receivableTotal', width: 98, align: 'right', render: function (v) { return fmtMoney(v); } }, { title: '实收款总额', dataIndex: 'actualTotal', key: 'actualTotal', width: 98, align: 'right', render: function (v) { return fmtMoney(v); } }, { title: '减免总金额', dataIndex: 'discountTotal', key: 'discountTotal', width: 88, align: 'right', render: function (v) { return fmtMoney(v); } }, - { title: '实际到账金额', dataIndex: 'arrivalAmount', key: 'arrivalAmount', width: 100, align: 'right', render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; return fmtMoney(v); } }, - { title: '是否已开票', dataIndex: 'isInvoiced', key: 'isInvoiced', width: 88, render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; if (v === true || v === '已开票') return '已开票'; if (v === '部分开票') return '部分开票'; return '未开票'; } }, - { title: '已开票金额', dataIndex: 'invoicedAmount', key: 'invoicedAmount', width: 100, align: 'right', render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; var n = typeof v === 'number' ? v : parseFloat(v); return fmtMoney(isNaN(n) ? 0 : n); } }, { title: '操作', key: 'action', @@ -260,7 +273,6 @@ const Component = function () { var auditStatus = record && record.auditStatus; var isPending = auditStatus === '待审批'; var isRejected = auditStatus === '已驳回' || auditStatus === '审批驳回'; - var isApproved = auditStatus === '审批通过'; var canEdit = isPending || isRejected; var parentRecord = record && record._parentRecord; function handleDelete() { @@ -271,8 +283,7 @@ const Component = function () { return React.createElement(Space, { size: 'small' }, React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record); } }, '查看'), canEdit ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goEdit(record); } }, '编辑') : null, - (isPending || isRejected) ? React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: handleDelete }, '删除') : null, - isApproved ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goInvoice(record); } }, '开票') : null + (isPending || isRejected) ? React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: handleDelete }, '删除') : null ); } } @@ -299,12 +310,6 @@ const Component = function () { } function expandRowRender(record) { var rows = (record._detailList || []).map(function (r) { var o = {}; for (var k in r) o[k] = r[k]; o._parentCode = record.contractCode; o._parentRecord = record; return o; }); - var sumReceivable = rows.reduce(function (acc, r) { return acc + (Number(r.receivableTotal) || 0); }, 0); - var sumActual = rows.reduce(function (acc, r) { return acc + (Number(r.actualTotal) || 0); }, 0); - var sumDiscount = rows.reduce(function (acc, r) { return acc + (Number(r.discountTotal) || 0); }, 0); - var sumArrival = rows.reduce(function (acc, r) { return acc + (Number(r.arrivalAmount) || 0); }, 0); - var sumInvoiced = rows.reduce(function (acc, r) { return acc + (Number(r.invoicedAmount) || 0); }, 0); - var summaryStyle = { backgroundColor: '#fafafa', fontWeight: 600 }; return React.createElement('div', { style: { marginBottom: 0, paddingLeft: expandColumnWidth, boxSizing: 'border-box' }, draggable: false, @@ -316,30 +321,12 @@ const Component = function () { dataSource: rows, pagination: false, size: 'small', - bordered: true, - summary: rows.length > 0 ? function () { - return React.createElement(Table.Summary, null, - React.createElement(Table.Summary.Row, { style: summaryStyle }, - React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '总计'), - React.createElement(Table.Summary.Cell, { index: 1 }, ''), - React.createElement(Table.Summary.Cell, { index: 2 }, ''), - React.createElement(Table.Summary.Cell, { index: 3 }, ''), - React.createElement(Table.Summary.Cell, { index: 4 }, ''), - React.createElement(Table.Summary.Cell, { index: 5, align: 'right' }, fmtMoney(sumReceivable)), - React.createElement(Table.Summary.Cell, { index: 6, align: 'right' }, fmtMoney(sumActual)), - React.createElement(Table.Summary.Cell, { index: 7, align: 'right' }, fmtMoney(sumDiscount)), - React.createElement(Table.Summary.Cell, { index: 8, align: 'right' }, fmtMoney(sumArrival)), - React.createElement(Table.Summary.Cell, { index: 9 }, ''), - React.createElement(Table.Summary.Cell, { index: 10, align: 'right' }, fmtMoney(sumInvoiced)), - React.createElement(Table.Summary.Cell, { index: 11 }, '') - ) - ); - } : null + bordered: true }) ); } - var requirementContent = '提车应收款(2026年3月5日版本)\n一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」模块\n#面包屑:财务管理-提车应收款;\n\n页面分为2个卡片;\n1.筛选:\n#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式;\n1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.4.业务部门:选择器,支持选择所有业务部门;\n1.5.业务负责人:选择器,支持选择所有业务负责人;\n\n2.列表:\n#列表展示方式为:嵌套子表格,分为主表和子表,可点击主表展开子表,一个合同编码可以展开多个提车应收款明细;\n2.1.主表显示字段为:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人、操作;\n2.1.1.合同编码:显示该提车应收款租赁合同对应合同编码;\n2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同;\n2.1.3.项目名称:显示该租赁合同对应项目名称;\n2.1.4.客户名称:显示该租赁合同对应客户名称;\n2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD;\n2.1.6.业务部门:显示该租赁合同对应业务部门名称;\n2.1.7.业务负责人:显示该租赁合同对应业务负责人;\n2.1.8.操作:提车收款单;\n 2.1.8.1.提车收款单:点击提车收款单,跳转提车收款单页进行子表创建;\n主表右下角为分页符,支持选择单页显示多少条数据;\n\n2.2.子表显示字段为:序号、审批状态、创建时间、提车数量、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、已开票金额、操作;\n2.2.1.序号:按照提车首付款收费单据提交时间从最早开始显示第1条,按照顺序1.2.3....;\n2.2.2.审批状态:审批通过、待审批、审批中、审批驳回;\n 2.2.2.1.审批通过:该审批通过最终节点审批,显示为审批通过;\n 2.2.2.2.待审批:审批提车收款单仅保存,但未提交审批;\n 2.2.2.3.审批中:已发起审批流程,但未完成最终节点审批;\n 2.2.2.4.审批驳回:发起审批流程,但任意节点被驳回。被驳回的提车收款单支持重新修改后提交审批;\n2.2.3.创建时间:显示提车收款单创建时间,格式为:YYYY-MM-DD HH:MM;\n2.2.4.创建人:显示已提车收款单创建人姓名;\n2.2.5.提车数量:显示该提车收费单对应提车数量,点击提车数量弹出气泡卡片,卡片中列表显示品牌、型号、车牌号;\n2.2.6.应收款金额:按照该提车收费单据应收款金额,格式为:xx.xx元;\n2.2.7.实收款金额:按照该提车收费单据实收款金额,格式为:xx.xx元;\n2.2.8.减免金额:按照该提车收费单减免总金额,显示当前子表对应减免总额,格式为:xx.xx元;\n2.2.9.实际到账金额:显示该提车收费单财务填写的实际到账金额,格式为:xx.xx元;\n2.2.10.是否已开票:显示财务是否完成开票,已开票显示为:已开票,未开票显示为:未开票,部分开票显示为:部分开票;\n2.2.11.已开票金额:显示财务已开票金额,格式为:xx.xx元;\n2.2.12.总计:显示应收款总额、实收款总额、减免总金额、实际到账金额、已开票金额等所有子表求和数据;\n2.2.13.操作:查看、编辑、删除、开票;\n 2.2.12.1.查看:点击查看后,跳转提车应收款-查看页面,审批通过、待审批、审批中、审批驳回均可点击查看来查看提车收款单详情;\n 2.2.12.2.编辑:点击编辑后,跳转提车应收款-编辑页面,待审批、审批驳回均可进行编辑操作;\n 2.2.12.2.删除:待审批、审批驳回状态子表支持删除功能,点击弹出二次确认,点击确认后删除该记录;\n 2.2.12.3.开票:点击开票后,进入提车应收款-开票页面,审批通过状态子表才显示,开票由财务人员操作,需要做权限控制,其他用户没有开票入口;'; + var requirementContent = '提车应收款(2026年3月10日版本)\n一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」模块\n#面包屑:财务管理-提车应收款;\n\n页面分为2个卡片;\n1.筛选:\n#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式;\n1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.4.业务部门:选择器,支持选择所有业务部门;\n1.5.业务负责人:选择器,支持选择所有业务负责人;\n\n2.列表:\n#列表展示方式为:嵌套子表格,分为主表和子表,可点击主表展开子表,一个合同编码可以展开多个提车应收款明细;\n2.1.主表显示字段为:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人、操作;\n2.1.1.合同编码:显示该提车应收款租赁合同对应合同编码;\n2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同;\n2.1.3.项目名称:显示该租赁合同对应项目名称;\n2.1.4.客户名称:显示该租赁合同对应客户名称;\n2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD;\n2.1.6.业务部门:显示该租赁合同对应业务部门名称;\n2.1.7.业务负责人:显示该租赁合同对应业务负责人;\n2.1.8.操作:提车收款单;\n 2.1.8.1.提车收款单:点击提车收款单,跳转提车收款单页进行子表创建,该合同所有车辆都已完成提车应收款后,该按钮不显示;\n主表右下角为分页符,支持选择单页显示多少条数据;\n\n2.2.子表显示字段为:序号、审批状态、创建时间、提车数量、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、已开票金额、操作;\n2.2.1.序号:按照提车首付款收费单据提交时间从最早开始显示第1条,按照顺序1.2.3....;\n2.2.2.审批状态:审批通过、待审批、审批中、已驳回;\n 2.2.2.1.审批通过:该审批通过最终节点审批,显示为审批通过;\n 2.2.2.2.待审批:审批提车收款单仅保存,但未提交审批;\n 2.2.2.3.审批中:已发起审批流程,但未完成最终节点审批;\n 2.2.2.4.审批驳回:发起审批流程,但任意节点被驳回。被驳回的提车收款单支持重新修改后提交审批;\n2.2.3.创建时间:显示提车收款单创建时间,格式为:YYYY-MM-DD HH:MM;\n2.2.4.创建人:显示已提车收款单创建人姓名;\n2.2.5.提车数量:显示该提车收费单对应提车数量,点击提车数量弹出气泡卡片,卡片中列表显示品牌、型号、车牌号;\n2.2.6.应收款金额:按照该提车收费单据应收款金额,格式为:xx.xx元;\n2.2.7.实收款金额:按照该提车收费单据实收款总额,格式为:xx.xx元;\n2.2.8.减免金额:按照该提车收费单减免总金额,显示当前子表对应减免总额,格式为:xx.xx元;\n2.2.9.实际到账金额:显示该提车收费单财务填写的实际到账金额,格式为:xx.xx元;\n2.2.10.是否已开票:显示财务是否完成开票,已开票显示为:已开票,未开票显示为:未开票,部分开票显示为:部分开票;\n2.2.11.已开票金额:显示财务已开票金额,格式为:xx.xx元;\n2.2.12.总计:显示应收款总额、实收款总额、减免总金额、实际到账金额、已开票金额等所有子表求和数据;\n2.2.13.操作:查看、编辑、删除;\n 2.2.12.1.查看:点击查看后,跳转提车应收款-查看页面,审批通过、待审批、审批中、审批驳回均可点击查看来查看提车收款单详情;\n 2.2.12.2.编辑:点击编辑后,跳转提车应收款-编辑页面,待审批、审批驳回均可进行编辑操作;\n 2.2.12.2.删除:待审批、审批驳回状态子表支持删除功能,点击弹出二次确认,点击确认后删除该记录;\n '; return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, @@ -423,7 +410,7 @@ const Component = function () { React.createElement(Button, { type: 'link', size: 'small', onClick: function () { filterExpanded[1](!filterExpanded[0]); } }, filterExpanded[0] ? '收起' : '展开') ) ), - React.createElement(Card, { title: '列表', style: cardStyle }, + React.createElement(Card, { title: '提车应收款', style: cardStyle }, React.createElement(Table, { rowKey: 'contractCode', columns: mainColumns, diff --git a/web端/财务管理/提车收款单-编辑.jsx b/web端/财务管理/提车收款单-编辑.jsx index 266a57e..d136305 100644 --- a/web端/财务管理/提车收款单-编辑.jsx +++ b/web端/财务管理/提车收款单-编辑.jsx @@ -39,6 +39,7 @@ const Component = function () { // 模拟:项目信息(实际由路由/接口拉取) var projectInfo = useMemo(function () { return { + collectCode: 'HT-ZL-2025-001TC0001', contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '嘉兴氢能示范项目', @@ -268,6 +269,10 @@ const Component = function () { extra: React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setProjectCardExpanded(!projectCardExpanded[0]); } }, projectCardExpanded[0] ? '收起' : '展开') }, projectCardExpanded[0] ? React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '提车收款单编码'), + React.createElement('div', { style: valueStyle }, projectInfo.collectCode || '-') + ), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同编码'), React.createElement('div', { style: valueStyle }, projectInfo.contractCode || '-') diff --git a/web端/车辆租赁合同/车辆租赁合同-查看.jsx b/web端/车辆租赁合同/车辆租赁合同-查看.jsx index 5d03824..c6ba059 100644 --- a/web端/车辆租赁合同/车辆租赁合同-查看.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-查看.jsx @@ -144,24 +144,37 @@ const Component = function() { { title: '法务部', person: '高洁 / 彭青松', status: '已通过', approveTime: '2026-02-16 14:00' } ]; - var approvalStepEls = approvalSteps.map(function(step, index) { - var isLast = index === approvalSteps.length - 1; - var lineStyle = Object.assign({}, styles.stepLine, step.status === '已通过' ? styles.stepLineDone : {}); - if (isLast) lineStyle.display = 'none'; - return React.createElement('div', { key: index, style: Object.assign({}, styles.stepItem, { zIndex: approvalSteps.length - index }) }, - !isLast ? React.createElement('div', { style: lineStyle }) : null, - React.createElement('div', { style: Object.assign({}, styles.stepIcon, styles.stepIconDone) }, '✓'), - React.createElement('div', { style: styles.stepTitle }, step.title), - React.createElement('div', { style: styles.stepDesc }, step.person), - React.createElement('div', { style: styles.stepStatus }, step.status), - step.approveTime ? React.createElement('div', { style: styles.stepTime }, step.approveTime) : null - ); - }); - - var approvalCardEl = React.createElement('div', { style: styles.approvalCard }, - React.createElement('div', { style: styles.approvalCardHeader }, '审批状态'), - React.createElement('div', { style: styles.approvalCardBody }, - React.createElement('div', { style: styles.stepWrap }, approvalStepEls) + var Steps = window.antd && window.antd.Steps; + var approvalCurrent = (function() { + for (var i = 0; i < approvalSteps.length; i++) { + if (approvalSteps[i].status !== '已通过') return i; + } + return approvalSteps.length - 1; + })(); + var approvalCardEl = React.createElement('div', { style: Object.assign({}, styles.card, { marginTop: 16 }) }, + React.createElement('div', { style: Object.assign({}, styles.cardHeader, { cursor: 'default' }) }, + React.createElement('span', { style: styles.cardTitle }, '审批状态') + ), + React.createElement('div', { style: styles.cardBody }, + Steps + ? React.createElement(Steps, { + direction: 'vertical', + current: approvalCurrent, + items: approvalSteps.map(function(s) { + var statusText = s.status === '已通过' ? '审批通过' : s.status === '审批驳回' ? '审批驳回' : '待审批'; + var desc = React.createElement('div', { style: { fontSize: 13, color: 'rgba(0,0,0,0.65)', marginTop: 4 } }, + React.createElement('div', null, '审批状态:', statusText), + React.createElement('div', null, '审批人:', s.person || '-'), + React.createElement('div', null, '审批时间:', s.approveTime || '-') + ); + return { + title: s.title, + description: desc, + status: s.status === '已通过' ? 'finish' : s.status === '审批驳回' ? 'error' : 'wait' + }; + }) + }) + : React.createElement('div', { style: { color: '#999', fontSize: 13 } }, '暂无审批信息') ) ); @@ -180,13 +193,13 @@ const Component = function() { var mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', remarks: '' }; var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }]; var mockRentalOrders = [ - { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }, { project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] }, - { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] } + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vehicleStatus: '已交车', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }, { project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] }, + { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vehicleStatus: '已交车-临时替换', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] } ]; var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' }; var addVehicleDate = '2026-03-15'; var mockNewVehicleOrders = [ - { brand: '品牌B', model: '型号B1', plateNo: '沪A30003', vin: 'L3234567890ABCDEF', monthRent: '7000', serviceFee: '400', deposit: '8000', remark: '新增车辆', serviceItems: [{ project: '保养费用', fee: '180', effectiveDate: '2026-04-01' }] } + { brand: '品牌B', model: '型号B1', plateNo: '沪A30003', vehicleStatus: '待交车', vin: 'L3234567890ABCDEF', monthRent: '7000', serviceFee: '400', deposit: '8000', remark: '新增车辆', serviceItems: [{ project: '保养费用', fee: '180', effectiveDate: '2026-04-01' }] } ]; var mockNewVehicleSummary = { vehicleCount: 1, totalRentService: '7400.00', totalDeposit: '8000.00' }; var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' }; @@ -358,6 +371,7 @@ const Component = function() { React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.brand)), React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.model)), React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.plateNo)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vehicleStatus || '—')), React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vin)), React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')), React.createElement('td', { style: styles.rentalTd }, serviceCell), @@ -373,6 +387,7 @@ const Component = function() { React.createElement('th', { style: styles.rentalTh, width: 100 }, '品牌'), React.createElement('th', { style: styles.rentalTh, width: 100 }, '型号'), React.createElement('th', { style: styles.rentalTh, width: 120 }, '车牌号'), + React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆状态'), React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆识别代码'), React.createElement('th', { style: styles.rentalTh, width: 120 }, '车辆月租金'), React.createElement('th', { style: styles.rentalTh, width: 80 }, '服务费项目'), @@ -496,7 +511,7 @@ const Component = function() { ) ); - var requirementContent = '车辆租赁合同-查看(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「查看租赁合同」模块,在车辆租赁合同操作列点击「查看」进行查看;\n1.面包屑:\n#业务管理-车辆租赁合同-查看合同\n\n2.审批状态:\n#显示审批工作流顺序、审批部门、审批人员、审批状态、审批时间;\n2.1.工作流顺序:以横向步骤条方式从左到右显示,已审批节点和未审批节点样式有区分;\n2.2.审批部门:显示对应审批部门名称;\n2.3.审批人员:显示对应审批人员姓名,多个人员以/分隔;\n2.4.审批状态:显示审批状态,分为:已通过、待审批;\n2.5.审批时间:显示审批操作时间,格式为:YYYY-MM-DD HH:MM,待审批时显示为:-;\n\n3.客户基本信息卡片:\n#显示已审批合同实际信息,不可修改,当客户信息产生变更时,对该条历史合同数据不作更新,合同变更为三方合同时不在原有客户基本信息做修改,而是增加三方客户基本信息卡片;\n3.1.客户名称:从该条合同自动反查;\n3.2.客户统一信用代码:从该条合同自动反查;\n3.3.客户地址:从该条合同自动反查;\n3.4.客户联系人:从该条合同自动反查;\n3.5.客户电话:从该条合同自动反查;\n3.6.客户电子邮箱:从该条合同自动反查;\n3.7.企业名称:从该条合同自动反查;\n3.8.企业电话:从该条合同自动反查;\n3.9.邮寄地址:从该条合同自动反查;\n3.10.开户银行:从该条合同自动反查;\n3.11.银行账号:从该条合同自动反查;\n3.12.纳税人识别号:从该条合同自动反查;\n3.13.业务部门:从该条合同自动反查;\n3.14.业务负责人:从该条合同自动反查;\n\n4.丙方客户基本信息卡片(当合同转三方合同时显示):\n#显示已审批合同丙方实际信息,不可修改;;\n4.1.客户名称:从该条合同自动反查;\n4.2.客户统一信用代码:从该条合同自动反查;\n4.3.客户地址:从该条合同自动反查;\n4.4.客户联系人:从该条合同自动反查;\n4.5.客户电话:从该条合同自动反查;\n4.6.客户电子邮箱:从该条合同自动反查;\n4.7.企业名称:从该条合同自动反查;\n4.8.企业电话:从该条合同自动反查;\n4.9.邮寄地址:从该条合同自动反查;\n4.10.开户银行:从该条合同自动反查;\n4.11.银行账号:从该条合同自动反查;\n4.12.纳税人识别号:从该条合同自动反查;\n4.13.业务部门:从该条合同自动反查;\n4.14.业务负责人:从该条合同自动反查;\n\n5.合同基本信息卡片:\n#显示合同基本信息,不可修改;\n5.1.项目名称:从该条合同自动反查;\n5.2.合同编码:从该条合同自动反查,在新增租赁合同点击提交审核时生成;\n 合同编码由:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]组成;\n 5.2.1.地区简写:如上海为SH,嘉兴为JX;\n 5.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;\n 5.2.3.签约时间:显示合同签约时间,如20260216\n 5.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;\n 5.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团编号为1235的合同;\n 5.2.6.签署状态:A为正式合同,B为试用合同;\n 如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中编号为1235,也可以理解为第1235份合同;\n 5.2.7.如果该合同为续签合同,则自动在新合同编码后额外添加:(续签自:旧合同合同编码xxx);\n 5.2.8.如果该合同为转正式合同,则自动在新合同编码后额外添加:(转正式合同自:旧合同合同编码xxx);\n5.3.合同类型:从该条合同自动反查;\n5.4.生效日期:从该条合同自动反查;\n5.5.付款方式:从该条合同自动反查;\n5.6.主要车型:从该条合同自动反查;\n5.7.结束日期:从该条合同自动反查;\n 5.7.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 5.7.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 5.7.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n5.8.付款周期:从该条合同自动反查;\n5.9.签约公司:从该条合同自动反查;\n5.10.交车区域:从该条合同自动反查;\n5.11.交车地点:从该条合同自动反查;\n5.12.合同原件:从该条合同自动反查,显示「合同原件名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列,如果转为三方合同,则会显示新附件,显示「三方合同原件名称.格式」「文件大小」「上传时间」;\n5.13.备注:从该条合同自动反查;\n 5.13.1.如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx;\n 5.13.2.如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx;\n\n6.被授权人信息卡片:\n#显示所有被授权人信息,不可修改,如要新增被授权人,需要在列表中点击添加被授权人进行处理;\n6.1.被授权人:从该条合同自动反查;\n6.2.被授权人联系电话:从该条合同自动反查;\n6.3.被授权人身份证:从该条合同自动反查;\n\n7.租赁订单信息卡片:\n#显示租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n7.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 7.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 7.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 7.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 7.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n7.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 7.2.1.序号:从该条合同自动反查;\n 7.2.2.品牌:从该条合同自动反查;\n 7.2.3.型号:从该条合同自动反查;\n 7.2.4.车牌号:从该条合同自动反查;\n 7.2.5.车辆识别代码:从该条合同自动反查;\n 7.2.6.车辆月租金(元):从该条合同自动反查;\n 7.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 7.2.7.1.服务项目:从该条合同自动反查;\n 7.2.7.2.费用:从该条合同自动反查;\n 7.2.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 7.2.8.服务费:从该条合同自动反查;\n 7.2.9.保证金:从该条合同自动反查;\n 7.2.10.备注:从该条合同自动反查;\n7.3.氢费承担方:从该条合同自动反查;\n7.4.付款方式:从该条合同自动反查;\n7.5.氢气预付款:从该条合同自动反查;\n7.6.退还车氢气单价:从该条合同自动反查,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n8.新增车辆信息卡片(用户每次为合同新增车辆时,都会生成一个新增车辆信息卡片):\n#显示租赁合同对应新增车辆的明细费用、氢费明细费用等相关信息;\n8.1.卡片标题显示为:新增车辆信息(YYYY-MM-DD)\n8.2.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额等相关信息;\n 8.2.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 8.2.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 8.2.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n8.3.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 8.3.1.序号:从该条合同自动反查;\n 8.3.2.品牌:从该条合同自动反查;\n 8.3.3.型号:从该条合同自动反查;\n 8.3.4.车牌号:从该条合同自动反查;\n 8.3.5.车辆识别代码:从该条合同自动反查;\n 8.3.6.车辆月租金:从该条合同自动反查;\n 8.3.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 8.3.7.1.服务项目:从该条合同自动反查;\n 8.3.7.2.费用:从该条合同自动反查;\n 8.3.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 8.3.8.服务费:从该条合同自动反查;\n 8.3.9.保证金:从该条合同自动反查;\n 8.3.10.备注:从该条合同自动反查;\n\n9.其他费用信息卡片:\n#显示对应租赁费用模板证照补办费用、违约金费用、易损件费用、其他费用等信息;\n9.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 9.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,从该条合同自动反查;\n 9.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n 9.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,从该条合同自动反查;\n 9.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n\n10.账单计算方式卡片:\n#显示实际结算方式;\n\n11.盖章合同附件:\n#显示财务审核完成后,由法务上传的盖章版合同文件,显示「盖章合同名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列;如果转为三方合同,则会重新审批,法务上传显示新附件,显示「三方盖章合同名称.格式」「文件大小」「上传时间」\n\n12.合同变更历史记录:\n#显示合同变更历史,包括序号、变更时间、操作类型、操作人、原始记录、备注;\n12.1.序号:显示变更历史序号,按照变更时间倒序(从近到远)进行排序;\n12.2.变更时间:显示变更时间,格式为:YYYY-MM-DD HH:MM;\n12.3.操作类型:包括变更为三方合同、添加车辆、添加授权人、续签合同、撤回合同、终止合同、转正式合同、保存;\n12.4.操作人:显示对应操作类型操作人员;\n12.5.原始记录:显示查看变更前记录,点击打开新页面至查看原始合同快照;\n12.6.备注:备注改动内容,包括:\n 12.6.1.变更为三方合同时:增加丙方客户“客户名称xxxxxx”;\n 12.6.2.添加车辆时:添加车辆“车牌号xxx”、“车牌号xxx”、“车牌号xxx”,每辆车单独一行显示;\n 12.6.3.添加授权人时:添加授权人“授权人xxxx”、“授权人xxx”,每个授权人单独一行显示;\n 12.6.4.续签合同时:续签自”原合同编码xxxxxx“;\n 12.6.5.撤回合同时:主动撤回;\n 12.6.6.终止合同时:主动终止;\n 12.6.7.转正式合同时:转正式合同自“原合同编码xxxx”;\n 12.6.8.保存:无;\n\n13.最下方为返回按钮,点击返回车辆租赁合同列表;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; + var requirementContent = '车辆租赁合同-查看(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「查看租赁合同」模块,在车辆租赁合同操作列点击「查看」进行查看;\n1.面包屑:\n#业务管理-车辆租赁合同-查看合同\n\n3.客户基本信息卡片:\n#显示已审批合同实际信息,不可修改,当客户信息产生变更时,对该条历史合同数据不作更新,合同变更为三方合同时不在原有客户基本信息做修改,而是增加三方客户基本信息卡片;\n3.1.客户名称:从该条合同自动反查;\n3.2.客户统一信用代码:从该条合同自动反查;\n3.3.客户地址:从该条合同自动反查;\n3.4.客户联系人:从该条合同自动反查;\n3.5.客户电话:从该条合同自动反查;\n3.6.客户电子邮箱:从该条合同自动反查;\n3.7.企业名称:从该条合同自动反查;\n3.8.企业电话:从该条合同自动反查;\n3.9.邮寄地址:从该条合同自动反查;\n3.10.开户银行:从该条合同自动反查;\n3.11.银行账号:从该条合同自动反查;\n3.12.纳税人识别号:从该条合同自动反查;\n3.13.业务部门:从该条合同自动反查;\n3.14.业务负责人:从该条合同自动反查;\n\n4.丙方客户基本信息卡片(当合同转三方合同时显示):\n#显示已审批合同丙方实际信息,不可修改;;\n4.1.客户名称:从该条合同自动反查;\n4.2.客户统一信用代码:从该条合同自动反查;\n4.3.客户地址:从该条合同自动反查;\n4.4.客户联系人:从该条合同自动反查;\n4.5.客户电话:从该条合同自动反查;\n4.6.客户电子邮箱:从该条合同自动反查;\n4.7.企业名称:从该条合同自动反查;\n4.8.企业电话:从该条合同自动反查;\n4.9.邮寄地址:从该条合同自动反查;\n4.10.开户银行:从该条合同自动反查;\n4.11.银行账号:从该条合同自动反查;\n4.12.纳税人识别号:从该条合同自动反查;\n4.13.业务部门:从该条合同自动反查;\n4.14.业务负责人:从该条合同自动反查;\n\n5.合同基本信息卡片:\n#显示合同基本信息,不可修改;\n5.1.项目名称:从该条合同自动反查;\n5.2.合同编码:从该条合同自动反查,在新增租赁合同点击提交审核时生成;\n 合同编码由:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]组成;\n 5.2.1.地区简写:如上海为SH,嘉兴为JX;\n 5.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;\n 5.2.3.签约时间:显示合同签约时间,如20260216\n 5.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;\n 5.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团编号为1235的合同;\n 5.2.6.签署状态:A为正式合同,B为试用合同;\n 如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中编号为1235,也可以理解为第1235份合同;\n 5.2.7.如果该合同为续签合同,则自动在新合同编码后额外添加:(续签自:旧合同合同编码xxx);\n 5.2.8.如果该合同为转正式合同,则自动在新合同编码后额外添加:(转正式合同自:旧合同合同编码xxx);\n5.3.合同类型:从该条合同自动反查;\n5.4.生效日期:从该条合同自动反查;\n5.5.付款方式:从该条合同自动反查;\n5.6.主要车型:从该条合同自动反查;\n5.7.结束日期:从该条合同自动反查;\n 5.7.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 5.7.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 5.7.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n5.8.付款周期:从该条合同自动反查;\n5.9.签约公司:从该条合同自动反查;\n5.10.交车区域:从该条合同自动反查;\n5.11.交车地点:从该条合同自动反查;\n5.12.合同原件:从该条合同自动反查,显示「合同原件名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列,如果转为三方合同,则会显示新附件,显示「三方合同原件名称.格式」「文件大小」「上传时间」;\n5.13.备注:从该条合同自动反查;\n 5.13.1.如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx;\n 5.13.2.如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx;\n\n6.被授权人信息卡片:\n#显示所有被授权人信息,不可修改,如要新增被授权人,需要在列表中点击添加被授权人进行处理;\n6.1.被授权人:从该条合同自动反查;\n6.2.被授权人联系电话:从该条合同自动反查;\n6.3.被授权人身份证:从该条合同自动反查;\n\n7.租赁订单信息卡片:\n#显示租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n7.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 7.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 7.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 7.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 7.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n7.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 7.2.1.序号:从该条合同自动反查;\n 7.2.2.品牌:从该条合同自动反查;\n 7.2.3.型号:从该条合同自动反查;\n 7.2.4.车牌号:从该条合同自动反查;\n 7.2.5.车辆状态:分为已交车、已交车-临时替换、已交车-永久替换、待交车;\n 7.2.6.车辆识别代码:从该条合同自动反查;\n 7.2.7.车辆月租金:从该条合同自动反查;\n 7.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 7.2.7.1.服务项目:从该条合同自动反查;\n 7.2.7.2.费用:从该条合同自动反查;\n 7.2.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 7.2.8.服务费:从该条合同自动反查;\n 7.2.9.保证金:从该条合同自动反查;\n 7.2.10.备注:从该条合同自动反查;\n7.3.氢费承担方:从该条合同自动反查;\n7.4.付款方式:从该条合同自动反查;\n7.5.氢气预付款:从该条合同自动反查;\n7.6.退还车氢气单价:从该条合同自动反查,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n8.新增车辆信息卡片(用户每次为合同新增车辆时,都会生成一个新增车辆信息卡片):\n#显示租赁合同对应新增车辆的明细费用、氢费明细费用等相关信息;\n8.1.卡片标题显示为:新增车辆信息(YYYY-MM-DD)\n8.2.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额等相关信息;\n 8.2.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 8.2.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 8.2.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n8.3.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 8.3.1.序号:从该条合同自动反查;\n 8.3.2.品牌:从该条合同自动反查;\n 8.3.3.型号:从该条合同自动反查;\n 8.3.4.车牌号:从该条合同自动反查;\n 8.3.5.车辆识别代码:从该条合同自动反查;\n 8.3.6.车辆月租金:从该条合同自动反查;\n 8.3.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 8.3.7.1.服务项目:从该条合同自动反查;\n 8.3.7.2.费用:从该条合同自动反查;\n 8.3.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 8.3.8.服务费:从该条合同自动反查;\n 8.3.9.保证金:从该条合同自动反查;\n 8.3.10.备注:从该条合同自动反查;\n\n9.其他费用信息卡片:\n#显示对应租赁费用模板证照补办费用、违约金费用、易损件费用、其他费用等信息;\n9.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 9.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,从该条合同自动反查;\n 9.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n 9.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,从该条合同自动反查;\n 9.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n\n10.账单计算方式卡片:\n#显示实际结算方式;\n\n11.盖章合同附件:\n#显示财务审核完成后,由法务上传的盖章版合同文件,显示「盖章合同名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列;如果转为三方合同,则会重新审批,法务上传显示新附件,显示「三方盖章合同名称.格式」「文件大小」「上传时间」\n\n12.合同变更历史记录:\n#显示合同变更历史,包括序号、变更时间、操作类型、操作人、原始记录、备注;\n12.1.序号:显示变更历史序号,按照变更时间倒序(从近到远)进行排序;\n12.2.变更时间:显示变更时间,格式为:YYYY-MM-DD HH:MM;\n12.3.操作类型:包括变更为三方合同、添加车辆、添加授权人、续签合同、撤回合同、终止合同、转正式合同、保存;\n12.4.操作人:显示对应操作类型操作人员;\n12.5.原始记录:显示查看变更前记录,点击打开新页面至查看原始合同快照;\n12.6.备注:备注改动内容,包括:\n 12.6.1.变更为三方合同时:增加丙方客户“客户名称xxxxxx”;\n 12.6.2.添加车辆时:添加车辆“车牌号xxx”、“车牌号xxx”、“车牌号xxx”,每辆车单独一行显示;\n 12.6.3.添加授权人时:添加授权人“授权人xxxx”、“授权人xxx”,每个授权人单独一行显示;\n 12.6.4.续签合同时:续签自”原合同编码xxxxxx“;\n 12.6.5.撤回合同时:主动撤回;\n 12.6.6.终止合同时:主动终止;\n 12.6.7.转正式合同时:转正式合同自“原合同编码xxxx”;\n 12.6.8.保存:无;\n\n2.审批状态:\n按照流程引擎设置节点显示;\n\n13.最下方为返回按钮,点击返回车辆租赁合同列表;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 720 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px', overflow: 'auto' }) }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementContent)), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; var servicePopoverRentalOpen = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow]; @@ -513,7 +528,6 @@ const Component = function() { return React.createElement('div', { style: styles.page }, servicePopoverContentCustom, React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '查看合同')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), - approvalCardEl, React.createElement('div', { style: styles.anchorWrap }, React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), @@ -537,6 +551,7 @@ const Component = function() { React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, attachmentContent), React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, changeHistoryContent), + approvalCardEl, React.createElement('div', { style: { height: 60 } }), reqSpecModalContent, React.createElement('div', { style: styles.footer }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault) }, '返回')) diff --git a/web端/运维管理/车辆业务/替换车管理-新增.jsx b/web端/运维管理/车辆业务/替换车管理-新增.jsx index a2296ea..869557a 100644 --- a/web端/运维管理/车辆业务/替换车管理-新增.jsx +++ b/web端/运维管理/车辆业务/替换车管理-新增.jsx @@ -14,6 +14,7 @@ const Component = function () { var Button = antd.Button; var Modal = antd.Modal; var message = antd.message; + var DatePicker = antd.DatePicker; // 模拟:进行中的车辆租赁合同列表(选项目后反写合同信息) var contractList = [ @@ -58,6 +59,7 @@ const Component = function () { contactPhone: '', businessDept: '', businessPerson: '', + replaceDate: null, replaceType: undefined, replaceReason: undefined, replaceReasonDesc: '', @@ -171,7 +173,7 @@ const Component = function () { var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 }; var formItemStyle = { marginBottom: 12 }; - var requirementContent = '替换车管理-新增(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「新增」模块\n\n1.面包屑:\n#运维管理-车辆业务-替换车管理-新增\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:选择器,可选择所有进行中的合同,支持输入框中输入关键词进行模糊搜索,下拉显示对应项;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换类型:选择器,分为「永久替换」「临时替换」两个选项;\n 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.2.替换原因:选择器,分为「客户原因」「车辆原因」;\n3.3.替换原因说明:文本域,默认提示信息为:请说明替换原因;\n3.4.被替换车牌号:选择器,只能选择该租赁合同当前对应的已交车但未还车车牌号;\n3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.8.替换车车牌号:选择器,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号;\n3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为提交审核、保存、取消按钮;\n4.1.点击提交并审核,toast提示:替换车申请已提交审核;\n4.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中(待审批),该条数据只能保存人自己查看并编辑,其他人无法操作;\n4.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回替换车管理列表页;'; + var requirementContent = '替换车管理-新增(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「新增」模块\n1.面包屑:\n#运维管理-车辆业务-替换车管理-新增\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:选择器,可选择所有进行中的合同,支持输入框中输入关键词进行模糊搜索,下拉显示对应项;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换时间:日期选择器,格式为:YYYY-MM-DD,精确至天;\n3.2.替换类型:选择器,分为「永久替换」「临时替换」两个选项;\n 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.3.替换原因:选择器,分为「客户原因」「车辆原因」;\n3.4.替换原因说明:文本域,默认提示信息为:请说明替换原因;\n3.5.被替换车牌号:选择器,只能选择该租赁合同当前对应的已交车但未还车车牌号,已被替换但替换车申请还在审核中时,该车辆不可选;\n3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.9.替换车车牌号:选择器,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号;\n3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为提交审核、保存、取消按钮;\n4.1.点击提交并审核,toast提示:替换车申请已提交审核;\n4.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中(待审批),该条数据只能保存人自己查看并编辑,其他人无法操作;\n4.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回替换车管理列表页;\n'; return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, @@ -233,6 +235,25 @@ const Component = function () { React.createElement(Card, { title: '替换车详情', style: cardStyle }, React.createElement('div', { style: formRowStyle }, React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '替换时间'), + React.createElement(DatePicker, { + placeholder: '请选择日期', + style: { width: '100%' }, + format: 'YYYY-MM-DD', + value: form.replaceDate, + onChange: function (d) { + setEdited(true); + setForm(function (p) { + var n = {}; + for (var k in p) n[k] = p[k]; + n.replaceDate = d; + return n; + }); + }, + allowClear: true + }) + ), + React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '替换类型'), React.createElement(Select, { placeholder: '请选择', diff --git a/web端/运维管理/车辆业务/替换车管理-查看.jsx b/web端/运维管理/车辆业务/替换车管理-查看.jsx index b8db0c2..e185b1f 100644 --- a/web端/运维管理/车辆业务/替换车管理-查看.jsx +++ b/web端/运维管理/车辆业务/替换车管理-查看.jsx @@ -10,6 +10,7 @@ const Component = function () { var Input = antd.Input; var Button = antd.Button; var Modal = antd.Modal; + var Steps = antd.Steps; var requirementModalVisible = useState(false); var setRequirementModalVisible = requirementModalVisible[1]; @@ -24,6 +25,7 @@ const Component = function () { contactPhone: '13800138001', businessDept: '业务1部', businessPerson: '张经理', + replaceDate: '2026-02-18', replaceType: '永久替换', replaceReason: '车辆原因', replaceReasonDesc: '原车故障需维修,临时用替换车保障客户用车。', @@ -53,35 +55,13 @@ const Component = function () { var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 }; var formItemStyle = { marginBottom: 12 }; - // 审批情况(参照车辆租赁合同-查看顶部审批状态) + // 审批情况(竖排步骤条,参照提车应收款-审核) var approvalSteps = [ - { title: '业务部主管', person: '姚守涛', status: '已通过', approveTime: '2026-02-20 09:30' }, - { title: '事业部主管', person: '尚建华', status: '已通过', approveTime: '2026-02-20 10:15' }, - { title: '运维主管', person: '王运维', status: '已通过', approveTime: '2026-02-20 11:00' } + { title: '业务部主管', person: '姚守涛', status: 'finish', approveTime: '2026-02-20 09:30' }, + { title: '事业部主管', person: '尚建华', status: 'finish', approveTime: '2026-02-20 10:15' }, + { title: '运维主管', person: '王运维', status: 'finish', approveTime: '2026-02-20 11:00' } ]; - var approvalCardStyle = { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }; - var approvalHeaderStyle = { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600, color: '#333', textAlign: 'center' }; - var approvalBodyStyle = { padding: '24px 20px', display: 'flex', justifyContent: 'center' }; - var stepWrapStyle = { display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', justifyContent: 'center' }; - var stepItemStyle = { flex: '1 1 0', minWidth: 140, maxWidth: 220, textAlign: 'center', position: 'relative' }; - var stepIconStyle = { width: 32, height: 32, borderRadius: '50%', margin: '0 auto 8px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, fontWeight: 600, backgroundColor: '#52c41a', color: '#fff' }; - var stepLineStyle = { position: 'absolute', top: 16, left: '50%', right: '-50%', height: 2, backgroundColor: '#52c41a', zIndex: 0 }; - var stepTitleStyle = { fontSize: 13, color: '#333', fontWeight: 500, marginBottom: 4 }; - var stepDescStyle = { fontSize: 12, color: '#666' }; - var stepStatusStyle = { fontSize: 12, color: '#52c41a', marginTop: 4 }; - var stepTimeStyle = { fontSize: 12, color: '#999', marginTop: 2 }; - var requirementContent = '替换车管理-查看(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「查看」模块\n1.面包屑:\n#运维管理-车辆业务-替换车管理-查看\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:根据已填信息反查,不可编辑;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换类型:选择器,根据已填信息反查,不可编辑;\n 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.2.替换原因:选择器,根据已填信息反查,不可编辑;\n3.3.替换原因说明:文本域,根据已填信息反查,不可编辑;\n3.4.被替换车牌号:选择器,根据已填信息反查,不可编辑;\n3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.8.替换车车牌号:选择器,根据已填信息反查,不可编辑;\n3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为返回按钮;\n4.1.点击返回,返回替换车管理列表页;'; - var approvalStepEls = approvalSteps.map(function (step, index) { - var isLast = index === approvalSteps.length - 1; - return React.createElement('div', { key: index, style: Object.assign({}, stepItemStyle, { zIndex: approvalSteps.length - index }) }, - !isLast ? React.createElement('div', { style: stepLineStyle }) : null, - React.createElement('div', { style: stepIconStyle }, '✓'), - React.createElement('div', { style: stepTitleStyle }, step.title), - React.createElement('div', { style: stepDescStyle }, step.person), - React.createElement('div', { style: stepStatusStyle }, step.status), - step.approveTime ? React.createElement('div', { style: stepTimeStyle }, step.approveTime) : null - ); - }); + var requirementContent = '替换车管理-查看(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「查看」模块\n1.面包屑:\n#运维管理-车辆业务-替换车管理-查看\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:根据已填信息反查,不可编辑;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换时间:日期选择器(禁用),显示退换时间,格式为YYYY-MM-DD;\n3.2.替换类型:选择器,根据已填信息反查,不可编辑;\n 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.3.替换原因:选择器,根据已填信息反查,不可编辑;\n3.4.替换原因说明:文本域,根据已填信息反查,不可编辑;\n3.5.被替换车牌号:选择器,根据已填信息反查,不可编辑;\n3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.9.替换车车牌号:选择器,根据已填信息反查,不可编辑;\n3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为返回按钮;\n4.1.点击返回,返回替换车管理列表页;'; return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, @@ -95,12 +75,6 @@ const Component = function () { }), React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { setRequirementModalVisible(true); } }, '查看需求说明') ), - React.createElement('div', { style: approvalCardStyle }, - React.createElement('div', { style: approvalHeaderStyle }, '审批情况'), - React.createElement('div', { style: approvalBodyStyle }, - React.createElement('div', { style: stepWrapStyle }, approvalStepEls) - ) - ), React.createElement(Card, { title: '选择项目', style: cardStyle }, React.createElement('div', { style: formRowStyle }, React.createElement('div', { style: formItemStyle }, @@ -139,6 +113,10 @@ const Component = function () { ), React.createElement(Card, { title: '替换车详情', style: cardStyle }, React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '替换时间'), + React.createElement(Input, { value: data.replaceDate || '', disabled: true, placeholder: 'YYYY-MM-DD' }) + ), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '替换类型'), React.createElement(Input, { value: data.replaceType || '', disabled: true }) @@ -189,6 +167,25 @@ const Component = function () { React.createElement(Button, { onClick: handleBack }, '返回') ) ), + React.createElement(Card, { title: '审批情况', style: cardStyle }, + React.createElement(Steps, { + direction: 'vertical', + current: approvalSteps.length, + items: approvalSteps.map(function (s) { + var statusText = s.status === 'finish' ? '审批通过' : '待审批'; + var desc = React.createElement('div', { style: { fontSize: 13, color: 'rgba(0,0,0,0.65)', marginTop: 4 } }, + React.createElement('div', null, '审批状态:', statusText), + React.createElement('div', null, '审批人:', s.person || '—'), + s.approveTime ? React.createElement('div', null, '审批时间:', s.approveTime) : null + ); + return { + title: s.title, + description: desc, + status: s.status === 'finish' ? 'finish' : 'wait' + }; + }) + }) + ), React.createElement(Modal, { title: '需求说明', open: requirementModalVisible[0], diff --git a/web端/运维管理/车辆业务/替换车管理-编辑.jsx b/web端/运维管理/车辆业务/替换车管理-编辑.jsx index 6174327..8140899 100644 --- a/web端/运维管理/车辆业务/替换车管理-编辑.jsx +++ b/web端/运维管理/车辆业务/替换车管理-编辑.jsx @@ -10,6 +10,7 @@ const Component = function () { var Input = antd.Input; var Button = antd.Button; var Select = antd.Select; + var DatePicker = antd.DatePicker; var Modal = antd.Modal; var Option = Select.Option; @@ -51,6 +52,7 @@ const Component = function () { contactPhone: '13800138001', businessDept: '业务1部', businessPerson: '张经理', + replaceDate: '2026-02-18', replaceType: '永久替换', replaceReason: '车辆原因', replaceReasonDesc: '原车故障需维修,临时用替换车保障客户用车。', @@ -162,7 +164,7 @@ const Component = function () { var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 }; var formItemStyle = { marginBottom: 12 }; - var requirementContent = '替换车管理-编辑(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「编辑」模块\n1.面包屑:\n#运维管理-车辆业务-替换车管理-编辑\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:选择器,反写已新增完成的内容,可编辑,可选择所有进行中的合同,支持输入框中输入关键词进行模糊搜索,下拉显示对应项;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换类型:选择器,反写已新增完成的内容,可编辑,分为「永久替换」「临时替换」两个选项;\n 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.2.替换原因:选择器,反写已新增完成的内容,可编辑,分为「客户原因」「车辆原因」;\n3.3.替换原因说明:文本域,反写已新增完成的内容,可编辑,默认提示信息为:请说明替换原因;\n3.4.被替换车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该租赁合同当前对应的已交车但未还车车牌号;\n3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.8.替换车车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号;\n3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为提交审核、保存、取消按钮;\n4.1.点击提交并审核,toast提示:替换车申请已提交审核;\n4.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中(待审批),该条数据只能保存人自己查看并编辑,其他人无法操作;\n4.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回替换车管理列表页;'; + var requirementContent = '替换车管理-编辑(2026年3月3日版本)\n一个「数字化资产ONEOS运管平台」中的「运维管理」「车辆业务」「替换车管理」「编辑」模块\n1.面包屑:\n#运维管理-车辆业务-替换车管理-编辑\n页面由选择项目、替换车详情、两个单独卡片组成;\n\n2.选择项目:\n#可通过选择进行中的车辆租赁合同,拉取租赁合同中对应车辆进行替换;\n2.1.项目名称:选择器,反写已新增完成的内容,可编辑,可选择所有进行中的合同,支持输入框中输入关键词进行模糊搜索,下拉显示对应项;\n2.2.合同编码:根据项目名称自动反查,不可编辑;\n2.3.客户名称:根据项目名称自动反查,不可编辑;\n2.4.对接人:根据项目名称自动反查,不可编辑;\n2.5.合同签订时间:根据项目名称自动反查,不可编辑;\n2.6.客户联系电话:根据项目名称自动反查,不可编辑;\n2.7.业务部门:根据项目名称自动反查,不可编辑;\n2.8.业务人员:根据项目名称自动反查,不可编辑;\n\n3.替换车详情:\n3.1.替换时间:日历选择器,精确至天,格式为:YYYY-MM-DD,反写新增时填写的日期;\n3.2.替换类型:选择器,反写已新增完成的内容,可编辑,分为「永久替换」「临时替换」两个选项;\n 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车;\n 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车;\n 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作;\n 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车;\n3.3.替换原因:选择器,反写已新增完成的内容,可编辑,分为「客户原因」「车辆原因」;\n3.4.替换原因说明:文本域,反写已新增完成的内容,可编辑,默认提示信息为:请说明替换原因;\n3.5.被替换车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该租赁合同当前对应的已交车但未还车车牌号;\n3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码;\n3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌;\n3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号;\n3.9.替换车车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号;\n3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码;\n3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌;\n3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号;\n\n下方为提交审核、保存、取消按钮;\n4.1.点击提交并审核,toast提示:替换车申请已提交审核;\n4.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中(待审批),该条数据只能保存人自己查看并编辑,其他人无法操作;\n4.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回替换车管理列表页;\n'; return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, @@ -222,6 +224,16 @@ const Component = function () { ), React.createElement(Card, { title: '替换车详情', style: cardStyle }, React.createElement('div', { style: formRowStyle }, + React.createElement('div', { style: formItemStyle }, + React.createElement('div', { style: labelStyle }, '替换时间'), + React.createElement(DatePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + value: data.replaceDate ? (typeof data.replaceDate === 'string' ? window.dayjs && window.dayjs(data.replaceDate) : data.replaceDate) : null, + onChange: function (date, dateStr) { updateDetail('replaceDate', dateStr || ''); }, + placeholder: '请选择日期' + }) + ), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '替换类型'), React.createElement(Select, { diff --git a/web端/需求说明/业务管理/租赁账单 b/web端/需求说明/业务管理/租赁账单 new file mode 100644 index 0000000..0bedf1f --- /dev/null +++ b/web端/需求说明/业务管理/租赁账单 @@ -0,0 +1,48 @@ +租赁账单(2026年3月10日版本) +一个「数字化资产ONEOS运管平台」中的「租赁账单」模块 +#面包屑:业务管理-租赁账单; +1.筛选: +#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式; +1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项; +1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项; +1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项; +1.4.业务部门:选择器,支持选择所有业务部门; +1.5.业务负责人:选择器,支持选择所有业务负责人; +1.6.交车任务编码:输入框,支持模糊搜索; +1.7.右侧为重置、查询按钮; + +2.租赁账单列表: +列表展示方式为:嵌套子表格,分为主表和子表 +2.1.主表:显示以下字段:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人; + 2.1.1.合同编码:显示车辆租赁合同编码; + 2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同; + 2.1.3.项目名称:显示该租赁合同对应项目名称; + 2.1.4.客户名称:显示该租赁合同对应客户名称; + 2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD; + 2.1.6.交车任务编码:显示交车任务编码; + 2.1.7.业务部门:显示该租赁合同对应业务部门名称; + 2.1.8.业务负责人:显示该租赁合同对应业务负责人; + 2.1.9.主表右下方为分页符,支持分页和选择单页显示数据条数; + +2.2.子表:显示以下字段:账单编号、账单期数、账单开始日期、账单结束日期、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、开票金额、操作; + 2.2.1.账单编号:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据; + 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推; + 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单; + 2.2.2.账单期数:显示该笔账单对应期数; + 2.2.3.处理状态:已提交、待提交、已结清; + 2.2.3.1.已提交:业务人员已通过点击收费明细,对收费项进行过维护并经过二次确认后提交; + 2.2.3.2.待提交:业务人员未完成收费明细填报; + 2.2.3.3.已结清:(等对接财务系统后,到账金额等于实收金额总额后算作已结清); + 2.2.3.账单开始日期:显示该笔账单开始日期,格式为:YYYY-MM-DD; + 2.2.4.账单结束日期:显示该笔账单结束日期,格式为:YYYY-MM-DD; + 2.2.5.提车数量:显示提车数量,格式为:xx辆,点击弹出气泡卡片,卡片内为列表,显示:品牌、型号、车牌号; + 2.2.6.应收款总额:显示该笔账单应收款总额,格式为:xx.xx元,计算方式:「所有车辆月租金总和」+「所有车辆服务费总和」; + 2.2.7.实收款总额:显示该笔账单实收款总额,格式为:xx.xx元,计算方式:「所有车辆月租金总和」+「所有车辆服务费总和」-「减免总金额」; + 2.2.8.减免总金额:显示该笔账单减免总金额,格式为:xx.xx元,计算方式:「所有减免金额总和」; + 2.2.9.车辆成本:根据型号成本表中对应型号车辆成本,租赁订单包含车辆型号*实际账单天数计算得出; + 2.2.10.氢费成本:显示格式为:xx.xx元,点击变为输入框,后缀为元,支持2位小数,失焦后保存; + 2.2.11.其他成本:显示格式为:xx.xx元,点击变为输入框,后缀为元,支持2位小数,失焦后保存; + + 2.2.12.操作:查看、收费明细; + 2.2.12.1.查看:点击跳转租赁账单-查看页; + 2.2.12.2.收费明细:提交后收费明细将不可修改; diff --git a/web端/需求说明/业务管理/租赁账单-收费明细 b/web端/需求说明/业务管理/租赁账单-收费明细 new file mode 100644 index 0000000..e06fbfb --- /dev/null +++ b/web端/需求说明/业务管理/租赁账单-收费明细 @@ -0,0 +1,51 @@ +租赁账单-收费明细(2026年3月11日版本) +一个「数字化资产ONEOS运管平台」中的「租赁账单」「收费明细」模块 +#面包屑:业务管理-租赁账单-收费明细; +1.账单信息: +#显示该账单对应合同及账单信息,显示以下字段: + 1.1.合同编码:显示该账单对应合同编码; + 1.2.合同类型:显示该账单对应合同类型,包括正式合同、试用合同; + 1.3.项目名称:显示该账单对应合同中项目名称; + 1.4.客户名称:显示该账单对应合同中客户名称; + 1.5.交车任务编码:显示交车任务对应编码; + 1.6.账单编码:显示该账单编码,账单编码规则为:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据; + 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推; + 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单;; + 1.7.账单期数:显示该账单期数; + 1.8.账单开始日期:显示账单开始日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD; + 1.9.账单结束日期:显示账单结束日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD; +2.账单明细: +#顶部显示应收款总额、实收款总额; + 2.1.应收款总额:「应收车辆月租金总和」+「应收车辆保证金总和」+「应收服务费总和」+「氢费预付款应收金额」 + 2.2.实收款总额:「实收车辆月租金总和」+「应收车辆保证金总和」+「实收服务费总和」+「氢费预付款实收金额」-「减免费用总和」「服务费各项减免费用总和」 + 2.3.开票金额:「实收车辆月租金总和」+「实收服务费总和」-「减免费用总和」「服务费各项减免费用总和」 + +#显示该账单对应车辆列表及相关费用信息,显示以下字段; + 2.3.序号:与租赁合同及交车任务中该车辆序号一致; + 2.4.品牌:与租赁合同及交车任务中该车辆品牌一致; + 2.5.型号:与租赁合同及交车任务中该车辆型号一致; + 2.6.车牌号:与租赁合同及交车任务中该车辆车牌号一致; + 2.7.应收车辆月租金:与租赁合同中该车辆车辆月租金一致,格式为:xx.xx元; + 2.8.实收车辆月租金:输入框,2位小数,后缀为元,默认为与应收车辆月租金一致; + 2.9.车辆租金备注:输入框,支持自定义输入; + 2.10.减免金额:输入框,2位小数,后缀为元,默认为0.00元; + 2.11.减免金额备注:输入框,支持自定义输入; + 2.12.减免证明:附件上传按钮,点击支持多文件上传,已上传文件一行显示一条,支持点击预览,格式支持各类图片、doc、docx、pdf等格式; + 2.13.应收车辆保证金:首期与合同中车辆保证金一致,第二期开始为0.00,格式为:xx.xx元; + 2.14.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注; + 2.14.1.服务项目:显示租赁合同中所有服务项目名称; + 2.14.2.应收费用:显示租赁合同中所有服务项目对应费用,格式为:xx.xx元; + 2.14.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元,默认与应收费用一致; + 2.14.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元; + 2.14.4.备注:选填项,输入框,由业务员自行输入备注信息; + 2.15.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元; + 2.16.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元; +#列表下方为氢费预付款情况,首期账单显示,第二期账单开始无此区域内容显示; + 2.17.氢费预付款应收金额:显示合同中氢费预付款金额; + 2.18.氢费预付款实收金额:输入框,2位小数,后缀为元,默认与氢费预付款应收金额一致; + 2.19.减免金额:输入框,2位小数,后缀为元; + 2.20.减免金额备注:输入框,支持自定义输入; + +3.下方为提交审核、保存、取消按钮 + 3.1.提交审核:点击提交审核按钮,进入审核流程; + 3.2.保存:点击保存按钮,保存当前表单已填数据,不做校验; diff --git a/web端/需求说明/业务管理/租赁账单-查看 b/web端/需求说明/业务管理/租赁账单-查看 new file mode 100644 index 0000000..a8761bc --- /dev/null +++ b/web端/需求说明/业务管理/租赁账单-查看 @@ -0,0 +1,50 @@ +租赁账单-查看(2026年3月11日版本) +一个「数字化资产ONEOS运管平台」中的「租赁账单」「查看」模块 +#面包屑:业务管理-租赁账单-收费明细; +1.账单信息: +#显示该账单对应合同及账单信息,显示以下字段: + 1.1.合同编码:显示该账单对应合同编码; + 1.2.合同类型:显示该账单对应合同类型,包括正式合同、试用合同; + 1.3.项目名称:显示该账单对应合同中项目名称; + 1.4.客户名称:显示该账单对应合同中客户名称; + 1.5.交车任务编码:显示交车任务对应编码; + 1.6.账单编码:显示该账单编码,账单编码规则为:[合同编码][账单编号]组成,主要用于后期与用友YS系统打通时获取财务收款及发票相关数据; + 前缀为合同编码,后缀为账单编号,规则为:ZD+4位编号,为该合同下第x份账单,例如:ZD0001为该合同下第1份账单,依次类推; + 例如:JXZL20260216YW101235AZD0001即为JXZL20260216YW101235A合同下第1份账单; + 1.7.账单期数:显示该账单期数; + 1.8.账单开始日期:显示账单开始日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD; + 1.9.账单结束日期:显示账单结束日期,根据租赁合同及交车任务开始计费日期按规则生成,格式为:YYYY-MM-DD; +2.账单明细: +#顶部显示应收款总额、实收款总额; + 2.1.应收款总额:「应收车辆月租金总和」+「应收车辆保证金总和」+「应收服务费总和」+「氢费预付款应收金额」 + 2.2.实收款总额:「实收车辆月租金总和」+「应收车辆保证金总和」+「实收服务费总和」+「氢费预付款实收金额」-「减免费用总和」「服务费各项减免费用总和」 + 2.3.开票金额:实收车辆月租金总和」+「实收服务费总和」-「减免费用总和」「服务费各项减免费用总和」 + +#显示该账单对应车辆列表及相关费用信息,显示以下字段; + 2.3.序号:与租赁合同及交车任务中该车辆序号一致; + 2.4.品牌:与租赁合同及交车任务中该车辆品牌一致; + 2.5.型号:与租赁合同及交车任务中该车辆型号一致; + 2.6.车牌号:与租赁合同及交车任务中该车辆车牌号一致; + 2.7.应收车辆月租金:与租赁合同中该车辆车辆月租金一致,格式为:xx.xx元; + 2.8.实收车辆月租金:与收费明细中实收车辆月租金一致,格式为:xx.xx元; + 2.9.车辆租金备注:显示收费明细中已填写备注信息; + 2.10.减免金额:显示收费明细中已填写减免金额; + 2.11.减免金额备注:显示收费明细中减免金额备注信息; + 2.12.减免证明:显示已上传减免证明文件,已上传文件一行显示一条,支持点击预览,格式支持各类图片、doc、docx、pdf等格式; + 2.13.应收车辆保证金:首期与合同中车辆保证金一致,第二期开始为0.00,格式为:xx.xx元; + 2.14.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注; + 2.14.1.服务项目:显示租赁合同中所有服务项目名称; + 2.14.2.应收费用:显示租赁合同中所有服务项目对应费用,格式为:xx.xx元; + 2.14.3.实收费用:显示收费明细中填写的实收费用金额,精确至2位小数,后缀为元,默认与应收费用一致; + 2.14.4.减免费用:显示收费明细中填写的减免费用金额,精确至2位小数,后缀为元; + 2.14.4.备注:显示收费明细中填写的备注信息; + 2.15.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元; + 2.16.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元; +#列表下方为氢费预付款情况,首期账单显示,第二期账单开始无此区域内容显示; + 2.17.氢费预付款应收金额:显示合同中氢费预付款金额; + 2.18.氢费预付款实收金额:显示收费明细中填写的氢费预付款实收金额,格式为:xx.xx元; + 2.19.减免金额:显示收费明细中填写的减免金额,格式为:xx.xx元; + 2.20.减免金额备注:显示收费明细中填写的减免金额备注信息; + +3.下方为返回按钮 + 3.1.返回:点击返回按钮,返回租赁账单列表页面; diff --git a/web端/需求说明/交车任务 b/web端/需求说明/交车任务 index 405c524..729daf6 100644 --- a/web端/需求说明/交车任务 +++ b/web端/需求说明/交车任务 @@ -4,51 +4,42 @@ 1.1.业务管理-交车任务 2.筛选: -支持通过合同编码、项目名称、客户名称、预计交车日期、创建人进行筛选,右侧为查询、重置按钮; -2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果; -2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果; -2.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果; -2.4.预计交车日期:日期选择器,默认提示信息为:请选择预计交车开始时间 请选择预计交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD; -2.5.创建人:选择器,默认为所有创建人;提示信息为:请输入或选择创建人,支持从输入框输入内容进行模糊搜索,下拉显示结果; -2.6.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询; -2.7.重置:点击清空查询条件至默认 +支持通过合同编码、项目名称、客户名称进行筛选,右侧为查询、重置按钮; +2.1.交车任务编码:选择器,默认所有交车任务编码,提示信息为:请输入或选择交车任务编码,支持从输入框输入内容进行模糊搜索,下拉显示结果; + 编码规则为:[合同编码][交车任务编码],主要用于后期与用友YS系统打通时获取财务收款及发票相关数据; + 前缀为合同编码,后缀为交车任务编码,规则为:JC+4位编号,为该合同下第x份交车任务,例如:JC0001为该合同下第1份交车任务,依次类推; + 例如:JXZL20260216YW101235AJC0001即为JXZL20260216YW101235A合同下第1份账单; +2.2.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果; +2.3.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果; +2.4.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果; +2.5.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询; +2.6.重置:点击清空查询条件至默认; -3.列表:分为2个tab:进行中、已完成,右侧为新增; -3.1进行中:显示所有已新增成功,但未完成交车单提交的任务;列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作; +3.列表: +#嵌套子表格,分为主表和子表,右侧为新增按钮; +3.1.主表展示以下内容:合同编码、项目名称、客户名称、业务部门、业务负责人、合同生效日期、合同结束日期; 3.1.1合同编码:显示对应合同编码; 3.1.2.项目名称:显示对应项目名称; 3.1.3.客户名称:显示对应客户名称; -3.1.4.交车数量:显示交车数; -3.1.5.交车区域:显示交车省-市; -3.1.6.交车地点:显示交车详细地址; -3.1.7.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD; -3.1.8.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效; -3.1.9.创建人:显示交车任务的创建人用户姓名; -3.1.10.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD; -3.1.11.最后更新人:显示交车任务的最后一次更新人用户姓名; -3.1.12.最后更新时间:显示交车任务的最后一次更新时间,格式为YYYY-MM-DD; -3.1.13.操作:支持查看、编辑、停用/启用; - 3.1.13.1.查看:跳转查看交车任务; - 3.1.13.2.编辑:跳转编辑交车任务; - 3.1.13.3.停用/启用:任务创建后为启用状态,此处为停用,点击停用后,变为启用。停用状态的交车任务不会推送给对应区域运维人员,重新启用时判断预计交车结束日期是否已经过期,如已过期则启用时需要在弹出卡片中重新选择预计交车日期; +3.1.4.业务部门:显示业务部门名称; +3.1.5.业务负责人:显示业务负责人名称; +3.1.6.合同生效日期:显示合同生效日期,格式为:YYYY-MM-DD; +3.1.7.合同结束日期:显示合同结束日期,格式为:YYYY-MM-DD; -3.2.已完成:显示所有已新增成功并完成交车单提交的任务,列表展示以下内容:列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作; -3.2.1.合同编码:显示对应合同编码; -3.2.2.项目名称:显示对应项目名称; -3.2.3.客户名称:显示对应客户名称; -3.2.4.交车数量:显示交车数,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、实际交车日期、交车人; - 3.2.4.1.车辆类型:显示车辆类型; - 3.2.4.2.品牌:显示车辆品牌; - 3.2.4.3.型号:显示车辆型号; - 3.2.4.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-; - 3.2.4.5.实际交车日期:显示实际交车日期,与列表中显示相同,格式为YYYY-MM-DD,如该车还未交车则显示为-; - 3.2.4.6.交车人:显示实际交车人用户姓名,如该车还未还车则显示为-; -3.2.5.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD; -3.2.6.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效; +3.2.子表展示以下内容:交车任务编码、预计交车日期、交车数量、开始计费日期、是否需要退还车、创建人、创建时间; +3.2.1.交车任务编码:编码规则为:[合同编码][交车任务编码],主要用于后期与用友YS系统打通时获取财务收款及发票相关数据; + 前缀为合同编码,后缀为交车任务编码,规则为:JC+4位编号,为该合同下第x份交车任务,例如:JC0001为该合同下第1份交车任务,依次类推; + 例如:JXZL20260216YW101235AJC0001即为JXZL20260216YW101235A合同下第1份账单; +3.2.2.任务状态:显示挂起/激活,挂起的数据操作列对应激活、激活的数据操作列对应挂起; +3.2.3.交车状态:显示交车任务状态,分为:已交车、未交车; +3.2.4.预计交车日期:显示该交车任务预计交车日期,可能是某天,也可能是开始-结束时间段,格式分别为:YYYY-MM-DD、YYYY-MM-DD至YYYY-MM-DD/; +3.2.5.交车数量:显示实际交车数量,格式为:xx辆,点击数量弹出气泡卡片,列表显示:品牌、型号、车牌号、实际交车日期、交车人,其中实际交车日期和交车人没交车时显示为-; +3.2.6.开始计费日期:显示该交车任务开始计费日期,格式为:YYYY-MM-DD; 3.2.7.创建人:显示交车任务的创建人用户姓名; 3.2.8.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD; -3.2.9.最后更新人:显示交车任务的最后一次更新人用户姓名; -3.2.10.最后更新时间:显示交车任务的最后一次更新时间,格式为YYYY-MM-DD; -3.2.11.操作:支持查看; +3.2.9.操作:支持查看、编辑、挂起/激活; + 3.2.9.1.查看:跳转交车任务-查看页; + 3.2.9.2.编辑:跳转交车任务-编辑页,已交车的任务不支持编辑; + 3.2.9.3.挂起/激活:点击挂起,进行二次确认提示,提示文案:挂起交车任务会释放清空该交车任务车牌号以释放车辆,是否确定?;点击激活,弹框中重新设置预计交车日期、开始计费日期,设置成功后提示交车任务激活成功; 4.列表右下方为分页功能,支持单页显示条目选择; \ No newline at end of file diff --git a/web端/需求说明/交车任务-编辑 b/web端/需求说明/交车任务-编辑 new file mode 100644 index 0000000..2c7ac0d --- /dev/null +++ b/web端/需求说明/交车任务-编辑 @@ -0,0 +1,28 @@ +编辑交车任务 +一个「数字化资产ONEOS运管平台」中的「交车任务」「编辑」模块 +1.面包屑: +1.1.业务管理-交车任务-编辑 + +2.表单: +2.1.选择项目名称:必选项,反写新增时写入的数据,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;; +2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码; +2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称; +2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务; +2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点; +2.6.预计交车日期:必填项,反写新增时写入的数据,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期; + 2.6.1.如勾选不需要还车,则预计交车日期隐藏,不需要设置; +2.7.开始计费日期:必填项,反写新增时写入的数据,日期选择器,支持单日选择,格式为YYYY-MM-DD; +2.8.已交车辆是否需要还车:勾选框,反写新增时写入的数据,内容为不需要还车,默认不勾选,不勾选时,无法在车辆列表勾选已交未还的车辆,勾选后,已交未还的车辆也可以勾选,但不会生成新交车任务,只是按照开始计费日期生成账单; + 2.8.1.勾选时,车辆列表只显示已交未还的车辆清单,不能添加新车(避免已交未还部分车辆开始计费日期计算不精确); + 2.8.2.不勾选时,车辆列表显示已交未还的车辆清单及续签合同新增车辆,但是已交未还部分的车辆不可勾选; +2.9.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注; + 2.9.1.全选/多选:支持全选、多选模式,反写新增时写入的数据,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务和已交车的车辆不可选,多选框为禁用状态,悬浮时提示:该车辆已完成交车/该车辆已创建交车任务; + 2.9.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌; + 2.9.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号; + 2.9.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-; + 2.9.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码; + 2.9.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元; + 2.9.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元; + 2.9.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元; + 2.9.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-; +2.10.页面底部为提交、取消; \ No newline at end of file diff --git a/web端/需求说明/新增交车任务 b/web端/需求说明/新增交车任务 index 48d2141..c25be55 100644 --- a/web端/需求说明/新增交车任务 +++ b/web端/需求说明/新增交车任务 @@ -10,15 +10,19 @@ 2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务; 2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点; 2.6.预计交车日期:必填项,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期; + 2.6.1.如勾选不需要还车,则预计交车日期隐藏,不需要设置; 2.7.开始计费日期:必填项,日期选择器,支持单日选择,格式为YYYY-MM-DD; -2.8.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注; - 2.8.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中; - 2.8.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌; - 2.8.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号; - 2.8.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-; - 2.8.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码; - 2.8.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元; - 2.8.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元; - 2.8.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元; - 2.8.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-; -2.9.页面底部为提交、取消; \ No newline at end of file +2.8.已交车辆是否需要还车:勾选框,内容为不需要还车,默认不勾选,不勾选时,无法在车辆列表勾选已交未还的车辆,勾选后,已交未还的车辆也可以勾选,但不会生成新交车任务,只是按照开始计费日期生成账单; + 2.8.1.勾选时,车辆列表只显示已交未还的车辆清单,不能添加新车(避免已交未还部分车辆开始计费日期计算不精确); + 2.8.2.不勾选时,车辆列表显示已交未还的车辆清单及续签合同新增车辆,但是已交未还部分的车辆不可勾选; +2.9.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注; + 2.9.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务和已交车的车辆不可选,多选框为禁用状态,悬浮时提示:该车辆已完成交车/该车辆已创建交车任务; + 2.9.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌; + 2.9.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号; + 2.9.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-; + 2.9.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码; + 2.9.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元; + 2.9.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元; + 2.9.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元; + 2.9.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-; +2.10.页面底部为提交、取消; \ No newline at end of file diff --git a/web端/需求说明/租赁合同/车辆租赁合同-查看 b/web端/需求说明/租赁合同/车辆租赁合同-查看 index f37feb6..20b2dc1 100644 --- a/web端/需求说明/租赁合同/车辆租赁合同-查看 +++ b/web端/需求说明/租赁合同/车辆租赁合同-查看 @@ -3,14 +3,6 @@ 1.面包屑: #业务管理-车辆租赁合同-查看合同 -2.审批状态: -#显示审批工作流顺序、审批部门、审批人员、审批状态、审批时间; -2.1.工作流顺序:以横向步骤条方式从左到右显示,已审批节点和未审批节点样式有区分; -2.2.审批部门:显示对应审批部门名称; -2.3.审批人员:显示对应审批人员姓名,多个人员以/分隔; -2.4.审批状态:显示审批状态,分为:已通过、待审批; -2.5.审批时间:显示审批操作时间,格式为:YYYY-MM-DD HH:MM,待审批时显示为:-; - 3.客户基本信息卡片: #显示已审批合同实际信息,不可修改,当客户信息产生变更时,对该条历史合同数据不作更新,合同变更为三方合同时不在原有客户基本信息做修改,而是增加三方客户基本信息卡片; 3.1.客户名称:从该条合同自动反查; @@ -94,8 +86,9 @@ 7.2.2.品牌:从该条合同自动反查; 7.2.3.型号:从该条合同自动反查; 7.2.4.车牌号:从该条合同自动反查; - 7.2.5.车辆识别代码:从该条合同自动反查; - 7.2.6.车辆月租金:从该条合同自动反查; + 7.2.5.车辆状态:分为已交车、已交车-临时替换、已交车-永久替换、待交车; + 7.2.6.车辆识别代码:从该条合同自动反查; + 7.2.7.车辆月租金:从该条合同自动反查; 7.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间; 7.2.7.1.服务项目:从该条合同自动反查; 7.2.7.2.费用:从该条合同自动反查; @@ -165,6 +158,9 @@ 12.6.7.转正式合同时:转正式合同自“原合同编码xxxx”; 12.6.8.保存:无; +2.审批状态: +按照流程引擎设置节点显示; + 13.最下方为返回按钮,点击返回车辆租赁合同列表; 所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域; \ No newline at end of file diff --git a/web端/需求说明/财务管理/提车应收款-收款 b/web端/需求说明/财务管理/提车应收款-提车收款单 similarity index 93% rename from web端/需求说明/财务管理/提车应收款-收款 rename to web端/需求说明/财务管理/提车应收款-提车收款单 index 2311305..ba7973d 100644 --- a/web端/需求说明/财务管理/提车应收款-收款 +++ b/web端/需求说明/财务管理/提车应收款-提车收款单 @@ -18,19 +18,20 @@ 2.提车应收款信息: #上方显示应收款总额、实收款总额; -2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」; +2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」+「氢费预充值应收金额」; 点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额; 2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项; 2.1.2.金额:显示该项目对应应收金额; -2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」; +2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」+「氢费预充值实收金额」-「选中车辆减免金额总和」; 点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额; 2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项; 2.2.2.金额:显示该项目对应实收金额; +2.3.应开票总额:格式为:xx.xx元,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」; #中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段: 2.1.全选/多选:多选按钮组,支持表头点击全选,默认为取消勾选(整个车辆清单); 选择代表本次提车应收款只收取该部分车辆费用,可以编辑以下相应字段,不选择则代表本次提车应收款不收取该部分车辆费用,不能编辑车辆相应字段; - 提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款; + 提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款/该车辆已提交提车收款单; 2.2.序号:根据租赁合同中车辆对应序号顺序展示; 2.3.品牌:显示租赁合同中车辆对应品牌; 2.4.型号:显示租赁合同中车辆对应型号; diff --git a/web端/需求说明/运维管理-车务管理/替换车管理-新增 b/web端/需求说明/运维管理-车务管理/替换车管理-新增 index 4b61326..0277810 100644 --- a/web端/需求说明/运维管理-车务管理/替换车管理-新增 +++ b/web端/需求说明/运维管理-车务管理/替换车管理-新增 @@ -16,21 +16,22 @@ 2.8.业务人员:根据项目名称自动反查,不可编辑; 3.替换车详情: -3.1.替换类型:选择器,分为「永久替换」「临时替换」两个选项; - 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; - 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; +3.1.替换时间:日期选择器,格式为:YYYY-MM-DD,精确至天; +3.2.替换类型:选择器,分为「永久替换」「临时替换」两个选项; + 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; + 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作; 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车; -3.2.替换原因:选择器,分为「客户原因」「车辆原因」; -3.3.替换原因说明:文本域,默认提示信息为:请说明替换原因; -3.4.被替换车牌号:选择器,只能选择该租赁合同当前对应的已交车但未还车车牌号; -3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; -3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; -3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; -3.8.替换车车牌号:选择器,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号; -3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; -3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; -3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; +3.3.替换原因:选择器,分为「客户原因」「车辆原因」; +3.4.替换原因说明:文本域,默认提示信息为:请说明替换原因; +3.5.被替换车牌号:选择器,只能选择该租赁合同当前对应的已交车但未还车车牌号,已被替换但替换车申请还在审核中时,该车辆不可选; +3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; +3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; +3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; +3.9.替换车车牌号:选择器,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号; +3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; +3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; +3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; 下方为提交审核、保存、取消按钮; 4.1.点击提交并审核,toast提示:替换车申请已提交审核; diff --git a/web端/需求说明/运维管理-车务管理/替换车管理-查看 b/web端/需求说明/运维管理-车务管理/替换车管理-查看 index 747cca8..bb92994 100644 --- a/web端/需求说明/运维管理-车务管理/替换车管理-查看 +++ b/web端/需求说明/运维管理-车务管理/替换车管理-查看 @@ -16,21 +16,22 @@ 2.8.业务人员:根据项目名称自动反查,不可编辑; 3.替换车详情: -3.1.替换类型:选择器,根据已填信息反查,不可编辑; - 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; - 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; +3.1.替换时间:日期选择器(禁用),显示退换时间,格式为YYYY-MM-DD; +3.2.替换类型:选择器,根据已填信息反查,不可编辑; + 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; + 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作; 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车; -3.2.替换原因:选择器,根据已填信息反查,不可编辑; -3.3.替换原因说明:文本域,根据已填信息反查,不可编辑; -3.4.被替换车牌号:选择器,根据已填信息反查,不可编辑; -3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; -3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; -3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; -3.8.替换车车牌号:选择器,根据已填信息反查,不可编辑; -3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; -3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; -3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; +3.3.替换原因:选择器,根据已填信息反查,不可编辑; +3.4.替换原因说明:文本域,根据已填信息反查,不可编辑; +3.5.被替换车牌号:选择器,根据已填信息反查,不可编辑; +3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; +3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; +3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; +3.9.替换车车牌号:选择器,根据已填信息反查,不可编辑; +3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; +3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; +3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; 下方为返回按钮; 4.1.点击返回,返回替换车管理列表页; diff --git a/web端/需求说明/运维管理-车务管理/替换车管理-编辑 b/web端/需求说明/运维管理-车务管理/替换车管理-编辑 index 2bf8251..4f783c5 100644 --- a/web端/需求说明/运维管理-车务管理/替换车管理-编辑 +++ b/web端/需求说明/运维管理-车务管理/替换车管理-编辑 @@ -16,21 +16,22 @@ 2.8.业务人员:根据项目名称自动反查,不可编辑; 3.替换车详情: -3.1.替换类型:选择器,反写已新增完成的内容,可编辑,分为「永久替换」「临时替换」两个选项; - 3.1.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; - 3.1.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; +3.1.替换时间:日历选择器,精确至天,格式为:YYYY-MM-DD,反写新增时填写的日期; +3.2.替换类型:选择器,反写已新增完成的内容,可编辑,分为「永久替换」「临时替换」两个选项; + 3.2.1.类型为永久替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),由运维手动将被替换车进行还车; + 3.2.2.类型为临时替换时,该申请通过审核后替换车进行交车(交车时间为流程结束当天),被替换车不用还车,在被替换车重新交付客户时,由运维手动将替换车进行还车; 重新生成交车任务时,交车地点会自动继承自合同,由对应区域运维人员才能操作; 交车任务完成后,所有涉及到被替换车辆显示(例如车辆租赁合同、租赁账单、提车应收款等功能)会替换为新替换车的对应信息,如果是临时替换,在新替换车完成还车后,对应车辆记录会恢复为原有车辆数据。如果是永久替换,则由运维自主进行被替换车辆还车; -3.2.替换原因:选择器,反写已新增完成的内容,可编辑,分为「客户原因」「车辆原因」; -3.3.替换原因说明:文本域,反写已新增完成的内容,可编辑,默认提示信息为:请说明替换原因; -3.4.被替换车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该租赁合同当前对应的已交车但未还车车牌号; -3.5.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; -3.6.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; -3.7.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; -3.8.替换车车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号; -3.9.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; -3.10.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; -3.11.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; +3.3.替换原因:选择器,反写已新增完成的内容,可编辑,分为「客户原因」「车辆原因」; +3.4.替换原因说明:文本域,反写已新增完成的内容,可编辑,默认提示信息为:请说明替换原因; +3.5.被替换车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该租赁合同当前对应的已交车但未还车车牌号; +3.6.被替换车识别代码:输入框(禁用),选择被替换车车牌号后自动反写该车识别代码; +3.7.被替换车品牌:输入框(禁用),选择被替换车车牌号后自动反写该车品牌; +3.8.被替换车型号:输入框(禁用),选择被替换车车牌号后自动反写该车型号; +3.9.替换车车牌号:选择器,反写已新增完成的内容,可编辑,只能选择该人员权限下所有已备车车辆,在选择项目名称前,不能选择替换车车牌号; +3.10.替换车识别代码:输入框(禁用),选择替换车车牌号后自动反写该车识别代码; +3.11.替换车品牌:输入框(禁用),选择替换车车牌号后自动反写该车品牌; +3.12.替换车型号:输入框(禁用),选择替换车车牌号后自动反写该车型号; 下方为提交审核、保存、取消按钮; 4.1.点击提交并审核,toast提示:替换车申请已提交审核;