web端:财务管理提车应收款-收款、车辆租赁合同与替换车管理改动同步

- 财务管理:新增提车应收款-收款.jsx,移除旧提车应收款/收费明细/首付款页面;项目信息与开票信息改为直接展示
- 车辆租赁合同:列表租赁车辆数/已交车辆数及气泡列调整;续签/转正式合同去除合同编码,交车区域·交车地点·合同原件布局调整
- 运维-车辆业务:新增替换车管理、替换车管理-新增/查看/编辑

Made-with: Cursor
This commit is contained in:
王冕
2026-03-04 20:38:06 +08:00
parent 3be6b02e9a
commit e70342e8fe
13 changed files with 2209 additions and 1883 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -394,6 +394,18 @@ const Component = function() {
next.splice(index, 1);
setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]);
};
var copyRentalRow = function(index) {
setEdited(true);
var row = rentalOrders[index];
if (!row) return;
var serviceItemsCopy = (row.serviceItems || []).map(function(si) { return { project: si.project || '', fee: si.fee || '', effectiveDate: si.effectiveDate || '' }; });
if (serviceItemsCopy.length === 0) serviceItemsCopy = [{ project: '', fee: '', effectiveDate: '' }];
var newRow = { brand: row.brand || '', model: row.model || '', plateNo: '', vin: '', monthRent: row.monthRent || '', serviceItems: serviceItemsCopy, deposit: row.deposit || '', remark: row.remark || '' };
var next = rentalOrders.slice(0);
next.splice(index + 1, 0, newRow);
setRentalOrders(next);
if (typeof message !== 'undefined' && message.success) message.success('已复制该行(车牌号已清空)');
};
var updateRentalOrder = function(index, field, value) {
setEdited(true);
var next = rentalOrders.slice(0);
@@ -578,7 +590,6 @@ const Component = function() {
var contractFormRow1 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setEdited(true); setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, style: { width: '100%' } })),
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCodeDisplay, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '合同类型', required: true, error: formErrors.contractType }, React.createElement(Select, { placeholder: '请选择合同类型', style: { width: '100%' }, value: contractType || undefined, onChange: function(v) { setEdited(true); setContractType(v || ''); }, status: formErrors.contractType ? 'error' : undefined }, React.createElement(Option, { value: '正式合同' }, '正式合同'), React.createElement(Option, { value: '试用合同' }, '试用合同'))),
React.createElement(FormItem, { label: '生效日期', required: true, error: formErrors.effectiveDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效日期', value: effectiveDate && window.moment ? window.moment(effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEdited(true); setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })),
React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setEdited(true); setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))),
@@ -586,35 +597,37 @@ const Component = function() {
React.createElement(FormItem, { label: '结束日期', required: true, error: formErrors.endDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择结束日期', value: endDate && window.moment ? window.moment(endDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEdited(true); setEndDate(dateStr || ''); }, status: formErrors.endDate ? 'error' : undefined })),
React.createElement(FormItem, { label: '付款周期', required: true, error: formErrors.paymentPeriod }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: paymentPeriod || undefined, onChange: function(v) { setEdited(true); setPaymentPeriod(v || ''); }, status: formErrors.paymentPeriod ? 'error' : undefined }, [1,2,3,4,5,6,7,8,9,10,11,12].map(function(n) { return React.createElement(Option, { key: n, value: String(n) }, n + '个月'); }))),
React.createElement(FormItem, { label: '签约公司', required: true, error: formErrors.signingCompany }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: signingCompany || undefined, onChange: function(v) { setEdited(true); setSigningCompany(v || ''); }, status: formErrors.signingCompany ? 'error' : undefined }, orgList.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))),
React.createElement('div', { style: styles.formCol },
React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null)),
React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal },
React.createElement('div', null,
React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '支持多个附件上传doc/docx/pdf'),
contractOriginalFiles && contractOriginalFiles.length
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 12 } },
contractOriginalFiles.map(function(f, fi) {
return React.createElement('div', { key: fi, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', padding: '8px 12px', border: '1px solid #f0f0f0', borderRadius: 6, backgroundColor: '#fafafa' } },
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 2 } },
React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginalFile(fi); } }, f.name),
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size ? f.size : '-') + ' · ' + (f.uploadTime ? f.uploadTime : '-'))
),
React.createElement(Button, { type: 'link', danger: true, onClick: function() { removeContractOriginalFile(fi); } }, '删除')
);
})
)
: null,
React.createElement('input', { ref: contractOriginalRef, type: 'file', multiple: true, accept: '.doc,.docx,.pdf', style: { display: 'none' }, onChange: function(e) {
var files = e.target.files ? Array.prototype.slice.call(e.target.files) : [];
addContractOriginalFiles(files);
e.target.value = '';
} }),
React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件')
)
)
),
React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null))
);
var contractFormRow4 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '交车地点', required: true, error: formErrors.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setEdited(true); setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } }))
);
var contractFormRow5 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal },
React.createElement('div', null,
React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '支持多个附件上传doc/docx/pdf'),
contractOriginalFiles && contractOriginalFiles.length
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 12 } },
contractOriginalFiles.map(function(f, fi) {
return React.createElement('div', { key: fi, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', padding: '8px 12px', border: '1px solid #f0f0f0', borderRadius: 6, backgroundColor: '#fafafa' } },
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 2 } },
React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginalFile(fi); } }, f.name),
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size ? f.size : '-') + ' · ' + (f.uploadTime ? f.uploadTime : '-'))
),
React.createElement(Button, { type: 'link', danger: true, onClick: function() { removeContractOriginalFile(fi); } }, '删除')
);
})
)
: null,
React.createElement('input', { ref: contractOriginalRef, type: 'file', multiple: true, accept: '.doc,.docx,.pdf', style: { display: 'none' }, onChange: function(e) {
var files = e.target.files ? Array.prototype.slice.call(e.target.files) : [];
addContractOriginalFiles(files);
e.target.value = '';
} }),
React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件')
)
)
);
var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setEdited(true); setRemarks(e.target.value); }, style: styles.textarea, rows: 4 })));
var authorizedContent = React.createElement('div', null,
@@ -649,7 +662,7 @@ const Component = function() {
React.createElement('td', { style: styles.rentalTdCenter }, calcRowServiceFee(row) + ' 元'),
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateRentalOrder(idx, 'deposit', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))),
React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '备注', value: row.remark || '', onChange: function(e) { updateRentalOrder(idx, 'remark', e.target.value); }, style: Object.assign({}, styles.rentalInput, { width: '100%' }) })),
React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeRentalRow(idx); } }, '删除'))
React.createElement('td', { style: styles.rentalTdCenter }, React.createElement('div', { style: { display: 'inline-flex', gap: 4, alignItems: 'center' } }, React.createElement(Button, { type: 'link', size: 'small', onClick: function() { copyRentalRow(idx); } }, '复制'), React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeRentalRow(idx); } }, '删除')))
);
});
@@ -671,7 +684,7 @@ const Component = function() {
var rentalTh8 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 90, verticalAlign: 'middle' }) }, '服务费');
var rentalTh9 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '保证金');
var rentalTh10 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '备注');
var rentalTh11 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 60, verticalAlign: 'middle' }) }, '操作');
var rentalTh11 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 90, verticalAlign: 'middle' }) }, '操作');
var rentalTableThead = React.createElement('thead', null, React.createElement('tr', null, rentalTh1, rentalTh2, rentalTh3, rentalTh4, rentalTh5, rentalTh6, rentalTh7, rentalTh8, rentalTh9, rentalTh10, rentalTh11));
var rentalTableTbody = React.createElement('tbody', null, rentalTableBody);
var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody);
@@ -758,7 +771,7 @@ const Component = function() {
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式')
),
React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)),
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))),
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow4, contractFormRow5, contractFormRow2))),
React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)),
React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)),
React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)),

View File

@@ -569,7 +569,6 @@ const Component = function() {
var contractFormRow1 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setEdited(true); setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, style: { width: '100%' } })),
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCodeDisplay, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '合同类型', required: true }, React.createElement(Select, { placeholder: '请选择合同类型', style: { width: '100%' }, value: '正式合同', disabled: true }, React.createElement(Option, { value: '正式合同' }, '正式合同'))),
React.createElement(FormItem, { label: '生效日期', required: true, error: formErrors.effectiveDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效日期', value: effectiveDate && window.moment ? window.moment(effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEdited(true); setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })),
React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setEdited(true); setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))),
@@ -577,35 +576,37 @@ const Component = function() {
React.createElement(FormItem, { label: '结束日期', required: true, error: formErrors.endDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择结束日期', value: endDate && window.moment ? window.moment(endDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEdited(true); setEndDate(dateStr || ''); }, status: formErrors.endDate ? 'error' : undefined })),
React.createElement(FormItem, { label: '付款周期', required: true, error: formErrors.paymentPeriod }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: paymentPeriod || undefined, onChange: function(v) { setEdited(true); setPaymentPeriod(v || ''); }, status: formErrors.paymentPeriod ? 'error' : undefined }, [1,2,3,4,5,6,7,8,9,10,11,12].map(function(n) { return React.createElement(Option, { key: n, value: String(n) }, n + '个月'); }))),
React.createElement(FormItem, { label: '签约公司', required: true, error: formErrors.signingCompany }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: signingCompany || undefined, onChange: function(v) { setEdited(true); setSigningCompany(v || ''); }, status: formErrors.signingCompany ? 'error' : undefined }, orgList.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))),
React.createElement('div', { style: styles.formCol },
React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null)),
React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal },
React.createElement('div', null,
React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '从原合同自动反写支持多个附件上传doc/docx/pdf'),
contractOriginalFiles && contractOriginalFiles.length
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 12 } },
contractOriginalFiles.map(function(f, fi) {
return React.createElement('div', { key: fi, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', padding: '8px 12px', border: '1px solid #f0f0f0', borderRadius: 6, backgroundColor: '#fafafa' } },
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 2 } },
React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginalFile(fi); } }, f.name),
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size ? f.size : '—') + ' · ' + (f.uploadTime ? f.uploadTime : '—'))
),
f.isOriginal ? null : React.createElement(Button, { type: 'link', danger: true, onClick: function() { removeContractOriginalFile(fi); } }, '删除')
);
})
)
: null,
React.createElement('input', { ref: contractOriginalRef, type: 'file', multiple: true, accept: '.doc,.docx,.pdf', style: { display: 'none' }, onChange: function(e) {
var files = e.target.files ? Array.prototype.slice.call(e.target.files) : [];
addContractOriginalFiles(files);
e.target.value = '';
} }),
React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件')
)
)
),
React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null))
);
var contractFormRow4 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '交车地点', required: true, error: formErrors.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setEdited(true); setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } }))
);
var contractFormRow5 = React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal },
React.createElement('div', null,
React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '从原合同自动反写支持多个附件上传doc/docx/pdf'),
contractOriginalFiles && contractOriginalFiles.length
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 12 } },
contractOriginalFiles.map(function(f, fi) {
return React.createElement('div', { key: fi, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', padding: '8px 12px', border: '1px solid #f0f0f0', borderRadius: 6, backgroundColor: '#fafafa' } },
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 2 } },
React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginalFile(fi); } }, f.name),
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size ? f.size : '—') + ' · ' + (f.uploadTime ? f.uploadTime : '—'))
),
f.isOriginal ? null : React.createElement(Button, { type: 'link', danger: true, onClick: function() { removeContractOriginalFile(fi); } }, '删除')
);
})
)
: null,
React.createElement('input', { ref: contractOriginalRef, type: 'file', multiple: true, accept: '.doc,.docx,.pdf', style: { display: 'none' }, onChange: function(e) {
var files = e.target.files ? Array.prototype.slice.call(e.target.files) : [];
addContractOriginalFiles(files);
e.target.value = '';
} }),
React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件')
)
)
);
var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setEdited(true); setRemarks(e.target.value); }, style: styles.textarea, rows: 4 })));
var authorizedContent = React.createElement('div', null,
@@ -841,7 +842,7 @@ const Component = function() {
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式')
),
React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)),
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))),
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow4, contractFormRow5, contractFormRow2))),
React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)),
React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)),
React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)),

View File

@@ -51,6 +51,7 @@ const Component = function() {
});
var _vehiclePopoverRecord = useState(null);
var _deliveredPopoverRecord = useState(null);
var _authorizedModalVisible = useState(false);
var _authorizedModalRecord = useState(null);
var _authorizedList = useState([{ name: '', phone: '', idCard: '' }]);
@@ -140,8 +141,8 @@ const Component = function() {
projectName: '嘉兴氢能示范项目',
vehicleCount: 2,
vehicles: [
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A12345', actualDelivery: '2025-01-10 09:00' },
{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '-', actualDelivery: '2025-01-12 14:30' }
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A12345', actualDelivery: '2025-01-10 09:00', deliveryPerson: '张运维' },
{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '-', actualDelivery: '2025-01-12 14:30', deliveryPerson: '李运维' }
],
approvalStatus: '未提交',
contractStatus: '草稿',
@@ -166,7 +167,7 @@ const Component = function() {
projectName: '上海物流租赁项目',
vehicleCount: 1,
vehicles: [
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '沪D66666', actualDelivery: '2025-02-01 11:00' }
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '沪D66666', actualDelivery: '2025-02-01 11:00', deliveryPerson: '王运维' }
],
approvalStatus: '未提交',
contractStatus: '草稿',
@@ -191,7 +192,7 @@ const Component = function() {
projectName: '杭州城配租赁项目',
vehicleCount: 1,
vehicles: [
{ vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '浙B20002', actualDelivery: '2025-02-15 08:30' }
{ vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '浙B20002', actualDelivery: '2025-02-15 08:30', deliveryPerson: '赵运维' }
],
approvalStatus: '待审批',
contractStatus: '已提交审批',
@@ -216,8 +217,8 @@ const Component = function() {
projectName: '宁波冷链运输项目',
vehicleCount: 2,
vehicles: [
{ vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B2', plateNo: '-', actualDelivery: '2025-02-16 10:00' },
{ vehicleType: '49吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '浙C30003', actualDelivery: '2025-02-18 14:00' }
{ vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B2', plateNo: '-', actualDelivery: '2025-02-16 10:00', deliveryPerson: '钱运维' },
{ vehicleType: '49吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '浙C30003', actualDelivery: '2025-02-18 14:00', deliveryPerson: '孙运维' }
],
approvalStatus: '审批中',
contractStatus: '已提交审批',
@@ -242,7 +243,7 @@ const Component = function() {
projectName: '苏州城配试点项目',
vehicleCount: 1,
vehicles: [
{ vehicleType: '重型平板半挂车-重型平板半挂车', brand: '品牌D', model: '型号D2', plateNo: '苏E50005', actualDelivery: '2025-02-20 09:00' }
{ vehicleType: '重型平板半挂车-重型平板半挂车', brand: '品牌D', model: '型号D2', plateNo: '苏E50005', actualDelivery: '2025-02-20 09:00', deliveryPerson: '周运维' }
],
approvalStatus: '审批中',
contractStatus: '变更',
@@ -267,9 +268,9 @@ const Component = function() {
projectName: '南京氢能示范项目',
vehicleCount: 3,
vehicles: [
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏A60006', actualDelivery: '2025-01-20 08:00' },
{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏A60007', actualDelivery: '2025-01-21 10:00' },
{ vehicleType: '35吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '苏A60008', actualDelivery: '2025-01-22 14:00' }
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏A60006', actualDelivery: '2025-01-20 08:00', deliveryPerson: '吴运维' },
{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏A60007', actualDelivery: '2025-01-21 10:00', deliveryPerson: '郑运维' },
{ vehicleType: '35吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '苏A60008', actualDelivery: '2025-01-22 14:00', deliveryPerson: '冯运维' }
],
approvalStatus: '审批通过',
contractStatus: '合同进行中',
@@ -294,7 +295,7 @@ const Component = function() {
projectName: '无锡试用租赁项目',
vehicleCount: 1,
vehicles: [
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C2', plateNo: '苏B70007', actualDelivery: '2025-02-01 09:30' }
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C2', plateNo: '苏B70007', actualDelivery: '2025-02-01 09:30', deliveryPerson: '陈运维' }
],
approvalStatus: '审批通过',
contractStatus: '合同进行中',
@@ -345,8 +346,8 @@ const Component = function() {
projectName: '南通去年到期项目',
vehicleCount: 2,
vehicles: [
{ vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏F90009', actualDelivery: '2024-03-01 09:00' },
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏F90010', actualDelivery: '2024-03-02 10:00' }
{ vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏F90009', actualDelivery: '2024-03-01 09:00', deliveryPerson: '褚运维' },
{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏F90010', actualDelivery: '2024-03-02 10:00', deliveryPerson: '卫运维' }
],
approvalStatus: '审批通过',
contractStatus: '到期合同',
@@ -371,7 +372,7 @@ const Component = function() {
projectName: '镇江到期合同项目',
vehicleCount: 1,
vehicles: [
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '苏L00100', actualDelivery: '2024-06-01 11:00' }
{ vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '苏L00100', actualDelivery: '2024-06-01 11:00', deliveryPerson: '蒋运维' }
],
approvalStatus: '审批通过',
contractStatus: '已结束',
@@ -602,12 +603,21 @@ const Component = function() {
var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
var tableSingleLineStyle = '.contract-list-table .ant-table-thead th,.contract-list-table .ant-table-tbody td{white-space:nowrap;}';
var vehicleColumns = [
// 租赁车辆数气泡:不含实际交车日期
var vehicleColumnsRental = [
{ title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 180 },
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 },
{ title: '型号', dataIndex: 'model', key: 'model', width: 100 },
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }
];
// 已交车辆数气泡:含实际交车日期,后方增加交车人
var vehicleColumnsDelivered = [
{ title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 180 },
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 },
{ title: '型号', dataIndex: 'model', key: 'model', width: 100 },
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 },
{ title: '实际交车日期', dataIndex: 'actualDelivery', key: 'actualDelivery', width: 140 }
{ title: '实际交车日期', dataIndex: 'actualDelivery', key: 'actualDelivery', width: 140 },
{ title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 100 }
];
function getMoreMenuItems(record) {
@@ -681,19 +691,19 @@ const Component = function() {
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 140, fixed: 'left' },
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, fixed: 'left' },
{
title: '车辆数',
title: '租赁车辆数',
key: 'vehicleCount',
width: 90,
width: 100,
render: function(_, record) {
var open = _vehiclePopoverRecord[0] && _vehiclePopoverRecord[0].id === record.id;
var content = React.createElement('div', { style: { padding: 8 } },
React.createElement(Table, {
size: 'small',
rowKey: function(r, i) { return String(i); },
columns: vehicleColumns,
columns: vehicleColumnsRental,
dataSource: record.vehicles || [],
pagination: false,
scroll: { x: 600 }
scroll: { x: 460 }
})
);
return React.createElement(
@@ -712,6 +722,43 @@ const Component = function() {
);
}
},
{
title: '已交车辆数',
key: 'deliveredCount',
width: 100,
render: function(_, record) {
var deliveredVehicles = (record.vehicles || []).filter(function(v) {
var d = v.actualDelivery;
return d && String(d).trim() && d !== '-';
});
var deliveredCount = deliveredVehicles.length;
var open = _deliveredPopoverRecord[0] && _deliveredPopoverRecord[0].id === record.id;
var content = React.createElement('div', { style: { padding: 8 } },
React.createElement(Table, {
size: 'small',
rowKey: function(r, i) { return String(i); },
columns: vehicleColumnsDelivered,
dataSource: deliveredVehicles,
pagination: false,
scroll: { x: 700 }
})
);
return React.createElement(
Popover,
{
content: content,
title: '已交车列表',
open: open,
onOpenChange: function(visible) {
if (!visible) _deliveredPopoverRecord[1](null);
else _deliveredPopoverRecord[1](record);
},
trigger: 'click'
},
React.createElement('a', { style: { cursor: 'pointer', color: '#1890ff', fontWeight: 500 } }, deliveredCount)
);
}
},
{ title: '审批状态', dataIndex: 'approvalStatus', key: 'approvalStatus', width: 100 },
{ title: '合同状态', dataIndex: 'contractStatus', key: 'contractStatus', width: 110 },
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140 },
@@ -1028,10 +1075,11 @@ const Component = function() {
React.createElement('div', { style: reqItemStyle }, '2.1.10.创建人:选择器,支持全选或多选,拉取所有业务相关部门下所有用户姓名;'),
React.createElement('div', { style: reqItemStyle }, '2.1.11.合同结束日期:日期选择器,支持单输入框内双日历选择开始-结束时间;'),
React.createElement('div', { style: reqSectionStyle }, '3.列表:'),
React.createElement('div', { style: reqSubStyle }, '3.1.列表展示所有租赁合同信息,字段依次为:合同编码、项目名称、车辆数、审批状态、合同状态、客户名称、签约公司、业务部门、业务负责人、合同类型、合同结束日期、客户联系人、联系电话、创建人、创建时间、更新人、最后更新时间、备注、操作;列表右上角为新增、导出;'),
React.createElement('div', { style: reqSubStyle }, '3.1.列表展示所有租赁合同信息,字段依次为:合同编码、项目名称、租赁车辆数、已交车辆数、审批状态、合同状态、客户名称、签约公司、业务部门、业务负责人、合同类型、合同结束日期、客户联系人、联系电话、创建人、创建时间、更新人、最后更新时间、备注、操作;列表右上角为新增、导出;'),
React.createElement('div', { style: reqItemStyle }, '3.1.1.合同编码:显示租赁合同对应合同编码;'),
React.createElement('div', { style: reqItemStyle }, '3.1.2.项目名称:显示租赁合同对应项目名称;'),
React.createElement('div', { style: reqItemStyle }, '3.1.3.车辆数:显示车辆数,点击车辆数,显示气泡卡片,卡片中列表显示:车辆类型、品牌、型号、车牌号、实际交车日期;'),
React.createElement('div', { style: reqItemStyle }, '3.1.3.租赁车辆数:显示租赁车辆数,点击显示气泡卡片,卡片中列表显示:车辆类型、品牌、型号、车牌号、实际交车日期;'),
React.createElement('div', { style: reqItemStyle }, '3.1.4.已交车辆数:显示已交车辆数,样式与交互同租赁车辆数,点击显示气泡卡片,卡片中显示已交车列表(同租赁车辆数列表结构);'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.3.1.车辆类型4.5吨冷链车-轻型厢式货车、18吨双飞翼货车-重型厢式货车、49吨牵引车头-重型半挂牵引车、4.5吨货车-轻型厢式货车、18吨厢式货车-重型厢式货车、重型集装箱半挂车-重型集装箱半挂车、公务用车/小客车-小型普通客车、35吨牵引车头-重型半挂牵引车、重型平板半挂车-重型平板半挂车;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.3.2.品牌:显示租赁合同中对应车辆品牌;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.3.3.型号:显示租赁合同中对应车辆型号;'),
@@ -1043,6 +1091,7 @@ const Component = function() {
React.createElement('div', { style: reqSubItemStyle }, '3.1.4.3.审批通过:发起人已提交,最终节点完成审批;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.4.4.审批驳回:发起人已提交,任意流程节点驳回,该状态下操作列支持编辑和重新提交;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.4.5.未提交:发起人仅保存,但未提交审批;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.4.6.撤回:发起人主动撤回审批流程;'),
React.createElement('div', { style: reqItemStyle }, '3.1.5.合同状态:显示租赁合同状态,状态分为:草稿、变更、合同进行中、到期合同、已提交审批、已结束;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.5.1.草稿:发起人仅保存,但未提交审批;'),
React.createElement('div', { style: reqSubItemStyle }, '3.1.5.2.变更:发起人提交后,合同已通过审批的基础上,进行了「变更内容」操作,且已提交审批,但未完成最终节点审批;'),