From 8459ced6e59f484e67a5998e1c4ac3724fe012be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Mon, 20 Apr 2026 22:04:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B7=A5=E4=BD=9C=E5=8F=B0?= =?UTF-8?q?=E4=B8=8E=E8=BD=A6=E8=BE=86=E7=A7=9F=E8=B5=81=E5=90=88=E5=90=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- web端/工作台.jsx | 58 +++++++++++++++++++---- web端/车辆租赁合同/车辆租赁合同-新增.jsx | 60 +++++++++++++++--------- 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/web端/工作台.jsx b/web端/工作台.jsx index bc936a8..8e8cc9c 100644 --- a/web端/工作台.jsx +++ b/web端/工作台.jsx @@ -366,18 +366,19 @@ const Component = function () { return n; }, [noticeList]); - var markAllNoticesRead = useCallback(function () { + var markNoticeRead = useCallback(function (id) { setNoticeList(function (prev) { return (prev || []).map(function (n) { + if (n.id !== id) return n; + if (n.read) return n; return Object.assign({}, n, { read: true }); }); }); }, []); var openNoticeModal = useCallback(function () { - markAllNoticesRead(); setNoticeModalOpen(true); - }, [markAllNoticesRead]); + }, []); var pushUrgeNotice = useCallback(function (taskRow) { var adminName = mockAdminDisplayName; @@ -908,9 +909,35 @@ const Component = function () { return [ { title: '时间', dataIndex: 'time', key: 'time', width: 150 }, { title: '通知类型', dataIndex: 'type', key: 'type', width: 160 }, - { title: '内容', dataIndex: 'content', key: 'content', ellipsis: true } + { title: '内容', dataIndex: 'content', key: 'content', ellipsis: true }, + { + title: '状态', + key: 'read', + width: 72, + render: function (_, record) { + return record.read + ? React.createElement(Tag, { color: 'default', style: { margin: 0, fontSize: 11, lineHeight: '18px' } }, '已读') + : React.createElement(Tag, { color: 'warning', style: { margin: 0, fontSize: 11, lineHeight: '18px' } }, '未读'); + } + }, + { + title: '操作', + key: 'markRead', + width: 88, + render: function (_, record) { + if (record.read) { + return React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, '—'); + } + return React.createElement(Button, { + type: 'link', + size: 'small', + style: { padding: 0, height: 'auto' }, + onClick: function () { markNoticeRead(record.id); } + }, '设为已读'); + } + } ]; - }, []); + }, [markNoticeRead]); var overdueDeliveryPopoverTableStyle = { width: '100%', borderCollapse: 'collapse', fontSize: 12 }; var overdueDeliveryPopoverThStyle = { padding: '6px 8px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', backgroundColor: '#fafafa', fontWeight: 600 }; @@ -1354,10 +1381,20 @@ const Component = function () { ? React.createElement(Tag, { color: 'warning', style: { marginLeft: 6, fontSize: 11, lineHeight: '18px', padding: '0 6px' } }, '未读') : null ), - React.createElement(Text, { - ellipsis: { tooltip: true }, - style: { fontSize: 12, color: 'rgba(0,0,0,0.78)', display: 'block', lineHeight: 1.45, width: '100%' } - }, n.content) + React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 8, width: '100%' } }, + React.createElement(Text, { + ellipsis: { tooltip: true }, + style: { fontSize: 12, color: 'rgba(0,0,0,0.78)', display: 'block', lineHeight: 1.45, flex: 1, minWidth: 0 } + }, n.content), + !n.read + ? React.createElement(Button, { + type: 'link', + size: 'small', + style: { padding: 0, flexShrink: 0, height: 'auto', lineHeight: 1.45, fontSize: 12 }, + onClick: function () { markNoticeRead(n.id); } + }, '设为已读') + : null + ) ); }); return React.createElement(Card, { @@ -1803,7 +1840,8 @@ const Component = function () { rowKey: 'id', columns: noticeColumns, dataSource: noticeList, - pagination: { pageSize: 8, showSizeChanger: false } + pagination: { pageSize: 8, showSizeChanger: false }, + scroll: { x: 720 } }) ), diff --git a/web端/车辆租赁合同/车辆租赁合同-新增.jsx b/web端/车辆租赁合同/车辆租赁合同-新增.jsx index be09e2a..5493da6 100644 --- a/web端/车辆租赁合同/车辆租赁合同-新增.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-新增.jsx @@ -10,6 +10,8 @@ const Component = function() { var Modal = antd.Modal; var message = antd.message; var Option = Select.Option; + var Row = antd.Row; + var Col = antd.Col; var cs1 = React.useState(''); var customerSearch = cs1[0]; @@ -399,7 +401,7 @@ const Component = function() { breadcrumb: { marginBottom: 16, color: 'rgba(0,0,0,0.65)' }, breadcrumbSep: { margin: '0 8px', color: 'rgba(0,0,0,0.35)' }, anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 12, border: '1px solid rgba(0,0,0,0.06)', boxShadow: '0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.06)', padding: '12px 16px', minWidth: 168 }, - anchorItem: { display: 'block', padding: '8px 4px', minHeight: 36, color: '#1677ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13, borderRadius: 6, outline: 'none' }, + anchorItem: { display: 'block', padding: '8px 4px', minHeight: 36, color: '#1677ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 14, borderRadius: 6, outline: 'none' }, card: { backgroundColor: '#fff', borderRadius: 12, marginBottom: 16, border: '1px solid rgba(0,0,0,0.04)', boxShadow: '0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.06)', overflow: 'hidden' }, cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid rgba(0,0,0,0.06)', cursor: 'pointer' }, cardTitle: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.88)' }, @@ -421,9 +423,9 @@ const Component = function() { summaryListLabel: { flex: '0 0 140px', color: '#666' }, summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, authRow: { display: 'flex', gap: 12, alignItems: 'flex-start', marginBottom: 12 }, - authInput: { flex: 1, padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4 }, - btnDel: { padding: '8px 16px', color: '#ff4d4f', border: '1px solid #ff4d4f', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer' }, - btnAdd: { padding: '8px 16px', color: '#1677ff', border: '1px dashed #1677ff', borderRadius: 8, backgroundColor: '#fff', cursor: 'pointer', marginBottom: 16 }, + authInput: { flex: 1, padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + btnDel: { padding: '8px 16px', color: '#ff4d4f', border: '1px solid #ff4d4f', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer', fontSize: 14 }, + btnAdd: { padding: '8px 16px', color: '#1677ff', border: '1px dashed #1677ff', borderRadius: 8, backgroundColor: '#fff', cursor: 'pointer', marginBottom: 16, fontSize: 14 }, footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid rgba(0,0,0,0.06)', display: 'flex', gap: 12, justifyContent: 'flex-start', alignItems: 'center', zIndex: 99, boxShadow: '0 -2px 8px rgba(0,0,0,0.04)' }, btn: { padding: '8px 24px', borderRadius: 8, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14, minHeight: 40 }, btnPrimary: { backgroundColor: '#1677ff', color: '#fff', borderColor: '#1677ff' }, @@ -432,12 +434,12 @@ const Component = function() { regionCascader: { position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4, backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: 4, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', zIndex: 10, display: 'flex', minHeight: 200 }, regionCascaderCol: { flex: 1, borderRight: '1px solid #f0f0f0', overflowY: 'auto' }, regionCascaderColLast: { flex: 1 }, - regionCascaderItem: { padding: '10px 12px', cursor: 'pointer' }, - rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, - rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, - rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'top' }, - rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, - rentalInput: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 13 }, + regionCascaderItem: { padding: '10px 12px', cursor: 'pointer', fontSize: 14 }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 14 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600, fontSize: 14 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'top', fontSize: 14 }, + rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle', fontSize: 14 }, + rentalInput: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#999' }, modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, @@ -447,7 +449,7 @@ const Component = function() { btnGroupItem: { padding: '8px 16px', border: 'none', borderRight: '1px solid #d9d9d9', backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, btnGroupItemLast: { borderRight: 'none' }, btnGroupItemActive: { backgroundColor: '#1677ff', color: '#fff', borderColor: '#1677ff', borderRightColor: '#1677ff' }, - feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitle: { fontSize: 16, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, feeSectionTitleFirst: { marginTop: 0 }, modalFormInput: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, height: 36, boxSizing: 'border-box' } }; @@ -478,16 +480,17 @@ const Component = function() { }; var FormItem = function(props) { - var colStyle = props.fullWidth ? styles.formColFull : (props.colStyle ? Object.assign({}, styles.formCol, props.colStyle) : styles.formCol); - return React.createElement('div', { style: colStyle }, - React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + var span = props.fullWidth ? 24 : (props.span || 8); // 满宽占 24 列,常规占 8 列(1/3 宽度) + var colStyle = props.colStyle || {}; + return React.createElement(Col, { span: span, style: Object.assign({ marginBottom: 16 }, colStyle) }, + React.createElement('label', { style: styles.label, className: 'acro-text-base' }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), props.children, - props.error ? React.createElement('div', { style: styles.errMsg, role: 'alert' }, props.error) : null + props.error ? React.createElement('div', { style: styles.errMsg, className: 'acro-text-xs', role: 'alert' }, props.error) : null ); }; var customerOptions = customerList.map(function(c) { return React.createElement(Option, { key: c.id, value: c.id }, c.name); }); - var customerFields = React.createElement('div', { style: styles.formRow }, + var customerFields = React.createElement(Row, { gutter: 24 }, React.createElement(FormItem, { label: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, onChange: function(id) { var c = customerList.find(function(x) { return x.id === id; }); selectCustomer(c || null); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, status: formErrors.customer ? 'error' : undefined }, customerOptions)), React.createElement(FormItem, { label: '客户统一信用代码' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.creditCode : '', disabled: true, style: { width: '100%' } })), React.createElement(FormItem, { label: '客户地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.address : '', disabled: true, style: { width: '100%' } })), @@ -504,7 +507,7 @@ const Component = function() { React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setBusinessOwner(v || ''); setOwnerFocusError(''); }, onFocus: handleOwnerFocus, onBlur: handleOwnerBlur, disabled: !businessDept, status: (formErrors.businessOwner || ownerFocusError) ? 'error' : undefined }, ownerOptions.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))) ); - var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + var contractFormRow1 = React.createElement(Row, { gutter: 24 }, React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, 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) { 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) { setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })), @@ -513,13 +516,13 @@ 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) { 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) { 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) { 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(React.Fragment, null, 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, role: 'combobox', 'aria-expanded': deliveryRegionOpen, 'aria-haspopup': 'listbox', 'aria-controls': 'delivery-region-cascader', 'aria-invalid': !!formErrors.deliveryRegion, 'aria-label': '交车区域,省-市', onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); }, onKeyDown: function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setDeliveryRegionOpen(!deliveryRegionOpen); } } }), deliveryRegionOpen ? React.createElement('div', { id: 'delivery-region-cascader', 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: '#e6f4ff', color: '#1677ff' } : {}), 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(); 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: '#e6f4ff', color: '#1677ff' } : {}), 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.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } })), React.createElement(FormItem, { label: '合同审批类型', required: true, error: formErrors.contractApprovalType }, React.createElement(Select, { placeholder: '请选择合同审批类型', style: { width: '100%' }, value: contractApprovalType || undefined, onChange: function(v) { setContractApprovalType(v || ''); }, status: formErrors.contractApprovalType ? 'error' : undefined }, React.createElement(Option, { value: '标准合同审批' }, '标准合同审批'), React.createElement(Option, { value: '非标准合同审批' }, '非标准合同审批'))) ); - var contractFormRow1Part2 = React.createElement('div', { style: styles.formRow }, + var contractFormRow1Part2 = React.createElement(Row, { gutter: 24 }, React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal }, contractOriginal ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, @@ -544,7 +547,7 @@ const Component = function() { ) ) ); - var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setRemarks(e.target.value); }, style: styles.textarea, rows: 4 }))); + var contractFormRow2 = React.createElement(Row, { gutter: 24 }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setRemarks(e.target.value); }, style: styles.textarea, rows: 4 }))); var authorizedContent = React.createElement('div', null, React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人姓名'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人联系电话'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人身份证'), React.createElement('span', { style: { flex: '0 0 80px' } })), @@ -561,7 +564,7 @@ const Component = function() { var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setReturnHydrogenPrice(e.target.value); }, addonAfter: '元', status: formErrors.returnHydrogenPrice ? 'error' : undefined, style: { width: '100%' } }); var returnHydrogenColStyle = hydrogenPaymentMethod === '预付' ? { flex: '1 1 0', minWidth: 180 } : { flex: '0 0 calc(50% - 8px)', minWidth: 180 }; hydrogenFields.push(React.createElement('div', { key: 'hydrogen-amount-row', style: { display: 'flex', gap: 16, flex: '1 1 100%' } }, hydrogenPaymentMethod === '预付' ? React.createElement(FormItem, { label: '氢气预付款', required: true, error: formErrors.hydrogenPrepay, colStyle: { flex: '1 1 0', minWidth: 180 } }, hydrogenPrepayInput) : null, React.createElement(FormItem, { label: '退还车氢气单价', required: true, error: formErrors.returnHydrogenPrice, colStyle: returnHydrogenColStyle }, returnHydrogenInput))); - return React.createElement.apply(React, ['div', { style: Object.assign({}, styles.formRow, { marginTop: 20 }) }].concat(hydrogenFields)); + return React.createElement.apply(React, [Row, { gutter: 24, style: { marginTop: 20 } }].concat(hydrogenFields)); })(); var plateNoOptions = vehicleList.map(function(v) { return React.createElement(Option, { key: v.plateNo, value: v.plateNo }, v.plateNo); }); @@ -632,7 +635,7 @@ const Component = function() { var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows)); var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows)); var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows)); - var feeTemplateSelect = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择费用模板', required: true, error: formErrors.feeTemplate }, React.createElement(Select, { placeholder: '请选择费用模板', style: { width: '100%' }, value: feeTemplate || undefined, onChange: function(v) { setFeeTemplate(v || ''); }, status: formErrors.feeTemplate ? 'error' : undefined }, feeTemplates.map(function(f, i) { return React.createElement(Option, { key: i, value: f }, f); })))); + var feeTemplateSelect = React.createElement(Row, { gutter: 24 }, React.createElement(FormItem, { label: '选择费用模板', required: true, error: formErrors.feeTemplate }, React.createElement(Select, { placeholder: '请选择费用模板', style: { width: '100%' }, value: feeTemplate || undefined, onChange: function(v) { setFeeTemplate(v || ''); }, status: formErrors.feeTemplate ? 'error' : undefined }, feeTemplates.map(function(f, i) { return React.createElement(Option, { key: i, value: f }, f); })))); var feeTemplateBody = feeTemplate ? React.createElement('div', null, React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), feeCertTable, @@ -660,8 +663,19 @@ 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.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;\n2.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;\n2.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;\n2.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;\n2.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;\n2.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;\n2.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;\n2.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;\n2.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;\n2.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人字段不可选;\n\n3.合同基本信息卡片:\n#用于定义租赁合同基本情况和付款方式;\n3.1.项目名称:必填项,输入框,用于定义该合同项目名称,默认提示信息"请输入项目名称";\n3.2.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;\n3.3.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期;\n3.4.付款方式:必选项,付款方式分为「预付」「后付」两种;\n 3.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户;\n 3.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户;\n3.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称;\n3.6.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;\n 3.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 3.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 3.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n3.7.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单;\n3.8.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据;\n3.9.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;\n3.10.交车地点:必填项,输入框,支持自定义输入交车地点;\n3.11.合同审批类型:必填项,选择器,分为「标准合同审批」「非标准合同审批」;\n3.12.合同原件:必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.13.备注:文本域,支持自定义输入备注信息;\n\n4.被授权人信息卡片:\n#用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认;\n4.1.被授权人:必填项,输入框,用于输入被授权人信息;\n4.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;\n4.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;\n4.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可从多个授权人中选择接收授权人;\n\n5.租赁订单信息卡片:\n#用于定义租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n5.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 5.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 5.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 5.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 5.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n5.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注,默认且至少显示一行空数据;\n 5.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;\n 5.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;\n 5.2.3.型号:必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号;\n 5.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写;\n 5.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写;\n 5.2.6.车辆月租金:输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算;\n 5.2.7.服务费项目:点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;\n 5.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;\n 5.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;\n 5.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD;\n 5.2.7.4.操作:删除,点击删除直接删除该行数据;\n 5.2.7.5.新增一行数据:点击添加一行服务项目;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 5.2.8.服务费:自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元;\n 5.2.9.保证金:必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中;\n 5.2.10.备注:选填项,输入框,用于备注车辆复杂情况;\n 5.2.11.操作:删除,点击删除删除该行数据;\n 5.2.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息;\n5.3.氢费承担方:必选项,选择器,选项为:「我方」、「客户」,默认选择「客户」;\n 5.3.1.选择「我方」:不显示付款方式、氢气预付款;\n 5.3.2.选择「客户」:付款方式字段默认为预付,可手动修改;\n5.4.付款方式:必选项,选择器,选项为:「预付」、「月付款」、「自行结算」,默认为:「预付」;\n 5.4.1.预付:选择「预付」,需要填写:「氢气预付款」,氢气预付款指合同签署时客户就需预先付出的氢费款项,该部分款项会自动计入提车应收款中氢气预付款金额;\n 5.4.2.月付款:选择「月付款」,提车应收款中不进行收费,而是由业务人员按照实际情况,通过氢费账单功能生成对应氢费账单,单独与客户进行结算;\n 5.4.3.自行结算:选择「自行结算」,指合同签署后,所有氢气费用由客户自行承担;\n5.5.氢气预付款:选择「客户」「预付」时显示,必填项,输入框,支持2位小数,氢气预付款金额会计算入该合同交车应收款中,并计入5.1.4.氢气预付款金额中;\n5.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,不管氢费承担方和付款方式选择任何选项都需要进行维护,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n6.其他费用信息卡片:\n#用于选择对应租赁费用模板,选择后展示证照补办费用、违约金费用、易损件费用、其他费用等信息,租赁费用模板管理功能位于「车辆租赁合同」列表左上角;\n6.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 6.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;\n 6.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n\n7.账单计算方式卡片:\n#必选项,填充按钮组,默认为按自然月结算,可手动修改,用于定义租赁合同的账单计算方式,分为「按自然月结算」、「按付款周期天数结算」两种方式;\n7.1.按付款周期天数结算:账单按照合同基本信息卡片中每隔付款周期*30天形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款从交车任务配置的开始计费时间开始,根据60天收取车辆租金,此后每隔60天生成一期租赁账单;\n7.2.按自然月结算:账单按照第一个月计费开始日期到当月最后一天为第一期,之后按照付款周期所选月份间隔,从开始月份第一天到间隔月份最后一天的自然月方式形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款车辆租金需要收取≥2个月租金作为标准流程,<2个月租金作为非标流程;\n 租赁账单首期从交车任务配置的开始计费时间开始,如付款周期为2个月,则首期账单结束时间为第二个月最后一天,付款周期为3个月,则首期账单结束时间为第三个月最后一天,此后每一期按照实际自然月开始-结束形成账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;\n\n8.最下方为提交并审核、保存、取消三个按钮;\n8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;\n8.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作;\n8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;\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, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + var acroCss = React.createElement('style', null, ` + .acro-text-xs { font-size: 12px; } + .acro-text-sm { font-size: 13px; } + .acro-text-base { font-size: 14px; } + .acro-text-lg { font-size: 16px; } + .acro-text-xl { font-size: 20px; } + .acro-text-xxl { font-size: 24px; } + .acro-title-promo { font-size: 36px; } + `); + 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 }, 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: '#1677ff' } }, '新增租赁合同')), React.createElement('button', { type: 'button', 'aria-label': '打开需求说明', style: { color: '#1677ff', cursor: 'pointer', fontSize: 14, border: 'none', background: 'none', padding: '8px 4px', minHeight: 40, borderRadius: 6 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + acroCss, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb, className: 'acro-text-base' }, 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: '#1677ff' } }, '新增租赁合同')), React.createElement('button', { type: 'button', 'aria-label': '打开需求说明', style: { color: '#1677ff', cursor: 'pointer', border: 'none', background: 'none', padding: '8px 4px', minHeight: 40, borderRadius: 6 }, className: 'acro-text-base', onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), React.createElement('div', { style: styles.anchorWrap, role: 'navigation', 'aria-label': '页面区块锚点导航' }, React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), React.createElement('button', { type: 'button', style: styles.anchorItem, 'aria-label': '跳转到客户基本信息', onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'),