diff --git a/web端/财务管理/提车应收款.jsx b/web端/财务管理/提车应收款.jsx new file mode 100644 index 0000000..2a823cf --- /dev/null +++ b/web端/财务管理/提车应收款.jsx @@ -0,0 +1,432 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 提车应收款(财务管理 - 运维管理-财务管理-提车应收款,antd 规范布局) + +const Component = function() { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Select = antd.Select; + var Button = antd.Button; + var Table = antd.Table; + var Card = antd.Card; + var DatePicker = antd.DatePicker; + var Modal = antd.Modal; + var Space = antd.Space; + var message = antd.message; + var App = antd.App; + + var RangePicker = DatePicker.RangePicker; + var dayjs = window.dayjs || null; + + // 筛选条件(表单输入) + var _contractCode = useState(undefined); + var _projectName = useState(undefined); + var _customerName = useState(undefined); + var _auditStatus = useState(undefined); + var _chargeOperator = useState(undefined); + var _chargeTimeRange = useState(null); + var _invoiceTimeRange = useState(null); + var _invoiceOperator = useState(undefined); + // 已生效筛选(点击查询后) + var _appliedFilter = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + auditStatus: undefined, + chargeOperator: undefined, + chargeTimeRange: null, + invoiceTimeRange: null, + invoiceOperator: undefined + }); + // 列表多选(默认全选由 Table rowSelection 控制) + var _selectedRowKeys = useState([]); + // 撤回二次确认 + var _withdrawModalVisible = useState(false); + var _withdrawRecord = useState(null); + // 筛选展开(默认只显示第一行,点击更多展开、收起关闭) + var _filterExpanded = useState(false); + // 查看需求说明弹窗 + var _requirementModalVisible = useState(false); + + // 需求说明文案 + var requirementContent = '「车辆管理系统」中的「财务管理」模块下「提车应收款」模块\n1.面包屑:\n1.1.运维管理-财务管理-提车应收款\n页面由筛选卡片、列表卡片组成;\n\n2.筛选:\n2.1.合同编码:用来通过提车应收款筛选车辆租赁合同编码;选择器,支持从输入框输入内容模糊搜索,选择器支持点击删除,下拉显示匹配项;\n2.2.项目名称:用来通过提车应收款筛选车辆租赁合同对应项目名称;选择器,支持从输入框输入内容模糊搜索,选择器支持点击删除,下拉显示匹配项;\n2.3.客户名称:用来通过提车应收款筛选车辆租赁合同对应客户名称,选择器,支持从输入框输入内容模糊搜索,选择器支持点击删除,下拉显示匹配项;\n2.4.审核状态:用来通过审核状态筛选对应提车应收款数据,选择器,选项为:全部、待提交、审批中、审批完成、审批驳回、撤回;\n2.5.收费情况操作人:用来通过收费情况操作人筛选对应提车应收款数据,选择器,支持从输入框输入内容模糊搜索,选择器支持点击删除,下拉显示匹配项,选项为:全部、所有操作人;\n2.6.收费情况提交时间:日期选择器,支持单输入框双日历选择开始-结束时间,提示信息为:请选择开始时间、请选择结束时间;\n2.7.开票时间:日期选择器,支持单输入框双日历选择开始-结束时间,提示信息为:请选择开始时间、请选择结束时间;\n2.8.开票操作人:用来通过收费情况操作人筛选对应提车应收款数据,选择器,支持从输入框输入内容模糊搜索,选择器支持点击删除,下拉显示匹配项,选项为:全部、所有操作人;\n2.9.右侧为查询和重置按钮,筛选条件以且的模式进行筛选,点击重置清空条件,点击查询与列表进行联动;\n\n3.列表:\n合同到达生效日期立刻生成提车应收款数据,以列表进行显示,列表第一列为多选框,默认全选,可手动取消选择某行数据,配合导出按钮进行对应数据导出;\n列表字段依次为:合同编码、项目名称、客户名称、业务部门、业务负责人、合同生效日期、应收总金额、减免总金额、审核状态、收费情况操作人、收费情况提交时间、开票时间、开票操作人、操作;\n右上角为导出按钮,支持筛选后多选行数据进行导出;\n3.1.合同编码:显示该笔提车应收款对应车辆租赁合同-合同编码;\n3.2.项目名称:显示该笔提车应收款对应车辆租赁合同-项目名称;\n3.3.客户名称:显示该笔提车应收款对应车辆租赁合同-客户名称;\n3.4.业务部门:显示该笔提车应收款对应车辆租赁合同-业务部门;\n3.5.业务负责人:显示该笔提车应收款对应车辆租赁合同-业务负责人;\n3.6.合同生效日期:显示该笔提车应收款对应车辆租赁合同-合同生效日期;\n3.7.应收总金额:显示该笔提车提车应收款总金额,格式为:xx.xx元。从收费明细中计算(业务提交审核前会根据业务实际填写金额重新计算),计算方式为:提车应收款总额+氢费预付款总额;\n3.8.减免总金额:显示收费明细中所有减免总金额总额,无则显示为0.00元;\n3.9.审核状态:分为待提交、审批中、审批完成、审批驳回、撤回;\n 3.9.1.待提交:生成提车应收款后收费明细未修改或仅保存,且未提交审批;操作可:查看、收费明细;\n 3.9.2.审批中:生成提车应收款后收费明细已提交审批,但未完成最终节点审批;操作可:查看、撤回;\n 3.9.3.审批完成:提车应收款已完成最终节点审批,最终节点审批后不可撤回;操作可:查看、开票信息;\n 3.9.4.审批驳回:提车应收款在任意审批节点被驳回,被驳回后显示为审批驳回,可重新编辑后再次提交审批;操作可:查看、收费明细;\n 3.9.5.撤回:提车应收款在状态为审批中时可自行撤回,撤回后显示为撤回,可重新编辑后再次提交审批;操作可:查看、收费明细;\n3.10.收费情况操作人:提车应收款中点击收费明细,并点击提交审核的操作人,如撤回或驳回后由其他操作人提交审核,则显示最后一次操作的操作人;\n3.14.收费情况提交时间:提车应收款收费明细最后一次提交时间,格式为:YYYY-MM-DD HH:MM;\n3.15.开票时间:提车应收款完成审批后,在开票信息中填报的开票时间,格式为:YYYY-MM-DD,如未开票则显示:待开票;\n3.16.开票操作人:提车应收款开票信息开票时间、发票附件提交时,记录提交人,如未开票则显示为-;\n3.17.操作:查看、收费明细、撤回、开票信息;\n 3.17.1.查看:点击跳转查看提车应收款页;\n 3.17.2.收费明细:点击跳转收费明细页;\n 3.17.3.撤回:点击弹出气泡卡片进行二次确认,提示内容:是否确认撤回该流程。点击确认后提示撤回成功/失败(撤回过程中已完成最终节点审批则提示撤回失败);\n 3.17.4.开票信息:点击跳转开票信息页;\n3.18.分页:列表右下角为分页功能,支持选择单页显示数据条目数;'; + + // 选项 + var contractCodeOptions = [ + { value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' }, + { value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' }, + { value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' }, + { value: 'HT-ZL-2024-088', label: 'HT-ZL-2024-088' } + ]; + var projectNameOptions = [ + { value: '北京朝阳区租赁项目', label: '北京朝阳区租赁项目' }, + { value: '上海浦东车辆租赁', label: '上海浦东车辆租赁' }, + { value: '广州天河运营项目', label: '广州天河运营项目' } + ]; + var customerNameOptions = [ + { value: '某某科技有限公司', label: '某某科技有限公司' }, + { value: '某某物流有限公司', label: '某某物流有限公司' }, + { value: '某某制造有限公司', label: '某某制造有限公司' } + ]; + var auditStatusOptions = [ + { value: '全部', label: '全部' }, + { value: '待提交', label: '待提交' }, + { value: '审批中', label: '审批中' }, + { value: '审批完成', label: '审批完成' }, + { value: '审批驳回', label: '审批驳回' }, + { value: '撤回', label: '撤回' } + ]; + var operatorOptions = [ + { value: '全部', label: '全部' }, + { value: '张三', label: '张三' }, + { value: '李四', label: '李四' }, + { value: '王五', label: '王五' } + ]; + + var auditStatusMap = { draft: '待提交', auditing: '审批中', completed: '审批完成', rejected: '审批驳回', withdrawn: '撤回' }; + var rawList = useMemo(function() { + var list = []; + var contracts = ['HT-ZL-2025-001', 'HT-ZL-2025-002', 'HT-ZL-2025-003', 'HT-ZL-2024-088']; + var projects = ['北京朝阳区租赁项目', '上海浦东车辆租赁', '广州天河运营项目']; + var customers = ['某某科技有限公司', '某某物流有限公司', '某某制造有限公司']; + var depts = ['运营部', '业务1部', '业务2部']; + var owners = ['张三', '李四', '王五']; + var statuses = ['draft', 'auditing', 'completed', 'rejected', 'withdrawn']; + for (var i = 1; i <= 25; i++) { + var audit = statuses[i % 5]; + list.push({ + id: 'AR' + i, + contractCode: contracts[i % contracts.length], + projectName: projects[i % projects.length], + customerName: customers[i % customers.length], + department: depts[i % 3], + responsible: owners[i % 3], + effectiveDate: '2025-' + (String((i % 12) + 1)).padStart(2, '0') + '-' + (String((i % 28) + 1)).padStart(2, '0'), + receivableTotal: (12000 + i * 500).toFixed(2), + discountTotal: (i % 4 === 0 ? 200 : 0).toFixed(2), + auditStatus: audit, + chargeOperator: audit === 'draft' || audit === 'withdrawn' ? '-' : '张三', + chargeSubmitTime: audit === 'draft' || audit === 'withdrawn' ? '-' : '2025-02-20 14:30', + invoiceTime: audit === 'completed' ? '2025-02-25' : '待开票', + invoiceOperator: audit === 'completed' ? '李四' : '-' + }); + } + return list; + }, []); + + var appliedFilter = _appliedFilter[0]; + var filteredList = useMemo(function() { + var list = rawList.slice(); + var f = appliedFilter; + if (f.contractCode) list = list.filter(function(r) { return r.contractCode === f.contractCode; }); + if (f.projectName) list = list.filter(function(r) { return r.projectName === f.projectName; }); + if (f.customerName) list = list.filter(function(r) { return r.customerName === f.customerName; }); + if (f.auditStatus && f.auditStatus !== '全部') { + var statusKey = null; + for (var k in auditStatusMap) { if (auditStatusMap[k] === f.auditStatus) { statusKey = k; break; } } + if (statusKey) list = list.filter(function(r) { return r.auditStatus === statusKey; }); + } + if (f.chargeOperator && f.chargeOperator !== '全部') { + list = list.filter(function(r) { return r.chargeOperator === f.chargeOperator; }); + } + if (f.chargeTimeRange && f.chargeTimeRange.length === 2 && f.chargeTimeRange[0] && f.chargeTimeRange[1]) { + var startStr = f.chargeTimeRange[0].format ? f.chargeTimeRange[0].format('YYYY-MM-DD') : ''; + var endStr = f.chargeTimeRange[1].format ? f.chargeTimeRange[1].format('YYYY-MM-DD') : ''; + if (startStr || endStr) { + list = list.filter(function(r) { + var t = (r.chargeSubmitTime || '').split(' ')[0]; + if (!t || t === '-') return false; + if (startStr && t < startStr) return false; + if (endStr && t > endStr) return false; + return true; + }); + } + } + if (f.invoiceTimeRange && f.invoiceTimeRange.length === 2 && f.invoiceTimeRange[0] && f.invoiceTimeRange[1]) { + var isStart = f.invoiceTimeRange[0].format ? f.invoiceTimeRange[0].format('YYYY-MM-DD') : ''; + var ieEnd = f.invoiceTimeRange[1].format ? f.invoiceTimeRange[1].format('YYYY-MM-DD') : ''; + if (isStart || ieEnd) { + list = list.filter(function(r) { + var t = r.invoiceTime === '待开票' ? '' : r.invoiceTime; + if (!t) return false; + if (isStart && t < isStart) return false; + if (ieEnd && t > ieEnd) return false; + return true; + }); + } + } + if (f.invoiceOperator && f.invoiceOperator !== '全部') { + list = list.filter(function(r) { return r.invoiceOperator === f.invoiceOperator; }); + } + return list; + }, [rawList, appliedFilter]); + + var handleQuery = useCallback(function() { + _appliedFilter[1]({ + contractCode: _contractCode[0], + projectName: _projectName[0], + customerName: _customerName[0], + auditStatus: _auditStatus[0], + chargeOperator: _chargeOperator[0], + chargeTimeRange: _chargeTimeRange[0], + invoiceTimeRange: _invoiceTimeRange[0], + invoiceOperator: _invoiceOperator[0] + }); + }, []); + var handleReset = useCallback(function() { + _contractCode[1](undefined); + _projectName[1](undefined); + _customerName[1](undefined); + _auditStatus[1](undefined); + _chargeOperator[1](undefined); + _chargeTimeRange[1](null); + _invoiceTimeRange[1](null); + _invoiceOperator[1](undefined); + _appliedFilter[1]({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + auditStatus: undefined, + chargeOperator: undefined, + chargeTimeRange: null, + invoiceTimeRange: null, + invoiceOperator: undefined + }); + _selectedRowKeys[1]([]); + }, []); + var handleExport = useCallback(function() { + var keys = _selectedRowKeys[0].length > 0 ? _selectedRowKeys[0] : filteredList.map(function(r) { return r.id; }); + message.info('导出已选数据,共 ' + keys.length + ' 条'); + }, [_selectedRowKeys[0], filteredList]); + var handleWithdraw = useCallback(function(record) { + _withdrawRecord[1](record); + _withdrawModalVisible[1](true); + }, []); + var handleWithdrawOk = useCallback(function() { + message.success('撤回成功'); + _withdrawModalVisible[1](false); + _withdrawRecord[1](null); + }, []); + + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + + var filterItems = [ + React.createElement('div', { key: 'contractCode', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同编码'), + React.createElement(Select, { + placeholder: '请选择或输入合同编码', + style: filterControlStyle, + value: _contractCode[0], + onChange: function(v) { _contractCode[1](v); }, + allowClear: true, + showSearch: true, + options: contractCodeOptions, + filterOption: function(input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } + })), + React.createElement('div', { key: 'projectName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '项目名称'), + React.createElement(Select, { + placeholder: '请选择或输入项目名称', + style: filterControlStyle, + value: _projectName[0], + onChange: function(v) { _projectName[1](v); }, + allowClear: true, + showSearch: true, + options: projectNameOptions, + filterOption: function(input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } + })), + React.createElement('div', { key: 'customerName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请选择或输入客户名称', + style: filterControlStyle, + value: _customerName[0], + onChange: function(v) { _customerName[1](v); }, + allowClear: true, + showSearch: true, + options: customerNameOptions, + filterOption: function(input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } + })), + React.createElement('div', { key: 'auditStatus', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '审核状态'), + React.createElement(Select, { + placeholder: '请选择', + style: filterControlStyle, + value: _auditStatus[0], + onChange: function(v) { _auditStatus[1](v); }, + allowClear: true, + options: auditStatusOptions + })), + React.createElement('div', { key: 'chargeOperator', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '收费情况操作人'), + React.createElement(Select, { + placeholder: '请选择或输入操作人', + style: filterControlStyle, + value: _chargeOperator[0], + onChange: function(v) { _chargeOperator[1](v); }, + allowClear: true, + showSearch: true, + options: operatorOptions, + filterOption: function(input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } + })), + React.createElement('div', { key: 'chargeTimeRange', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '收费情况提交时间'), + React.createElement(RangePicker, { + style: filterControlStyle, + placeholder: ['请选择开始时间', '请选择结束时间'], + value: _chargeTimeRange[0], + onChange: function(v) { _chargeTimeRange[1](v); } + })), + React.createElement('div', { key: 'invoiceTimeRange', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '开票时间'), + React.createElement(RangePicker, { + style: filterControlStyle, + placeholder: ['请选择开始时间', '请选择结束时间'], + value: _invoiceTimeRange[0], + onChange: function(v) { _invoiceTimeRange[1](v); } + })), + React.createElement('div', { key: 'invoiceOperator', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '开票操作人'), + React.createElement(Select, { + placeholder: '请选择或输入操作人', + style: filterControlStyle, + value: _invoiceOperator[0], + onChange: function(v) { _invoiceOperator[1](v); }, + allowClear: true, + showSearch: true, + options: operatorOptions, + filterOption: function(input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } + })) + ]; + + var columns = [ + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 130, fixed: 'left' }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160 }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140 }, + { title: '业务部门', dataIndex: 'department', key: 'department', width: 100 }, + { title: '业务负责人', dataIndex: 'responsible', key: 'responsible', width: 100 }, + { title: '合同生效日期', dataIndex: 'effectiveDate', key: 'effectiveDate', width: 120 }, + { title: '应收总金额', key: 'receivableTotal', width: 110, render: function(_, r) { return (r.receivableTotal || '0.00') + ' 元'; } }, + { title: '减免总金额', key: 'discountTotal', width: 110, render: function(_, r) { return (r.discountTotal || '0.00') + ' 元'; } }, + { title: '审核状态', key: 'auditStatus', width: 100, render: function(_, r) { return auditStatusMap[r.auditStatus] || '-'; } }, + { title: '收费情况操作人', dataIndex: 'chargeOperator', key: 'chargeOperator', width: 120 }, + { title: '收费情况提交时间', dataIndex: 'chargeSubmitTime', key: 'chargeSubmitTime', width: 160 }, + { title: '开票时间', dataIndex: 'invoiceTime', key: 'invoiceTime', width: 110 }, + { title: '开票操作人', dataIndex: 'invoiceOperator', key: 'invoiceOperator', width: 110 }, + { + title: '操作', + key: 'action', + width: 200, + fixed: 'right', + render: function(_, record) { + var audit = record.auditStatus; + var showCharge = audit === 'draft' || audit === 'rejected' || audit === 'withdrawn'; + var showWithdraw = audit === 'auditing'; + var showInvoice = audit === 'completed'; + return React.createElement(Space, { size: 4, wrap: true }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('跳转查看提车应收款页'); } }, '查看'), + showCharge ? React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('跳转收费明细页'); } }, '收费明细') : null, + showWithdraw ? React.createElement(Button, { type: 'link', size: 'small', onClick: function() { handleWithdraw(record); } }, '撤回') : null, + showInvoice ? React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('跳转开票信息页'); } }, '开票信息') : null + ); + } + } + ]; + + // 默认全选当前页:在 pagination 变化时把当前页 id 加入 selectedRowKeys(仅做初始化倾向,实际由 Table 受控) + // 筛选结果变化后默认全选(查询/重置后与列表联动) + React.useEffect(function() { + _selectedRowKeys[1](filteredList.map(function(r) { return r.id; })); + }, [filteredList]); + + var rowSelection = { + selectedRowKeys: _selectedRowKeys[0], + onChange: function(keys) { _selectedRowKeys[1](keys); }, + getCheckboxProps: function(record) { return { name: record.id }; }, + fixed: true + }; + + // 筛选默认第一行(3 项),展开显示全部 8 项 + var filterCount = _filterExpanded[0] ? 8 : 3; + var filterNodes = []; + for (var fi = 0; fi < filterCount && fi < filterItems.length; fi++) { + filterNodes.push(filterItems[fi]); + } + + // 多选选中时列表行不添加选中样式(选中行与未选中行背景一致) + var tableRowClassStyle = '.receivable-list-table .ant-table-tbody .ant-table-row-selected > td { background: #fff !important; }'; + + return React.createElement(App, null, + 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, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start' + } + }, filterNodes), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '更多') + ) + ), + React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: handleExport }, '导出') + ), + React.createElement(Card, null, + React.createElement(React.Fragment, null, + React.createElement('style', null, tableRowClassStyle), + React.createElement('div', { className: 'receivable-list-table' }, + React.createElement(Table, { + rowKey: 'id', + rowSelection: rowSelection, + columns: columns, + dataSource: filteredList, + scroll: { x: 1800 }, + size: 'small', + pagination: { + showSizeChanger: true, + showQuickJumper: true, + showTotal: function(t) { return '共 ' + t + ' 条'; } + } + }) + ) + ) + ), + React.createElement(Modal, { + title: '提示', + open: _withdrawModalVisible[0], + onCancel: function() { _withdrawModalVisible[1](false); _withdrawRecord[1](null); }, + onOk: handleWithdrawOk, + okText: '确认', + cancelText: '取消' + }, React.createElement('div', { style: { fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, '是否确认撤回该流程?')), + 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', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementContent)) + ) + ); +}; diff --git a/web端/财务管理/提车收款-收费明细-业务提交.jsx b/web端/财务管理/提车收款-收费明细-业务提交.jsx deleted file mode 100644 index a393423..0000000 --- a/web端/财务管理/提车收款-收费明细-业务提交.jsx +++ /dev/null @@ -1,461 +0,0 @@ -// 【重要】必须使用 const Component 作为组件变量名 -// 提车应收款 - 收费明细(租赁费用管理-提车应收款,第一步由业务填写) - -const Component = function() { - var useState = React.useState; - var useCallback = React.useCallback; - var useMemo = React.useMemo; - - // 模拟:项目信息(只读) - var mockProject = useMemo(function() { - return { - contractCode: 'HT-ZL-2025-001', - contractType: '正式合同', - projectName: '北京朝阳区租赁项目', - customerName: '某某科技有限公司', - paymentMethod: '预付', - paymentCycle: '1个月', - department: '运营部', - responsible: '张三' - }; - }, []); - - // 模拟:车辆列表(含服务费项),用于初始化可编辑账单行 - var mockVehicleList = useMemo(function() { - return [ - { brand: '奔驰', model: 'E300L', plateNo: '京A12345', monthlyRent: '8000.00', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }] }, - { brand: '宝马', model: '530Li', plateNo: '京B67890', monthlyRent: '7000.00', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }] } - ]; - }, []); - - // 模拟:氢费配置(租赁合同:客户承担+预付款时才有氢费预付款应收) - var mockHydrogen = useMemo(function() { - return { - bearer: '客户', // 客户承担 | 其他 - paymentMethod: '预付', // 预付 | 后付 - prepaymentAmount: '3580.00' // 氢气预付款金额 - }; - }, []); - - // 模拟:开票信息(只读) - var mockInvoice = useMemo(function() { - return { - customerName: '某某科技有限公司', - taxId: '91330400MA2XXXXX1', - address: '浙江省嘉兴市南湖区科技大道1号', - phone: '0571-88888888', - account: '6222021234567890123', - bank: '中国工商银行嘉兴分行', - mailingAddress: '浙江省嘉兴市南湖区科技大道1号' - }; - }, []); - - // 可编辑提车应收款行:应收车辆租金、车辆租金备注、应收保证金(只读)、服务费项(应收费用/备注可编辑)、减免金额、减免金额备注 - var vehicleRowsState = useState([]); - var vehicleRows = vehicleRowsState[0]; - var setVehicleRows = vehicleRowsState[1]; - // 服务费弹窗:当前打开的行索引,null 表示关闭 - var serviceModalRowState = useState(null); - var serviceModalRow = serviceModalRowState[0]; - var setServiceModalRow = serviceModalRowState[1]; - // 查看需求说明弹窗 - var requirementVisibleState = useState(false); - var requirementVisible = requirementVisibleState[0]; - var setRequirementVisible = requirementVisibleState[1]; - // 取消二次确认弹窗 - var cancelConfirmVisibleState = useState(false); - var cancelConfirmVisible = cancelConfirmVisibleState[0]; - var setCancelConfirmVisible = cancelConfirmVisibleState[1]; - - // 需求说明文案(#提车应收款-收款明细) - var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第一步由业务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成;\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期:显示付款周期,类型有1个月-12个月;\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额:显示总金额,格式为xx.xx元,计算方式为:所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.品牌:显示租赁合同中对应品牌;\n2.2.2.型号:显示租赁合同中对应型号;\n2.2.3.车牌号:显示租赁合同中对应车牌号,如无则显示为-;\n2.2.4.应收车辆租金:必填项,默认显示1个月车辆租金,按照合同车辆月租金反写,可修改;\n2.2.5.车辆租金备注:选填项,输入框,可自行备注车辆租金情况;\n2.2.6.应收保证金:必填项,默认显示应收车辆保证金,按照租赁合同保证金反写,不可修改;\n2.2.7.服务费项目:点击管理,弹出卡片,卡片中为列表,列表显示租赁合同所有已填写服务项目,不支持新增服务项目,列表字段为:服务项目、应付费用、服务费用备注;\n 2.2.8.1.服务项目:显示所有租赁合同中已添加的服务项目名称;\n 2.2.8.2.应收费用:必填项,默认拉取所有租赁合同中已添加的服务项目对应费用金额,可修改;\n 2.2.8.3.服务费用备注:选填项,输入框,可自行输入;\n2.1.8.应收服务费:根据服务费项目所有服务项目应付费用计算总和得出,不可修改;\n2.1.9.减免金额:选填项,默认为0,可修改,2位小数;\n2.1.10.减免金额备注:选填项,输入框,可自行备注减免情况;\n\n3.氢费预付款:\n3.1.上方为氢费预付款应收总额/氢费预付款应收金额/减免金额/减免金额备注;\n3.1.1.氢费应收总额:显示总金额,格式为xx.xx元,计算方式为:氢费预付款实付总额-减免金额;\n3.1.2.氢费预付款应收金额:必填项,输入框,支持两位小数,格式为xx.xx元,默认为与租赁合同氢气预付款金额相同;当租赁合同氢费设置为客户承担、预付款时才有,与氢气预付款金额相同,其他费用模式下,氢气预付款为0;\n3.1.2.减免金额:必填项,输入框,支持两位小数,格式为xx.xx元,默认为0.00元;\n3.1.3.减免金额备注:选填项,输入框,可自行输入;\n\n4.开票信息:\n4.1.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.之后为表单,显示客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间、发票附件、备注:\n4.2.1.客户名称:显示租赁合同客户名称;\n4.2.2.纳税人识别号:显示租赁合同对应客户纳税人识别号;\n4.2.3.地址:显示租赁合同对应客户地址;\n4.2.4.电话:显示租赁合同对应客户电话;\n4.2.5.账户:显示租赁合同对应客户账户;\n4.2.6.开户行:显示租赁合同对应客户开户行;\n4.2.7.邮寄地址:显示租赁合同对应客户邮寄地址;\n4.2.8.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.9.开票时间:显示为待开票;\n4.2.10.发票附件:显示为待上传;\n4.2.11.备注:显示为无;\n\n5.页面底部为提交、取消;\n5.1.提交审核:点击提交审核,该条记录移至审核中心,同时提车应收款列表中该条记录审核状态标记为:待审核;\n5.2.取消:点击取消,进行二次确认气泡提示,提示为:取消将会丢失所有已添加数据,是否确认取消,点击是则返回提车应收款列表页;点击否则关闭二次确认继续操作;\n\n6.审批流程:\n6.1.标准情况(车辆租金、服务费金额≥租赁合同配置的单月租金*付款周期或氢费金额=租赁合同配置的氢费预付款金额):业务->财务主管(吕红/宋欣怡);\n6.2.非标情况(车辆租金、服务费金额<租赁合同配置的单月租金*付款周期或氢费金额<租赁合同配置的氢费预付款金额);业务->财务主管(吕红/宋欣怡)->更上级领导(待定,可能是董事长),流程请待确认完成之后,再进行设置;'; - - // 氢费预付款表单:应收金额(必填,默认=合同氢气预付款金额)、减免金额、减免金额备注;应收总额=应收金额-减免金额(只读) - var hydrogenFormState = useState(function() { - var isCustomerPrepay = (mockHydrogen.bearer === '客户' && mockHydrogen.paymentMethod === '预付'); - var paid = isCustomerPrepay ? (mockHydrogen.prepaymentAmount || '0.00') : '0.00'; - return { paidTotal: paid, discount: '0.00', discountRemark: '' }; - }); - var hydrogenForm = hydrogenFormState[0]; - var setHydrogenForm = hydrogenFormState[1]; - // 氢费应收总额 = 应收金额 - 减免金额(只读显示) - var hydrogenPayableTotal = useMemo(function() { - var paid = parseFloat(hydrogenForm.paidTotal) || 0; - var discount = parseFloat(hydrogenForm.discount) || 0; - var v = paid - discount; - return (v >= 0 ? v : 0).toFixed(2); - }, [hydrogenForm.paidTotal, hydrogenForm.discount]); - - function formatTwoDecimals(val) { - var n = parseFloat(String(val).replace(/[^\d.-]/g, '')); - if (isNaN(n)) return '0.00'; - return n.toFixed(2); - } - - React.useEffect(function() { - var rows = mockVehicleList.map(function(v) { - var serviceItems = (v.serviceItems || []).map(function(s) { - return { name: s.name, fee: s.fee || '', remark: s.remark || '' }; - }); - return { - brand: v.brand, - model: v.model, - plateNo: v.plateNo || '-', - payableRent: v.monthlyRent || '', - rentRemark: '', - deposit: v.deposit || '', - depositRemark: '', - serviceItems: serviceItems, - discount: '0', - discountRemark: '' - }; - }); - setVehicleRows(rows); - }, [mockVehicleList]); - - // 单行应收服务费 = 该行服务费项应收费用之和 - function getRowServiceTotal(row) { - var total = 0; - var list = row.serviceItems || []; - for (var i = 0; i < list.length; i++) { - var n = parseFloat(list[i].fee); - if (!isNaN(n)) total += n; - } - return total.toFixed(2); - } - // 提车应收款总额 = 所有应收车辆租金 + 所有应收保证金 + 所有应收服务费 - 所有减免金额 - var totalPayable = useMemo(function() { - var rentSum = 0, depositSum = 0, serviceSum = 0, discountSum = 0; - for (var i = 0; i < vehicleRows.length; i++) { - var r = vehicleRows[i]; - rentSum += parseFloat(r.payableRent) || 0; - depositSum += parseFloat(r.deposit) || 0; - serviceSum += parseFloat(getRowServiceTotal(r)) || 0; - discountSum += parseFloat(r.discount) || 0; - } - return (rentSum + depositSum + serviceSum - discountSum).toFixed(2); - }, [vehicleRows]); - - var handleVehicleChange = useCallback(function(rowIndex, field, value) { - setVehicleRows(function(prev) { - var next = prev.slice(); - var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex]; - row[field] = value; - next[rowIndex] = row; - return next; - }); - }, []); - var handleServiceItemChange = useCallback(function(rowIndex, itemIndex, field, value) { - setVehicleRows(function(prev) { - var next = prev.slice(); - var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex]; - var items = (row.serviceItems || []).slice(); - var item = items[itemIndex] ? Object.assign({}, items[itemIndex]) : items[itemIndex]; - item[field] = value; - items[itemIndex] = item; - row.serviceItems = items; - next[rowIndex] = row; - return next; - }); - }, []); - - var handleSubmit = useCallback(function() { - var err = []; - for (var i = 0; i < vehicleRows.length; i++) { - var r = vehicleRows[i]; - if (!r.payableRent || String(r.payableRent).trim() === '') err.push('第' + (i + 1) + '行应收车辆租金'); - if (!r.deposit || String(r.deposit).trim() === '') err.push('第' + (i + 1) + '行应收保证金'); - var items = r.serviceItems || []; - for (var j = 0; j < items.length; j++) { - if (!items[j].fee || String(items[j].fee).trim() === '') err.push('第' + (i + 1) + '行服务费项「' + (items[j].name || '') + '」应收费用'); - } - } - if (!hydrogenForm.paidTotal || String(hydrogenForm.paidTotal).trim() === '') err.push('氢费预付款应收金额'); - if (!hydrogenForm.discount || String(hydrogenForm.discount).trim() === '') err.push('氢费减免金额'); - if (err.length > 0) { - alert('请填写必填项:' + err.join('、')); - return; - } - alert('提交成功,该条记录已移至审核中心,审核状态为待审核'); - }, [vehicleRows, hydrogenForm]); - var handleCancelClick = useCallback(function() { - setCancelConfirmVisible(true); - }, []); - var handleCancelConfirm = useCallback(function(confirmed) { - setCancelConfirmVisible(false); - if (confirmed) { - alert('已返回提车应收款列表'); - } - }, []); - - var styles = useMemo(function() { - return { - page: { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }, - breadcrumb: { marginBottom: 16, fontSize: 14, color: '#666' }, - card: { background: '#fff', borderRadius: 8, padding: 24, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' }, - cardTitle: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' }, - detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: 12, fontSize: 14 }, - formCol: { display: 'flex', flexDirection: 'column', gap: 4 }, - formLabel: { color: '#666', fontSize: 14 }, - formValue: { color: '#333', fontSize: 14 }, - requiredMark: { color: '#ff4d4f', marginRight: 4 }, - table: { width: '100%', borderCollapse: 'collapse', fontSize: 14 }, - th: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' }, - td: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333' }, - formInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, width: '100%', maxWidth: 140 }, - linkBtn: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 }, - totalLine: { marginBottom: 16, fontSize: 14 }, - totalAmount: { color: '#1890ff', fontWeight: 600, fontSize: 16 }, - footer: { marginTop: 24, display: 'flex', justifyContent: 'center', gap: 12 }, - primaryBtn: { padding: '8px 24px', backgroundColor: '#1890ff', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 14 }, - defaultBtn: { padding: '8px 24px', backgroundColor: '#fff', color: '#666', border: '1px solid #d9d9d9', borderRadius: 4, cursor: 'pointer', fontSize: 14 }, - modalOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }, - modalCard: { backgroundColor: '#fff', borderRadius: 8, padding: 24, maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' }, - modalHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }, - modalTitle: { fontSize: 16, fontWeight: 600 }, - modalClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: 4, cursor: 'pointer' }, - modalBody: { maxHeight: '60vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6, color: '#333' }, - pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 8 }, - requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: 14, textDecoration: 'none', background: 'none', border: 'none', padding: 0 } - }; - }, []); - - // 项目信息卡片(3列) - var projectCard = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardTitle }, '项目信息'), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同编码'), React.createElement('div', { style: styles.formValue }, mockProject.contractCode)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同类型'), React.createElement('div', { style: styles.formValue }, mockProject.contractType)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '项目名称'), React.createElement('div', { style: styles.formValue }, mockProject.projectName)) - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockProject.customerName)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款方式'), React.createElement('div', { style: styles.formValue }, mockProject.paymentMethod)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款周期'), React.createElement('div', { style: styles.formValue }, mockProject.paymentCycle)) - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务部门'), React.createElement('div', { style: styles.formValue }, mockProject.department)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务负责人'), React.createElement('div', { style: styles.formValue }, mockProject.responsible)), - React.createElement('div', { style: styles.formCol }, null) - ) - ); - - // 提车应收款表头 - var vehicleTableHeader = React.createElement('thead', null, - React.createElement('tr', null, - React.createElement('th', { style: styles.th }, '品牌'), - React.createElement('th', { style: styles.th }, '型号'), - React.createElement('th', { style: styles.th }, '车牌号'), - React.createElement('th', { style: styles.th }, React.createElement('span', { style: styles.requiredMark }, '*'), '应收车辆租金'), - React.createElement('th', { style: styles.th }, '车辆租金备注'), - React.createElement('th', { style: styles.th }, React.createElement('span', { style: styles.requiredMark }, '*'), '应收保证金'), - React.createElement('th', { style: styles.th }, '服务费项目'), - React.createElement('th', { style: styles.th }, '应收服务费'), - React.createElement('th', { style: styles.th }, '减免金额'), - React.createElement('th', { style: styles.th }, '减免金额备注') - ) - ); - - var vehicleTableBody = React.createElement('tbody', null, - vehicleRows.map(function(row, rowIndex) { - var serviceTotal = getRowServiceTotal(row); - return React.createElement('tr', { key: rowIndex }, - React.createElement('td', { style: styles.td }, row.brand), - React.createElement('td', { style: styles.td }, row.model), - React.createElement('td', { style: styles.td }, row.plateNo), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: styles.formInput, value: row.payableRent, onChange: function(e) { handleVehicleChange(rowIndex, 'payableRent', e.target.value); } }) - ), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: Object.assign({}, styles.formInput, { maxWidth: 120 }), value: row.rentRemark, onChange: function(e) { handleVehicleChange(rowIndex, 'rentRemark', e.target.value); }, placeholder: '选填' }) - ), - React.createElement('td', { style: styles.td }, row.deposit), - React.createElement('td', { style: styles.td }, - React.createElement('button', { type: 'button', style: styles.linkBtn, onClick: function() { setServiceModalRow(rowIndex); } }, '管理') - ), - React.createElement('td', { style: styles.td }, serviceTotal), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: styles.formInput, value: row.discount, onChange: function(e) { handleVehicleChange(rowIndex, 'discount', e.target.value); }, placeholder: '0' }) - ), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: Object.assign({}, styles.formInput, { maxWidth: 120 }), value: row.discountRemark, onChange: function(e) { handleVehicleChange(rowIndex, 'discountRemark', e.target.value); }, placeholder: '选填' }) - ) - ); - }) - ); - - var vehicleBillCard = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardTitle }, '提车应收款'), - React.createElement('div', { style: styles.totalLine }, - React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'), - React.createElement('span', { style: styles.totalAmount }, totalPayable + ' 元') - ), - React.createElement('table', { style: styles.table }, vehicleTableHeader, vehicleTableBody) - ); - - // 氢费预付款卡片:第一行应收总额(只读,=应收金额-减免);第二行应收金额/减免金额/减免金额备注 - var hydrogenCard = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardTitle }, '氢费预付款'), - React.createElement('div', { style: styles.totalLine }, - React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'), - React.createElement('span', { style: styles.totalAmount }, hydrogenPayableTotal + ' 元') - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, - React.createElement('div', { style: styles.formLabel }, React.createElement('span', { style: styles.requiredMark }, '*'), '氢费预付款应收金额'), - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement('input', { - type: 'text', - style: styles.formInput, - value: hydrogenForm.paidTotal, - onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: e.target.value }); }); }, - onBlur: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: formatTwoDecimals(prev.paidTotal) }); }); }, - placeholder: '0.00' - }), - React.createElement('span', { style: { fontSize: 14, color: '#333' } }, '元') - ) - ), - React.createElement('div', { style: styles.formCol }, - React.createElement('div', { style: styles.formLabel }, React.createElement('span', { style: styles.requiredMark }, '*'), '减免金额'), - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement('input', { - type: 'text', - style: styles.formInput, - value: hydrogenForm.discount, - onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: e.target.value }); }); }, - onBlur: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: formatTwoDecimals(prev.discount) }); }); }, - placeholder: '0.00' - }), - React.createElement('span', { style: { fontSize: 14, color: '#333' } }, '元') - ) - ), - React.createElement('div', { style: styles.formCol }, - React.createElement('div', { style: styles.formLabel }, '减免金额备注'), - React.createElement('input', { - type: 'text', - style: styles.formInput, - value: hydrogenForm.discountRemark, - onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discountRemark: e.target.value }); }); }, - placeholder: '选填' - }) - ) - ) - ); - - // 开票金额 = 提车应收款总额 + 氢费应收总额 - var invoiceAmount = useMemo(function() { - var a = parseFloat(totalPayable) || 0; - var b = parseFloat(hydrogenPayableTotal) || 0; - return (a + b).toFixed(2); - }, [totalPayable, hydrogenPayableTotal]); - - // 开票信息卡片:第1行开票金额;第2行起客户名称等表单;最后一行开票时间/发票附件/备注 - var invoiceCard = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardTitle }, '开票信息'), - React.createElement('div', { style: styles.totalLine }, - React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'), - React.createElement('span', { style: styles.totalAmount }, invoiceAmount + ' 元') - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockInvoice.customerName)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '纳税人识别号'), React.createElement('div', { style: styles.formValue }, mockInvoice.taxId)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.address)) - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '电话'), React.createElement('div', { style: styles.formValue }, mockInvoice.phone)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '账户'), React.createElement('div', { style: styles.formValue }, mockInvoice.account)), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开户行'), React.createElement('div', { style: styles.formValue }, mockInvoice.bank)) - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '邮寄地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.mailingAddress)), - React.createElement('div', { style: styles.formCol }, null), - React.createElement('div', { style: styles.formCol }, null) - ), - React.createElement('div', { style: styles.detailRow }, - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开票时间'), React.createElement('div', { style: styles.formValue }, '待开票')), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '发票附件'), React.createElement('div', { style: styles.formValue }, '待上传')), - React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '备注'), React.createElement('div', { style: styles.formValue }, '无')) - ) - ); - - // 服务费项目弹窗 - var serviceModal = null; - if (serviceModalRow !== null && vehicleRows[serviceModalRow]) { - var r = vehicleRows[serviceModalRow]; - var serviceList = r.serviceItems || []; - serviceModal = React.createElement('div', { style: styles.modalOverlay, onClick: function() { setServiceModalRow(null); } }, - React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, - React.createElement('div', { style: styles.modalHeader }, - React.createElement('span', { style: styles.modalTitle }, '服务费项目'), - React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setServiceModalRow(null); } }, '关闭') - ), - React.createElement('table', { style: styles.table }, - React.createElement('thead', null, - React.createElement('tr', null, - React.createElement('th', { style: styles.th }, '服务项目'), - React.createElement('th', { style: styles.th }, React.createElement('span', { style: styles.requiredMark }, '*'), '应收费用'), - React.createElement('th', { style: styles.th }, '服务费用备注') - ) - ), - React.createElement('tbody', null, - serviceList.map(function(item, itemIndex) { - return React.createElement('tr', { key: itemIndex }, - React.createElement('td', { style: styles.td }, item.name), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: styles.formInput, value: item.fee, onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'fee', e.target.value); } }) - ), - React.createElement('td', { style: styles.td }, - React.createElement('input', { type: 'text', style: Object.assign({}, styles.formInput, { maxWidth: 200 }), value: item.remark, onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'remark', e.target.value); }, placeholder: '选填' }) - ) - ); - }) - ) - ) - ) - ); - } - - var footer = React.createElement('div', { style: styles.footer }, - React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: handleSubmit }, '提交审核'), - React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: handleCancelClick }, '取消') - ); - - var requirementModal = requirementVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setRequirementVisible(false); } }, - React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, - React.createElement('div', { style: styles.modalHeader }, - React.createElement('span', { style: styles.modalTitle }, '需求说明'), - React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setRequirementVisible(false); } }, '关闭') - ), - React.createElement('div', { style: styles.modalBody }, requirementContent) - ) - ) : null; - - var cancelConfirmModal = cancelConfirmVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setCancelConfirmVisible(false); } }, - React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, - React.createElement('div', { style: styles.modalHeader }, - React.createElement('span', { style: styles.modalTitle }, '提示'), - null - ), - React.createElement('div', { style: { marginBottom: 20, fontSize: 14, color: '#333' } }, '取消将会丢失所有已添加数据,是否确认取消?'), - React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 12 } }, - React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: function() { handleCancelConfirm(false); } }, '否'), - React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: function() { handleCancelConfirm(true); } }, '是') - ) - ) - ) : null; - - return React.createElement('div', { style: styles.page }, - React.createElement('div', { style: styles.pageHeader }, - React.createElement('div', { style: styles.breadcrumb }, '租赁费用管理 > 提车应收款 > 收费明细'), - React.createElement('button', { type: 'button', style: styles.requirementLink, onClick: function() { setRequirementVisible(true); } }, '查看需求说明') - ), - projectCard, - vehicleBillCard, - hydrogenCard, - invoiceCard, - footer, - serviceModal, - requirementModal, - cancelConfirmModal - ); -}; diff --git a/web端/财务管理/提车收款-收费明细-财务提交.jsx b/web端/财务管理/提车收款-收费明细-财务提交.jsx new file mode 100644 index 0000000..016e297 --- /dev/null +++ b/web端/财务管理/提车收款-收费明细-财务提交.jsx @@ -0,0 +1,292 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 提车应收款 - 收费明细(租赁费用管理-提车应收款,第二步由财务填写) + +const Component = function() { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + var antd = window.antd; + var DatePicker = antd ? antd.DatePicker : null; + var Input = antd ? antd.Input : null; + var Upload = antd ? antd.Upload : null; + var Button = antd ? antd.Button : null; + + // 模拟:项目信息、车辆(业务提交后数据)、氢费(业务提交后)、开票基础信息 + var mockProject = useMemo(function() { + return { contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '北京朝阳区租赁项目', customerName: '某某科技有限公司', paymentMethod: '预付', paymentCycle: '1个月', department: '运营部', responsible: '张三' }; + }, []); + var mockVehicleList = useMemo(function() { + return [ + { brand: '奔驰', model: 'E300L', plateNo: '京A12345', payableRent: '8000.00', rentRemark: '首月', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }], serviceTotal: '500.00', discount: '0', discountRemark: '' }, + { brand: '宝马', model: '530Li', plateNo: '京B67890', payableRent: '7000.00', rentRemark: '', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }], serviceTotal: '250.00', discount: '0', discountRemark: '' } + ]; + }, []); + var mockHydrogen = useMemo(function() { + return { paidTotal: '3580.00', discount: '0.00', discountRemark: '' }; + }, []); + var mockInvoice = useMemo(function() { + return { customerName: '某某科技有限公司', taxId: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', phone: '0571-88888888', account: '6222021234567890123', bank: '中国工商银行嘉兴分行', mailingAddress: '浙江省嘉兴市南湖区科技大道1号' }; + }, []); + + var totalPayable = useMemo(function() { + var sum = 0; + for (var i = 0; i < mockVehicleList.length; i++) { + var r = mockVehicleList[i]; + sum += parseFloat(r.payableRent) || 0; + sum += parseFloat(r.deposit) || 0; + sum += parseFloat(r.serviceTotal) || 0; + sum -= parseFloat(r.discount) || 0; + } + return sum.toFixed(2); + }, [mockVehicleList]); + var hydrogenPayableTotal = useMemo(function() { + var p = parseFloat(mockHydrogen.paidTotal) || 0; + var d = parseFloat(mockHydrogen.discount) || 0; + return (p - d >= 0 ? p - d : 0).toFixed(2); + }, [mockHydrogen]); + var invoiceAmount = useMemo(function() { + return (parseFloat(totalPayable) + parseFloat(hydrogenPayableTotal)).toFixed(2); + }, [totalPayable, hydrogenPayableTotal]); + + var serviceModalRowState = useState(null); + var serviceModalRow = serviceModalRowState[0]; + var setServiceModalRow = serviceModalRowState[1]; + var cancelConfirmVisibleState = useState(false); + var cancelConfirmVisible = cancelConfirmVisibleState[0]; + var setCancelConfirmVisible = cancelConfirmVisibleState[1]; + var requirementVisibleState = useState(false); + var requirementVisible = requirementVisibleState[0]; + var setRequirementVisible = requirementVisibleState[1]; + + var invoiceFormState = useState({ invoiceTime: '', remark: '', invoiceFiles: [] }); + var invoiceForm = invoiceFormState[0]; + var setInvoiceForm = invoiceFormState[1]; + + var handleSubmit = useCallback(function() { + alert('提交成功,跳转至提车应收款列表页'); + }, []); + var handleCancelClick = useCallback(function() { setCancelConfirmVisible(true); }, []); + var handleCancelConfirm = useCallback(function(confirmed) { + setCancelConfirmVisible(false); + if (confirmed) alert('已返回提车应收款列表'); + }, []); + + var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第二步由财务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成;\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期:显示付款周期,类型有1个月-12个月;\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额:显示总金额,格式为xx.xx元,计算方式为:所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.品牌:显示租赁合同中对应品牌;\n2.2.2.型号:显示租赁合同中对应型号;\n2.2.3.车牌号:显示租赁合同中对应车牌号,如无则显示为-;\n2.2.4.应收车辆租金:显示业务提交的应收车辆租金;\n2.2.5.车辆租金备注:显示业务提交的车辆租金备注;\n2.2.6.应收保证金:显示业务提交的应收车辆保证金;\n2.2.7.服务费项目:点击查看,弹出气泡卡片,卡片中为列表,列表显示租赁合同所有已填写服务项目,列表字段为:服务项目、应收费用、服务费用备注;\n 2.2.8.1.服务项目:显示所有租赁合同中已添加的服务项目名称;\n 2.2.8.2.应收费用:显示所有业务提交的费用金额;\n 2.2.8.3.服务费用备注:显示所有业务提交的备注信息;\n2.1.8.应收服务费:根据服务费项目所有服务项目应付费用计算总和得出,不可修改;\n2.1.9.减免金额:显示业务提交的减免金额,格式为xx.xx元;\n2.1.10.减免金额备注:显示业务提交的减免金额备注信息;\n\n3.氢费预付款:\n3.1.上方为氢费预付款应收总额/氢费预付款应收金额/减免金额/减免金额备注;\n3.1.1.氢费应收总额:显示总金额,格式为xx.xx元,计算方式为:氢费预付款实付总额-减免金额;\n3.1.2.氢费预付款应收金额:显示业务填写的氢费预付款应收金额,格式为xx.xx元;\n3.1.2.减免金额:显示业务填写的减免金额,格式为xx.xx元;\n3.1.3.减免金额备注:显示业务填写的减免金额备注;\n\n\n4.开票信息:\n4.1.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.之后为表单,显示客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间、发票附件、备注:\n4.2.1.客户名称:显示租赁合同客户名称;\n4.2.2.纳税人识别号:显示租赁合同对应客户纳税人识别号;\n4.2.3.地址:显示租赁合同对应客户地址;\n4.2.4.电话:显示租赁合同对应客户电话;\n4.2.5.账户:显示租赁合同对应客户账户;\n4.2.6.开户行:显示租赁合同对应客户开户行;\n4.2.7.邮寄地址:显示租赁合同对应客户邮寄地址;\n4.2.8.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.9.开票时间:选填项,日期选择器,精确至天;;\n4.2.10.发票附件:选填项,附件上传按钮,点击后上传发票附件,支持多附件(满足可能会有多张发票组合的情况);\n4.2.11.备注:选填项,文本域,用于记录财务相关备注;\n\n5.页面底部为提交、取消;\n5.1.提交:点击提交,提示:提交成功,跳转至:提车应收款列表页;\n5.2.取消:点击取消,进行二次确认气泡提示,提示为:取消将会丢失所有已添加数据,是否确认取消,点击是则返回提车应收款列表页;点击否则关闭二次确认继续操作;'; + + var styles = useMemo(function() { + return { + page: { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }, + breadcrumb: { marginBottom: 16, fontSize: 14, color: '#666' }, + card: { background: '#fff', borderRadius: 8, padding: 24, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' }, + cardTitle: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' }, + detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: 12, fontSize: 14 }, + formCol: { display: 'flex', flexDirection: 'column', gap: 4 }, + formLabel: { color: '#666', fontSize: 14 }, + formValue: { color: '#333', fontSize: 14 }, + table: { width: '100%', borderCollapse: 'collapse', fontSize: 14 }, + th: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' }, + td: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333' }, + linkBtn: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 }, + totalLine: { marginBottom: 16, fontSize: 14 }, + totalAmount: { color: '#1890ff', fontWeight: 600, fontSize: 16 }, + footer: { marginTop: 24, display: 'flex', justifyContent: 'center', gap: 12 }, + primaryBtn: { padding: '8px 24px', backgroundColor: '#1890ff', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 14 }, + defaultBtn: { padding: '8px 24px', backgroundColor: '#fff', color: '#666', border: '1px solid #d9d9d9', borderRadius: 4, cursor: 'pointer', fontSize: 14 }, + formInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, width: '100%', maxWidth: 200 }, + modalOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }, + modalCard: { backgroundColor: '#fff', borderRadius: 8, padding: 24, maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' }, + modalHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }, + modalTitle: { fontSize: 16, fontWeight: 600 }, + modalClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: 4, cursor: 'pointer' }, + modalBody: { maxHeight: '60vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6, color: '#333' }, + pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 8 }, + requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: 14, background: 'none', border: 'none', padding: 0 } + }; + }, []); + + var projectCard = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardTitle }, '项目信息'), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同编码'), React.createElement('div', { style: styles.formValue }, mockProject.contractCode)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同类型'), React.createElement('div', { style: styles.formValue }, mockProject.contractType)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '项目名称'), React.createElement('div', { style: styles.formValue }, mockProject.projectName)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockProject.customerName)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款方式'), React.createElement('div', { style: styles.formValue }, mockProject.paymentMethod)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款周期'), React.createElement('div', { style: styles.formValue }, mockProject.paymentCycle)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务部门'), React.createElement('div', { style: styles.formValue }, mockProject.department)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务负责人'), React.createElement('div', { style: styles.formValue }, mockProject.responsible)), + React.createElement('div', { style: styles.formCol }, null) + ) + ); + + var vehicleTableHeader = React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.th }, '品牌'), + React.createElement('th', { style: styles.th }, '型号'), + React.createElement('th', { style: styles.th }, '车牌号'), + React.createElement('th', { style: styles.th }, '应收车辆租金'), + React.createElement('th', { style: styles.th }, '车辆租金备注'), + React.createElement('th', { style: styles.th }, '应收保证金'), + React.createElement('th', { style: styles.th }, '服务费项目'), + React.createElement('th', { style: styles.th }, '应收服务费'), + React.createElement('th', { style: styles.th }, '减免金额'), + React.createElement('th', { style: styles.th }, '减免金额备注') + ) + ); + var vehicleTableBody = React.createElement('tbody', null, + mockVehicleList.map(function(row, rowIndex) { + return React.createElement('tr', { key: rowIndex }, + React.createElement('td', { style: styles.td }, row.brand), + React.createElement('td', { style: styles.td }, row.model), + React.createElement('td', { style: styles.td }, row.plateNo || '-'), + React.createElement('td', { style: styles.td }, row.payableRent), + React.createElement('td', { style: styles.td }, row.rentRemark || '-'), + React.createElement('td', { style: styles.td }, row.deposit), + React.createElement('td', { style: styles.td }, + React.createElement('button', { type: 'button', style: styles.linkBtn, onClick: function() { setServiceModalRow(rowIndex); } }, '查看') + ), + React.createElement('td', { style: styles.td }, row.serviceTotal || '0.00'), + React.createElement('td', { style: styles.td }, (function() { var v = parseFloat(row.discount); return (isNaN(v) ? '0.00' : v.toFixed(2)) + ' 元'; })()), + React.createElement('td', { style: styles.td }, row.discountRemark || '-') + ); + }) + ); + var vehicleBillCard = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardTitle }, '提车应收款'), + React.createElement('div', { style: styles.totalLine }, + React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'), + React.createElement('span', { style: styles.totalAmount }, totalPayable + ' 元') + ), + React.createElement('table', { style: styles.table }, vehicleTableHeader, vehicleTableBody) + ); + + var hydrogenCard = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardTitle }, '氢费预付款'), + React.createElement('div', { style: styles.totalLine }, + React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'), + React.createElement('span', { style: styles.totalAmount }, hydrogenPayableTotal + ' 元') + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '氢费预付款应收金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.paidTotal + ' 元')), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discount + ' 元')), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额备注'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discountRemark || '-')) + ) + ); + + var dayjs = window.dayjs || null; + var invoiceTimeValue = null; + if (invoiceForm.invoiceTime && dayjs) { + var d = dayjs(invoiceForm.invoiceTime, 'YYYY-MM-DD'); + invoiceTimeValue = d && d.isValid && d.isValid() ? d : null; + } + var datePickerEl = DatePicker ? React.createElement(DatePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: '请选择开票日期', + value: invoiceTimeValue, + onChange: function(d, dateStr) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: dateStr || '' }); }); } + }) : React.createElement('input', { type: 'date', style: styles.formInput, value: invoiceForm.invoiceTime, onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: e.target.value }); }); } }); + var uploadButton = Button ? React.createElement(Button, null, '上传附件') : React.createElement('button', { type: 'button', style: styles.defaultBtn }, '上传附件'); + var uploadEl = Upload ? React.createElement(Upload, { + accept: '.pdf', + multiple: true, + fileList: invoiceForm.invoiceFiles || [], + onChange: function(info) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceFiles: info.fileList }); }); }, + beforeUpload: function() { return false; }, + showUploadList: true + }, uploadButton) : React.createElement('div', { style: { fontSize: 14, color: '#999' } }, '(需加载 antd Upload)'); + var remarkEl = Input && Input.TextArea ? React.createElement(Input.TextArea, { + placeholder: '选填,用于记录财务相关备注', + value: invoiceForm.remark || '', + onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); }, + rows: 3, + style: { width: '100%' } + }) : React.createElement('textarea', { style: Object.assign({}, styles.formInput, { minHeight: 60, resize: 'vertical' }), value: invoiceForm.remark || '', onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); }, placeholder: '选填,用于记录财务相关备注' }); + var invoiceCard = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardTitle }, '开票信息'), + React.createElement('div', { style: styles.totalLine }, + React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'), + React.createElement('span', { style: styles.totalAmount }, invoiceAmount + ' 元') + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockInvoice.customerName)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '纳税人识别号'), React.createElement('div', { style: styles.formValue }, mockInvoice.taxId)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.address)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '电话'), React.createElement('div', { style: styles.formValue }, mockInvoice.phone)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '账户'), React.createElement('div', { style: styles.formValue }, mockInvoice.account)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开户行'), React.createElement('div', { style: styles.formValue }, mockInvoice.bank)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '邮寄地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.mailingAddress)), + React.createElement('div', { style: styles.formCol }, null), + React.createElement('div', { style: styles.formCol }, null) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开票时间'), React.createElement('div', { style: { width: '100%' } }, datePickerEl)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '发票附件'), React.createElement('div', { style: { width: '100%' } }, uploadEl)), + React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '备注'), React.createElement('div', { style: { width: '100%' } }, remarkEl)) + ) + ); + + var serviceModal = null; + if (serviceModalRow !== null && mockVehicleList[serviceModalRow]) { + var r = mockVehicleList[serviceModalRow]; + var serviceList = r.serviceItems || []; + serviceModal = React.createElement('div', { style: styles.modalOverlay, onClick: function() { setServiceModalRow(null); } }, + React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, + React.createElement('span', { style: styles.modalTitle }, '服务费项目'), + React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setServiceModalRow(null); } }, '关闭') + ), + React.createElement('table', { style: styles.table }, + React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.th }, '服务项目'), React.createElement('th', { style: styles.th }, '应收费用'), React.createElement('th', { style: styles.th }, '服务费用备注'))), + React.createElement('tbody', null, serviceList.map(function(item, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.td }, item.name), React.createElement('td', { style: styles.td }, item.fee), React.createElement('td', { style: styles.td }, item.remark || '-')); })) + ) + ) + ); + } + + var footer = React.createElement('div', { style: styles.footer }, + React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: handleSubmit }, '提交'), + React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: handleCancelClick }, '取消') + ); + + var requirementModal = requirementVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setRequirementVisible(false); } }, + React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '需求说明'), React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setRequirementVisible(false); } }, '关闭')), + React.createElement('div', { style: styles.modalBody }, requirementContent) + ) + ) : null; + + var cancelConfirmModal = cancelConfirmVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setCancelConfirmVisible(false); } }, + React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '提示'), null), + React.createElement('div', { style: { marginBottom: 20, fontSize: 14, color: '#333' } }, '取消将会丢失所有已添加数据,是否确认取消?'), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 12 } }, + React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: function() { handleCancelConfirm(false); } }, '否'), + React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: function() { handleCancelConfirm(true); } }, '是') + ) + ) + ) : null; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: styles.pageHeader }, + React.createElement('div', { style: styles.breadcrumb }, '租赁费用管理 > 提车应收款 > 收费明细 > 财务提交'), + React.createElement('button', { type: 'button', style: styles.requirementLink, onClick: function() { setRequirementVisible(true); } }, '查看需求说明') + ), + projectCard, + vehicleBillCard, + hydrogenCard, + invoiceCard, + footer, + serviceModal, + requirementModal, + cancelConfirmModal + ); +}; diff --git a/web端/财务管理/提车收款-收费明细.jsx b/web端/财务管理/提车收款-收费明细.jsx new file mode 100644 index 0000000..0f92dc0 --- /dev/null +++ b/web端/财务管理/提车收款-收费明细.jsx @@ -0,0 +1,278 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 提车应收款 - 收费明细(租赁费用管理-提车应收款,第一步由业务填写,antd 规范) + +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 Table = antd.Table; + var Modal = antd.Modal; + var message = antd.message; + var App = antd.App; + + var mockProject = useMemo(function() { + return { contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '北京朝阳区租赁项目', customerName: '某某科技有限公司', paymentMethod: '预付', paymentCycle: '1个月', department: '运营部', responsible: '张三' }; + }, []); + var mockVehicleList = useMemo(function() { + return [ + { brand: '奔驰', model: 'E300L', plateNo: '京A12345', monthlyRent: '8000.00', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }] }, + { brand: '宝马', model: '530Li', plateNo: '京B67890', monthlyRent: '7000.00', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }] } + ]; + }, []); + var mockHydrogen = useMemo(function() { + return { bearer: '客户', paymentMethod: '预付', prepaymentAmount: '3580.00' }; + }, []); + var mockInvoice = useMemo(function() { + return { customerName: '某某科技有限公司', taxId: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', phone: '0571-88888888', account: '6222021234567890123', bank: '中国工商银行嘉兴分行', mailingAddress: '浙江省嘉兴市南湖区科技大道1号' }; + }, []); + + var vehicleRowsState = useState([]); + var vehicleRows = vehicleRowsState[0]; + var setVehicleRows = vehicleRowsState[1]; + var serviceModalRowState = useState(null); + var serviceModalRow = serviceModalRowState[0]; + var setServiceModalRow = serviceModalRowState[1]; + var requirementVisibleState = useState(false); + var requirementVisible = requirementVisibleState[0]; + var setRequirementVisible = requirementVisibleState[1]; + var cancelConfirmVisibleState = useState(false); + var cancelConfirmVisible = cancelConfirmVisibleState[0]; + var setCancelConfirmVisible = cancelConfirmVisibleState[1]; + + var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第一步由业务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成;\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期:显示付款周期,类型有1个月-12个月;\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额:显示总金额,格式为xx.xx元,计算方式为:所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.-2.2.7. 品牌至服务费项目(点击管理弹出卡片);\n2.1.8.应收服务费:根据服务费项目计算总和;\n2.1.9.减免金额:选填,默认为0;\n2.1.10.减免金额备注:选填;\n\n3.氢费预付款:\n3.1.氢费应收总额(=应收金额-减免金额);氢费预付款应收金额、减免金额、减免金额备注;\n\n4.开票信息:\n4.1.开票金额(=提车应收款总额+氢费应收总额);\n4.2.客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间(待开票)、发票附件(待上传)、备注(无);\n\n5.页面底部:提交审核、取消;取消二次确认;\n\n6.审批流程:标准/非标情况说明。'; + + var hydrogenFormState = useState(function() { + var isCustomerPrepay = (mockHydrogen.bearer === '客户' && mockHydrogen.paymentMethod === '预付'); + var paid = isCustomerPrepay ? (mockHydrogen.prepaymentAmount || '0.00') : '0.00'; + return { paidTotal: paid, discount: '0.00', discountRemark: '' }; + }); + var hydrogenForm = hydrogenFormState[0]; + var setHydrogenForm = hydrogenFormState[1]; + var hydrogenPayableTotal = useMemo(function() { + var paid = parseFloat(hydrogenForm.paidTotal) || 0; + var discount = parseFloat(hydrogenForm.discount) || 0; + var v = paid - discount; + return (v >= 0 ? v : 0).toFixed(2); + }, [hydrogenForm.paidTotal, hydrogenForm.discount]); + + function formatTwoDecimals(val) { + var n = parseFloat(String(val).replace(/[^\d.-]/g, '')); + if (isNaN(n)) return '0.00'; + return n.toFixed(2); + } + + React.useEffect(function() { + var rows = mockVehicleList.map(function(v) { + var serviceItems = (v.serviceItems || []).map(function(s) { + return { name: s.name, fee: s.fee || '', remark: s.remark || '' }; + }); + return { + brand: v.brand, + model: v.model, + plateNo: v.plateNo || '-', + payableRent: v.monthlyRent || '', + rentRemark: '', + deposit: v.deposit || '', + serviceItems: serviceItems, + discount: '0', + discountRemark: '' + }; + }); + setVehicleRows(rows); + }, [mockVehicleList]); + + function getRowServiceTotal(row) { + var total = 0; + var list = row.serviceItems || []; + for (var i = 0; i < list.length; i++) { + var n = parseFloat(list[i].fee); + if (!isNaN(n)) total += n; + } + return total.toFixed(2); + } + var totalPayable = useMemo(function() { + var rentSum = 0, depositSum = 0, serviceSum = 0, discountSum = 0; + for (var i = 0; i < vehicleRows.length; i++) { + var r = vehicleRows[i]; + rentSum += parseFloat(r.payableRent) || 0; + depositSum += parseFloat(r.deposit) || 0; + serviceSum += parseFloat(getRowServiceTotal(r)) || 0; + discountSum += parseFloat(r.discount) || 0; + } + return (rentSum + depositSum + serviceSum - discountSum).toFixed(2); + }, [vehicleRows]); + var invoiceAmount = useMemo(function() { + return (parseFloat(totalPayable) + parseFloat(hydrogenPayableTotal)).toFixed(2); + }, [totalPayable, hydrogenPayableTotal]); + + var handleVehicleChange = useCallback(function(rowIndex, field, value) { + setVehicleRows(function(prev) { + var next = prev.slice(); + var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex]; + row[field] = value; + next[rowIndex] = row; + return next; + }); + }, []); + var handleServiceItemChange = useCallback(function(rowIndex, itemIndex, field, value) { + setVehicleRows(function(prev) { + var next = prev.slice(); + var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex]; + var items = (row.serviceItems || []).slice(); + var item = items[itemIndex] ? Object.assign({}, items[itemIndex]) : items[itemIndex]; + item[field] = value; + items[itemIndex] = item; + row.serviceItems = items; + next[rowIndex] = row; + return next; + }); + }, []); + var handleSubmit = useCallback(function() { + var err = []; + for (var i = 0; i < vehicleRows.length; i++) { + var r = vehicleRows[i]; + if (!r.payableRent || String(r.payableRent).trim() === '') err.push('第' + (i + 1) + '行应收车辆租金'); + if (!r.deposit || String(r.deposit).trim() === '') err.push('第' + (i + 1) + '行应收保证金'); + var items = r.serviceItems || []; + for (var j = 0; j < items.length; j++) { + if (!items[j].fee || String(items[j].fee).trim() === '') err.push('第' + (i + 1) + '行服务费项「' + (items[j].name || '') + '」应收费用'); + } + } + if (!hydrogenForm.paidTotal || String(hydrogenForm.paidTotal).trim() === '') err.push('氢费预付款应收金额'); + if (!hydrogenForm.discount || String(hydrogenForm.discount).trim() === '') err.push('氢费减免金额'); + if (err.length > 0) { + message.warning('请填写必填项:' + err.join('、')); + return; + } + message.success('提交成功,该条记录已移至审核中心,审核状态为待审核'); + }, [vehicleRows, hydrogenForm]); + var handleCancelClick = useCallback(function() { setCancelConfirmVisible(true); }, []); + var handleCancelConfirm = useCallback(function(confirmed) { + setCancelConfirmVisible(false); + if (confirmed) message.info('已返回提车应收款列表'); + }, []); + + var layoutStyle = { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }; + var formRowStyle = { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }; + var formColStyle = { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }; + var labelStyle = { display: 'block', marginBottom: 6, color: 'rgba(0,0,0,0.85)' }; + var labelRequiredStyle = { color: '#ff4d4f', marginRight: 4 }; + var totalLineStyle = { marginBottom: 16, fontSize: 14 }; + var totalAmountStyle = { color: '#1890ff', fontWeight: 600, fontSize: 16 }; + var footerStyle = { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }; + + var FormItem = function(props) { + return React.createElement('div', { style: props.colStyle || formColStyle }, + React.createElement('label', { style: labelStyle }, props.required ? React.createElement('span', { style: labelRequiredStyle }, '*') : null, props.label), + props.children + ); + }; + + var projectFields = React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: mockProject.contractCode, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同类型' }, React.createElement(Input, { value: mockProject.contractType, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '项目名称' }, React.createElement(Input, { value: mockProject.projectName, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: mockProject.customerName, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '付款方式' }, React.createElement(Input, { value: mockProject.paymentMethod, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '付款周期' }, React.createElement(Input, { value: mockProject.paymentCycle, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门' }, React.createElement(Input, { value: mockProject.department, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务负责人' }, React.createElement(Input, { value: mockProject.responsible, disabled: true, style: { width: '100%' } })) + ); + + var vehicleColumns = [ + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 90 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '应收车辆租金', key: 'payableRent', width: 120, render: function(_, r, idx) { return React.createElement(Input, { value: r.payableRent, onChange: function(e) { handleVehicleChange(idx, 'payableRent', e.target.value); }, style: { width: '100%' }, addonAfter: '元' }); } }, + { title: '车辆租金备注', key: 'rentRemark', width: 110, render: function(_, r, idx) { return React.createElement(Input, { value: r.rentRemark || '', onChange: function(e) { handleVehicleChange(idx, 'rentRemark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } }, + { title: '应收保证金', dataIndex: 'deposit', key: 'deposit', width: 100 }, + { title: '服务费项目', key: 'service', width: 90, render: function(_, r, idx) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { setServiceModalRow(idx); } }, '管理'); } }, + { title: '应收服务费', key: 'serviceTotal', width: 100, render: function(_, r) { return getRowServiceTotal(r); } }, + { title: '减免金额', key: 'discount', width: 100, render: function(_, r, idx) { return React.createElement(Input, { value: r.discount, onChange: function(e) { handleVehicleChange(idx, 'discount', e.target.value); }, placeholder: '0', style: { width: '100%' } }); } }, + { title: '减免金额备注', key: 'discountRemark', width: 110, render: function(_, r, idx) { return React.createElement(Input, { value: r.discountRemark || '', onChange: function(e) { handleVehicleChange(idx, 'discountRemark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } } + ]; + var vehicleTable = React.createElement(Table, { + rowKey: function(_, i) { return String(i); }, + columns: vehicleColumns, + dataSource: vehicleRows, + pagination: false, + size: 'small', + scroll: { x: 1000 } + }); + + var hydrogenFields = React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '氢费预付款应收金额', required: true }, React.createElement(Input, { value: hydrogenForm.paidTotal, onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: e.target.value }); }); }, onBlur: function() { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: formatTwoDecimals(prev.paidTotal) }); }); }, placeholder: '0.00', addonAfter: '元', style: { width: '100%' } })), + React.createElement(FormItem, { label: '减免金额', required: true }, React.createElement(Input, { value: hydrogenForm.discount, onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: e.target.value }); }); }, onBlur: function() { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: formatTwoDecimals(prev.discount) }); }); }, placeholder: '0.00', addonAfter: '元', style: { width: '100%' } })), + React.createElement(FormItem, { label: '减免金额备注' }, React.createElement(Input, { value: hydrogenForm.discountRemark || '', onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discountRemark: e.target.value }); }); }, placeholder: '选填', style: { width: '100%' } })) + ); + + var invoiceFields = React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: mockInvoice.customerName, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: mockInvoice.taxId, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '地址' }, React.createElement(Input, { value: mockInvoice.address, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '电话' }, React.createElement(Input, { value: mockInvoice.phone, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '账户' }, React.createElement(Input, { value: mockInvoice.account, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户行' }, React.createElement(Input, { value: mockInvoice.bank, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: mockInvoice.mailingAddress, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开票时间' }, React.createElement(Input, { value: '待开票', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '发票附件' }, React.createElement(Input, { value: '待上传', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '备注' }, React.createElement(Input, { value: '无', disabled: true, style: { width: '100%' } })) + ); + + var serviceModalDataSource = serviceModalRow !== null && vehicleRows[serviceModalRow] ? (vehicleRows[serviceModalRow].serviceItems || []) : []; + var serviceModalColumns = [ + { title: '服务项目', dataIndex: 'name', key: 'name', width: 140 }, + { title: '应收费用', key: 'fee', width: 140, render: function(_, item, itemIndex) { return React.createElement(Input, { value: item.fee, onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'fee', e.target.value); }, style: { width: '100%' } }); } }, + { title: '服务费用备注', key: 'remark', width: 180, render: function(_, item, itemIndex) { return React.createElement(Input, { value: item.remark || '', onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'remark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } } + ]; + + var serviceModalOpen = serviceModalRow !== null; + var serviceModalContent = serviceModalOpen ? React.createElement(Table, { rowKey: function(_, i) { return String(i); }, columns: serviceModalColumns, dataSource: serviceModalDataSource, pagination: false, size: 'small' }) : null; + var serviceModalFooter = React.createElement(Button, { onClick: function() { setServiceModalRow(null); } }, '关闭'); + var requirementModalFooter = React.createElement(Button, { onClick: function() { setRequirementVisible(false); } }, '关闭'); + var cancelModalOk = function() { handleCancelConfirm(true); }; + var cancelModalCancel = function() { setCancelConfirmVisible(false); }; + + return React.createElement(App, null, + 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() { setRequirementVisible(true); } }, '查看需求说明') + ), + React.createElement(Card, { title: '项目信息', style: { marginBottom: 16 } }, projectFields), + React.createElement(Card, { title: '提车应收款', style: { marginBottom: 16 } }, + React.createElement('div', { style: totalLineStyle }, + React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'), + React.createElement('span', { style: totalAmountStyle }, totalPayable + ' 元') + ), + vehicleTable + ), + React.createElement(Card, { title: '氢费预付款', style: { marginBottom: 16 } }, + React.createElement('div', { style: totalLineStyle }, + React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'), + React.createElement('span', { style: totalAmountStyle }, hydrogenPayableTotal + ' 元') + ), + hydrogenFields + ), + React.createElement(Card, { title: '开票信息', style: { marginBottom: 80 } }, + React.createElement('div', { style: totalLineStyle }, + React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'), + React.createElement('span', { style: totalAmountStyle }, invoiceAmount + ' 元') + ), + invoiceFields + ), + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交审核'), + React.createElement(Button, { onClick: handleCancelClick }, '取消') + ), + React.createElement(Modal, { title: '服务费项目', open: serviceModalOpen, onCancel: function() { setServiceModalRow(null); }, footer: serviceModalFooter, destroyOnClose: true }, serviceModalContent), + React.createElement(Modal, { title: '需求说明', open: requirementVisible, onCancel: function() { setRequirementVisible(false); }, width: 720, footer: requirementModalFooter, bodyStyle: { maxHeight: '70vh', overflow: 'auto' } }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6 } }, requirementContent)), + React.createElement(Modal, { title: '提示', open: cancelConfirmVisible, onCancel: cancelModalCancel, onOk: cancelModalOk, okText: '是', cancelText: '否' }, React.createElement('div', { style: { fontSize: 14 } }, '取消将会丢失所有已添加数据,是否确认取消?')) + ) + ); +}; diff --git a/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx b/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx new file mode 100644 index 0000000..bbfe74c --- /dev/null +++ b/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx @@ -0,0 +1,457 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 车辆租赁合同 - 变更为三方合同模块(从原合同反写,增加丙方客户选择,IE11+ 兼容,采用 antd) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var message = antd.message; + var Option = Select.Option; + var FormItem = (function() { + return function FormItemComp(props) { + var colStyle = props.fullWidth ? { flex: '0 0 100%', marginBottom: 8 } : { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: { display: 'block', marginBottom: 6, color: '#333' } }, props.required ? React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: { color: '#ff4d4f', fontSize: 12, marginTop: 4 } }, props.error) : null + ); + }; + })(); + + var customerList = [ + { id: '1', name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }, + { id: '2', name: '上海某某运输公司', creditCode: '91310000MA2XXXXX2', address: '上海市浦东新区张江高科技园区', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '上海某某运输公司', companyPhone: '021-66666666', mailingAddress: '上海市浦东新区张江高科技园区', bank: '中国建设银行上海分行', bankAccount: '6217001234567890123', taxId: '91310000MA2XXXXX2' }, + { id: '3', name: '杭州某某供应链有限公司', creditCode: '91330100MA2YYYYY2', address: '浙江省杭州市余杭区未来科技城', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '杭州某某供应链有限公司', companyPhone: '0571-99999999', mailingAddress: '浙江省杭州市余杭区未来科技城', bank: '中国农业银行杭州分行', bankAccount: '6228481234567890123', taxId: '91330100MA2YYYYY2' } + ]; + var deptList = [{ id: 'YW1', name: '业务1部', owners: ['张经理', '李专员', '王专员'] }, { id: 'YW2', name: '业务2部', owners: ['赵经理', '钱专员'] }, { id: 'YW3', name: '业务3部', owners: ['孙经理', '周专员'] }]; + var orgList = ['嘉兴羚牛', '上海羚牛', '广东羚牛']; + var regionList = [ + { province: '浙江省', cities: ['杭州市', '宁波市', '嘉兴市', '湖州市'] }, + { province: '上海市', cities: ['上海市'] }, + { province: '广东省', cities: ['广州市', '深圳市', '东莞市'] } + ]; + var feeTemplates = ['标准费用模板A', '标准费用模板B', '定制费用模板C']; + var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }]; + var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }]; + var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }]; + var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; + + var originalContractCode = 'JXZL20260216YW101235A'; + var prevContractSample = { + customerId: '1', + businessDept: 'YW1', + businessOwner: '张经理', + projectName: '嘉兴氢能运输项目', + contractType: '正式合同', + effectiveDate: '2026-02-16', + paymentMethod: '预付', + endDate: '2027-02-16', + paymentPeriod: '1', + signingCompany: '嘉兴羚牛', + deliveryProvince: '浙江省', + deliveryCity: '嘉兴市', + deliveryLocation: '嘉兴市南湖区科技大道1号', + remarks: '', + authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], + rentalOrders: [ + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }, + { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' } + ], + hydrogenBearer: '客户', + hydrogenPaymentMethod: '预付', + hydrogenPrepay: '5000', + returnHydrogenPrice: '80', + feeTemplate: '标准费用模板A', + billingMethod: '按自然月结算' + }; + var prevCustomer = customerList.find(function(c) { return c.id === prevContractSample.customerId; }) || null; + + var selectedCustomer = React.useState(prevCustomer)[0]; + var setSelectedCustomer = React.useState(prevCustomer)[1]; + var cs4 = React.useState(prevContractSample.businessDept); + var businessDept = cs4[0]; + var setBusinessDept = cs4[1]; + var cs5 = React.useState(prevContractSample.businessOwner); + var businessOwner = cs5[0]; + + var thirdPartyCustomer = React.useState(null)[0]; + var setThirdPartyCustomer = React.useState(null)[1]; + var tpDeptState = React.useState(prevContractSample.businessDept); + var thirdPartyDept = tpDeptState[0]; + var setThirdPartyDept = tpDeptState[1]; + var tpOwnerState = React.useState(prevContractSample.businessOwner); + var thirdPartyOwner = tpOwnerState[0]; + var setThirdPartyOwner = tpOwnerState[1]; + var thirdPartyOwnerFocusError = React.useState('')[0]; + var setThirdPartyOwnerFocusError = React.useState('')[1]; + + var bs1 = React.useState(prevContractSample.projectName); + var projectName = bs1[0]; + var setProjectName = bs1[1]; + var bs6 = React.useState(prevContractSample.endDate); + var endDate = bs6[0]; + var setEndDate = bs6[1]; + var contractOriginalRef = React.useRef(null); + var csContractOriginal = React.useState([ + { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30', isOriginal: true }, + { name: '租赁合同-补充协议.pdf', size: '0.6 MB', uploadTime: '2026-02-16 14:35', isOriginal: true } + ]); + var contractOriginalFiles = csContractOriginal[0]; + var setContractOriginalFiles = csContractOriginal[1]; + + var cc1State = React.useState(false); + var cc1 = cc1State[0]; + var setCc1 = cc1State[1]; + var cc1bState = React.useState(false); + var cc1b = cc1bState[0]; + var setCc1b = cc1bState[1]; + var cc2State = React.useState(false); + var cc2 = cc2State[0]; + var setCc2 = cc2State[1]; + var cc3State = React.useState(false); + var cc3 = cc3State[0]; + var setCc3 = cc3State[1]; + var cc4State = React.useState(false); + var cc4 = cc4State[0]; + var setCc4 = cc4State[1]; + var cc5State = React.useState(false); + var cc5 = cc5State[0]; + var setCc5 = cc5State[1]; + var cc6State = React.useState(false); + var cc6 = cc6State[0]; + var setCc6 = cc6State[1]; + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + var cancelConfirmState = React.useState(false); + var cancelConfirmOpen = cancelConfirmState[0]; + var setCancelConfirmOpen = cancelConfirmState[1]; + var editedState = React.useState(false); + var edited = editedState[0]; + var setEdited = editedState[1]; + var formErrorsState = React.useState({}); + var formErrors = formErrorsState[0]; + var setFormErrors = formErrorsState[1]; + + var currentDept = deptList.find(function(d) { return d.id === businessDept; }); + var thirdPartyDeptObj = deptList.find(function(d) { return d.id === thirdPartyDept; }); + var thirdPartyOwnerOptions = thirdPartyDeptObj ? thirdPartyDeptObj.owners : []; + + var contractCodeDisplay = originalContractCode + '(变更为三方合同)'; + var mainVehicleModelsDisplay = '型号A1、型号A2'; + var deliveryRegionDisplay = prevContractSample.deliveryProvince + ' / ' + prevContractSample.deliveryCity; + var rentalTotalVehicles = prevContractSample.rentalOrders.length; + var rentalTotalRentService = prevContractSample.rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.monthRent) || 0) + (parseFloat(r.serviceFee) || 0); }, 0); + var rentalTotalDeposit = prevContractSample.rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.deposit) || 0); }, 0); + var rentalTotalHydrogen = prevContractSample.hydrogenPaymentMethod === '预付' && prevContractSample.hydrogenBearer === '客户' ? (parseFloat(prevContractSample.hydrogenPrepay) || 0) : 0; + + var selectThirdPartyCustomer = function(c) { + setEdited(true); + setThirdPartyCustomer(c); + }; + var handleThirdPartyOwnerFocus = function() { + if (!thirdPartyDept) setThirdPartyOwnerFocusError('请先选择业务部门'); + else setThirdPartyOwnerFocusError(''); + }; + var scrollToCard = function(id) { + var el = document.getElementById(id); + if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; + var pad2 = function(n) { + var s = String(n); + return s.length === 1 ? ('0' + s) : s; + }; + var addContractOriginalFiles = function(fileList) { + if (!fileList || !fileList.length) return; + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + for (var i = 0; i < fileList.length; i++) { + var f = fileList[i]; + if (!f) continue; + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : (now.getFullYear() + '-' + pad2(now.getMonth() + 1) + '-' + pad2(now.getDate()) + ' ' + pad2(now.getHours()) + ':' + pad2(now.getMinutes())); + next.push({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr, isOriginal: false }); + } + return next; + }); + }; + var removeContractOriginalFile = function(index) { + var f = contractOriginalFiles && contractOriginalFiles[index]; + if (f && f.isOriginal) return; + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + next.splice(index, 1); + return next; + }); + }; + var openContractOriginalFile = function(index) { + var f = contractOriginalFiles && contractOriginalFiles[index]; + if (!f) return; + if (f.file) { + var url = URL.createObjectURL(f.file); + window.open(url); + return; + } + message.info('原合同反写附件(原型),暂无可预览源文件'); + }; + + var validateSubmitAndReview = function() { + var errs = {}; + if (!thirdPartyCustomer) errs.thirdPartyCustomer = '请选择丙方客户'; + if (!thirdPartyDept) errs.thirdPartyDept = '请选择丙方业务部门'; + if (!thirdPartyOwner) errs.thirdPartyOwner = '请选择丙方业务负责人'; + if (!contractOriginalFiles || contractOriginalFiles.length === 0) errs.contractOriginal = '请上传合同原件'; + setFormErrors(errs); + if (Object.keys(errs).length > 0) { + setCc1(false); setCc1b(false); setCc2(false); setCc3(false); setCc4(false); setCc5(false); setCc6(false); + var firstId = errs.contractOriginal ? 'card-contract' : 'card-third-party'; + setTimeout(function() { var el = document.getElementById(firstId); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); + return false; + } + return true; + }; + + 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' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#666', cursor: 'default', border: '1px solid #d9d9d9', borderRadius: 4, padding: '8px 12px', width: '100%', fontSize: 14 }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + 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: 'middle' }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 }, + 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)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.name : '', disabled: true, style: { width: '100%' } })), + 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%' } })), + React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.phone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电子邮箱' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.email : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业名称' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyPhone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.mailingAddress : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门' }, React.createElement(Input, { value: currentDept ? currentDept.name : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务负责人' }, React.createElement(Input, { value: businessOwner || '', disabled: true, style: { width: '100%' } })) + ); + + var thirdPartyOptions = customerList.filter(function(c) { return !selectedCustomer || c.id !== selectedCustomer.id; }).map(function(c) { return React.createElement(Option, { key: c.id, value: c.id }, c.name); }); + var thirdPartyCustomerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称', required: true, error: formErrors.thirdPartyCustomer }, React.createElement(Select, { placeholder: '请选择或输入搜索丙方客户(只显示已审核客户)', style: { width: '100%' }, value: thirdPartyCustomer ? thirdPartyCustomer.id : undefined, onChange: function(id) { var c = customerList.find(function(x) { return x.id === id; }); selectThirdPartyCustomer(c || null); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, status: formErrors.thirdPartyCustomer ? 'error' : undefined }, thirdPartyOptions)), + React.createElement(FormItem, { label: '客户统一信用代码' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.creditCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户地址' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.address : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.contact : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电话' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.phone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电子邮箱' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.email : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业名称' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.companyName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业电话' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.companyPhone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.mailingAddress : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.bank : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: thirdPartyCustomer ? thirdPartyCustomer.taxId : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.thirdPartyDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: thirdPartyDept || undefined, onChange: function(v) { setEdited(true); setThirdPartyDept(v || ''); setThirdPartyOwner(''); }, status: formErrors.thirdPartyDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.thirdPartyOwner || thirdPartyOwnerFocusError }, React.createElement(Select, { placeholder: thirdPartyDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: thirdPartyOwner || undefined, onChange: function(v) { setEdited(true); setThirdPartyOwner(v || ''); setThirdPartyOwnerFocusError(''); }, onFocus: handleThirdPartyOwnerFocus, onBlur: function() { setThirdPartyOwnerFocusError(''); }, disabled: !thirdPartyDept, status: (formErrors.thirdPartyOwner || thirdPartyOwnerFocusError) ? 'error' : undefined }, thirdPartyOwnerOptions.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '项目名称' }, React.createElement(Input, { value: projectName, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCodeDisplay, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同类型' }, React.createElement(Input, { value: prevContractSample.contractType, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '生效日期' }, React.createElement(Input, { value: prevContractSample.effectiveDate, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '付款方式' }, React.createElement(Input, { value: prevContractSample.paymentMethod, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : '—')), + React.createElement(FormItem, { label: '结束日期' }, React.createElement(Input, { value: endDate || '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '付款周期' }, React.createElement(Input, { value: prevContractSample.paymentPeriod + '个月', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '签约公司' }, React.createElement(Input, { value: prevContractSample.signingCompany, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: deliveryRegionDisplay, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: prevContractSample.deliveryLocation, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal, fullWidth: true }, + 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: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: prevContractSample.remarks, disabled: true, style: { width: '100%', minHeight: 80, resize: 'vertical', backgroundColor: '#f5f5f5' }, rows: 4 })) + ); + + var authorizedContent = React.createElement('div', null, + prevContractSample.authorizedList.map(function(item, index) { + return React.createElement('div', { key: index, style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } }, + React.createElement('div', { style: Object.assign({}, styles.inputDisabled, { flex: 1 }) }, item.name), + React.createElement('div', { style: Object.assign({}, styles.inputDisabled, { flex: 1 }) }, item.phone), + React.createElement('div', { style: Object.assign({}, styles.inputDisabled, { flex: 1 }) }, item.idCard) + ); + }) + ); + + var rentalSummaryEl = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalVehicles + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalRentService.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalDeposit.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalHydrogen.toFixed(2) + ' 元')) + ); + var rentalTableBody = prevContractSample.rentalOrders.map(function(row, idx) { + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTd }, idx + 1), + 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.vin)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—')) + ); + }); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'), + 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: 120 }, '车辆月租金'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注') + ) + ), + React.createElement('tbody', null, rentalTableBody) + ); + var hydrogenReadOnly = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '氢费承担方' }, React.createElement(Input, { value: prevContractSample.hydrogenBearer, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '付款方式' }, React.createElement(Input, { value: prevContractSample.hydrogenPaymentMethod, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '氢气预付款' }, React.createElement(Input, { value: prevContractSample.hydrogenPrepay + ' 元', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '退还车氢气单价' }, React.createElement(Input, { value: prevContractSample.returnHydrogenPrice + ' 元', disabled: true, style: { width: '100%' } })) + ); + var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly); + + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.project), React.createElement('td', { style: styles.rentalTd }, r.standard), React.createElement('td', { style: styles.rentalTd }, r.serviceFee)); }; + var makeFeeRow5 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.category), React.createElement('td', { style: styles.rentalTd }, r.part), React.createElement('td', { style: styles.rentalTd }, r.partName), React.createElement('td', { style: styles.rentalTd }, r.qty), React.createElement('td', { style: styles.rentalTd }, r.feeDetail)); }; + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateCertFees.map(makeFeeRow3))); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplatePenaltyFees.map(makeFeeRow3))); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeTemplateConsumables.map(makeFeeRow5))); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateOtherFees.map(makeFeeRow3))); + var feeContent = React.createElement('div', null, + React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择费用模板' }, React.createElement(Input, { value: prevContractSample.feeTemplate, disabled: true, style: { width: '100%' } }))), + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), feeOtherTable + ); + + var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, prevContractSample.billingMethod)); + + var requirementContent = '车辆租赁合同-变更为三方合同(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「变更为三方合同」模块,在车辆租赁合同操作列点击「变更为三方合同」进行操作;\n\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.客户地址:从该条合同自动反查;\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.生效日期:从原合同自动反写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期;\n4.4.付款方式:从原合同自动反写,必选项,付款方式分为「预付」「后付」两种;\n 4.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户;\n 4.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户;\n4.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称;\n4.6.结束日期:需要重新填写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天;\n 4.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 4.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 4.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n4.7.付款周期:从原合同自动反写,必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单;\n4.8.签约公司:从原合同自动反写,必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据;\n4.9.交车区域:从原合同自动反写,必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;\n4.10.交车地点:从原合同自动反写,必填项,输入框,支持自定义输入交车地点;\n4.11.合同原件:显示原合同附件,同时支持上传新附件,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n4.12.备注:从原合同自动反写,如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx,文本域,支持自定义输入备注信息;\n\n5.被授权人信息卡片:\n#用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认;\n5.1.被授权人:从原合同自动反写,必填项,输入框,用于输入被授权人信息;\n5.2.被授权人联系电话:从原合同自动反写,必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;\n5.3.被授权人身份证:从原合同自动反写,必填项,输入框,用于输入被授权人身份证信息;\n5.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可从多个授权人中选择接收授权人;\n\n6.租赁订单信息卡片:\n#用于定义租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n6.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 6.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 6.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 6.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 6.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n6.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注,续签合同时,根据续签时的车辆信息填写(包括车牌号);\n 6.2.1.序号:从原合同自动反写,自动按照条数生成,规则为1、2、3....以此类推;\n 6.2.2.品牌:从原合同自动反写,必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;\n 6.2.3.型号:从原合同自动反写,必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号;\n 6.2.4.车牌号:从原合同自动反写,选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写;\n 6.2.5.车辆识别代码:从原合同自动反写,输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写;\n 6.2.6.车辆月租金:从原合同自动反写,输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算;\n 6.2.7.服务费项目:从原合同自动反写,点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;\n 6.2.7.1.服务项目:从原合同自动反写,必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;\n 6.2.7.2.费用:从原合同自动反写,必填项,输入框,支持2位小数,输入框后缀为元;\n 6.2.7.3.生效时间:从原合同自动反写,必选项,日期选择器,格式为YYYY-MM-DD;\n 6.2.7.4.操作:删除,点击删除直接删除该行数据;\n 6.2.7.5.新增一行数据:点击添加一行服务项目;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 6.2.8.服务费:从原合同自动反写,自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元;\n 6.2.9.保证金:从原合同自动反写,必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中;\n 6.2.10.备注:选填项,输入框,用于备注车辆复杂情况;\n 6.2.11.操作:删除,点击删除删除该行数据;\n 6.2.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息;\n6.3.氢费承担方:从原合同自动反写,必选项,选择器,选项为:「我方」、「客户」;\n 6.3.1.选择「我方」:不显示付款方式、氢气预付款;\n 6.3.2.选择「客户」:付款方式字段默认为预付,可手动修改;\n6.4.付款方式:从原合同自动反写,必选项,选择器,选项为:「预付」、「月付款」、「自行结算」;\n 6.4.1.预付:选择「预付」,需要填写:「氢气预付款」,氢气预付款指合同签署时客户就需预先付出的氢费款项,该部分款项会自动计入提车应收款中氢气预付款金额;\n 6.4.2.月付款:选择「月付款」,提车应收款中不进行收费,而是由业务人员按照实际情况,通过氢费账单功能生成对应氢费账单,单独与客户进行结算;\n 6.4.3.自行结算:选择「自行结算」,指合同签署后,所有氢气费用由客户自行承担;\n6.5.氢气预付款:从原合同自动反写,选择「客户」「预付」时显示,必填项,输入框,支持2位小数,氢气预付款金额会计算入该合同交车应收款中,并计入5.1.4.氢气预付款金额中;\n6.6.退还车氢气单价:从原合同自动反写,必填项,输入框,支持2位小数,后缀为元,不管氢费承担方和付款方式选择任何选项都需要进行维护,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n7.其他费用信息卡片:\n#用于选择对应租赁费用模板,选择后展示证照补办费用、违约金费用、易损件费用、其他费用等信息,租赁费用模板管理功能位于「车辆租赁合同」列表左上角;\n7.1.选择费用模板:从原合同自动反写,如果费用模板有更新,则按照更新后内容显示,必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 7.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;\n 7.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n 7.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;\n 7.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n\n8.账单计算方式卡片:\n#必选项,从原合同自动反写,填充按钮组,可手动修改,用于定义租赁合同的账单计算方式,分为「按自然月结算」、「按付款周期天数结算」两种方式;\n8.1.按付款周期天数结算:账单按照合同基本信息卡片中每隔付款周期*30天形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款从交车任务配置的开始计费时间开始,根据60天收取车辆租金,此后每隔60天生成一期租赁账单;\n8.2.按自然月结算:账单按照第一个月计费开始日期到当月最后一天为第一期,之后按照付款周期所选月份间隔,从开始月份第一天到间隔月份最后一天的自然月方式形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款车辆租金需要收取≥2个月租金作为标准流程,<2个月租金作为非标流程;\n 租赁账单首期从交车任务配置的开始计费时间开始,如付款周期为2个月,则首期账单结束时间为第二个月最后一天,付款周期为3个月,则首期账单结束时间为第三个月最后一天,此后每一期按照实际自然月开始-结束形成账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;\n\n9.最下方为提交并审核、保存、取消三个按钮;\n9.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同重新进入租赁合同审核列表中;\n9.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作;\n9.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' }) }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent)), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')) + ) + ) : null; + + 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: '#1890ff' } }, '变更为三方合同')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + 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'); } }, '客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-third-party'); } }, '丙方客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + 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-third-party', style: { marginTop: 16 } }, React.createElement(CardBlock, { id: 'card-third-party', title: '丙方客户基本信息', collapsed: cc1b, setCollapsed: setCc1b }, thirdPartyCustomerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, contractFormRow1)), + 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)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { style: { height: 60 } }), + reqSpecModalContent, + cancelConfirmOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setCancelConfirmOpen(false); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 520 }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, '提示'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '20px 24px' }) }, '取消将会丢失所有已填写内容,是否确认?'), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: function() { setCancelConfirmOpen(false); } }, '否'), + React.createElement(Button, { type: 'primary', onClick: function() { setCancelConfirmOpen(false); setEdited(false); message.info('已返回车辆租赁合同列表(原型)'); } }, '是') + ) + ) + ) : null, + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { setEdited(false); message.success('租赁合同已提交审核。同时该租赁合同重新进入租赁合同审核列表中。'); } } }, '提交并审核'), + React.createElement(Button, { onClick: function() { setEdited(false); message.success('已保存(不校验必填项),该条数据只能保存人自己查看并编辑,其他人无法操作。'); } }, '保存'), + React.createElement(Button, { onClick: function() { if (edited) setCancelConfirmOpen(true); else message.info('已返回车辆租赁合同列表(原型)'); } }, '取消') + ) + ); +}; diff --git a/web端/车辆租赁合同/新增租赁合同.jsx b/web端/车辆租赁合同/车辆租赁合同-新增.jsx similarity index 77% rename from web端/车辆租赁合同/新增租赁合同.jsx rename to web端/车辆租赁合同/车辆租赁合同-新增.jsx index 49acb07..9da57db 100644 --- a/web端/车辆租赁合同/新增租赁合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-新增.jsx @@ -624,28 +624,8 @@ const Component = function() { var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; - var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; - var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; - var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; - var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; - var reqSpecBlock = { marginBottom: 8 }; - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-新增租赁合同')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) - ); - var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) - ); - var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) - ); - 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: 640 }), 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, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + var requirementContent = '租赁合同\n\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.合同原件:必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.12.备注:文本域,支持自定义输入备注信息;\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; 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: '#1890ff' } }, '新增租赁合同')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), diff --git a/web端/车辆租赁合同/新增车辆.jsx b/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx similarity index 56% rename from web端/车辆租赁合同/新增车辆.jsx rename to web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx index 1f65e5f..8a2df2f 100644 --- a/web端/车辆租赁合同/新增车辆.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx @@ -16,6 +16,9 @@ const Component = function() { var cc1State = React.useState(false); var cc1 = cc1State[0]; var setCc1 = cc1State[1]; + var cc1bState = React.useState(false); + var cc1b = cc1bState[0]; + var setCc1b = cc1bState[1]; var cc2State = React.useState(false); var cc2 = cc2State[0]; var setCc2 = cc2State[1]; @@ -34,6 +37,22 @@ const Component = function() { var cc7State = React.useState(false); var cc7 = cc7State[0]; var setCc7 = cc7State[1]; + var cc8State = React.useState(false); + var cc8 = cc8State[0]; + var setCc8 = cc8State[1]; + var cc9State = React.useState(false); + var cc9 = cc9State[0]; + var setCc9 = cc9State[1]; + + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + var cancelConfirmState = React.useState(false); + var cancelConfirmOpen = cancelConfirmState[0]; + var setCancelConfirmOpen = cancelConfirmState[1]; + var editedState = React.useState(false); + var edited = editedState[0]; + var setEdited = editedState[1]; var emptyNewRow = { brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }; var _newVehicleRows = useState([Object.assign({}, emptyNewRow)]); @@ -48,10 +67,18 @@ const Component = function() { var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2'], '品牌D': ['型号D1'] }; var plateNoOptions = ['浙A10001', '浙A10002', '浙B20001', '浙B20002', '浙C30001', '浙C30002', '沪D40001', '沪D40002', '苏E50001', '苏E50002', '京F60001', '京F60002'].map(function(p) { return { value: p, label: p }; }); + var vehicleList = [ + { plateNo: '浙A10001', vin: 'L1234567890ABCDEF', brand: '品牌A', model: '型号A1' }, + { plateNo: '浙B20002', vin: 'L2234567890ABCDEF', brand: '品牌A', model: '型号A2' }, + { plateNo: '沪D40001', vin: 'L3234567890ABCDEF', brand: '品牌B', model: '型号B1' } + ]; + var addNewVehicleRow = useCallback(function() { + setEdited(true); setNewVehicleRows(function(prev) { return prev.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); }); }, []); var removeNewVehicleRow = useCallback(function(index) { + setEdited(true); setNewVehicleRows(function(prev) { var next = prev.slice(); next.splice(index, 1); @@ -60,13 +87,28 @@ const Component = function() { }); }, []); var updateNewVehicleRow = useCallback(function(index, field, value) { + setEdited(true); setNewVehicleRows(function(prev) { var next = prev.slice(); var row = next[index] || emptyNewRow; var o = {}; o[field] = value; if (field === 'brand') o.model = ''; - if (field === 'plateNo') o.vin = value; + if (field === 'plateNo') { + var v = null; + for (var vi = 0; vi < vehicleList.length; vi++) { + if (vehicleList[vi].plateNo === value) { v = vehicleList[vi]; break; } + } + if (v) { + o.vin = v.vin || ''; + if ((!row.brand && !row.model) && (v.brand || v.model)) { + o.brand = v.brand || ''; + o.model = v.model || ''; + } + } else { + o.vin = ''; + } + } next[index] = Object.assign({}, row, o); return next; }); @@ -84,6 +126,7 @@ const Component = function() { var closeServiceModal = function() { setServiceModalRowIndex(null); }; var addServiceItem = function() { if (serviceModalRowIndex === null) return; + setEdited(true); var next = newVehicleRows.slice(); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); @@ -95,6 +138,7 @@ const Component = function() { }; var removeServiceItem = function(siIndex) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = newVehicleRows.slice(); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(); @@ -108,6 +152,7 @@ const Component = function() { }; var updateServiceItem = function(siIndex, field, value) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = newVehicleRows.slice(); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(); @@ -164,6 +209,7 @@ const Component = function() { rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 }, + historyLink: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 }, footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, btnDefault: { backgroundColor: '#fff', color: '#333' }, @@ -197,7 +243,21 @@ const Component = function() { }; var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }; - var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + var isThreePartyContract = true; + var mockThirdPartyCustomer = { name: '杭州某某供应链有限公司', creditCode: '91330100MA2YYYYY2', address: '浙江省杭州市余杭区未来科技城', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '杭州某某供应链有限公司', companyPhone: '0571-99999999', mailingAddress: '浙江省杭州市余杭区未来科技城', bank: '中国农业银行杭州分行', bankAccount: '6228481234567890123', taxId: '91330100MA2YYYYY2', department: '业务2部', responsible: '李专员' }; + var contractOriginalFiles = [ + { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }, + { name: '租赁合同-补充协议.pdf', size: '0.6 MB', uploadTime: '2026-02-16 14:35' } + ]; + var stampedContractFiles = [ + { name: '盖章合同-租赁合同-盖章版.pdf', size: '1.2 MB', uploadTime: '2026-02-18 10:30' }, + { name: '盖章合同-补充协议-盖章版.pdf', size: '0.6 MB', uploadTime: '2026-02-18 10:35' } + ]; + if (isThreePartyContract) { + contractOriginalFiles = contractOriginalFiles.concat([{ name: '三方合同-原件.pdf', size: '0.8 MB', uploadTime: '2026-03-02 16:10' }]); + stampedContractFiles = stampedContractFiles.concat([{ name: '三方盖章合同-盖章版.pdf', size: '1.1 MB', uploadTime: '2026-03-03 09:50' }]); + } + var addVehicleDate = '2026-03-15'; 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: '' }, { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }]; @@ -241,6 +301,23 @@ const Component = function() { React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' }) ); + var thirdPartyCustomerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '客户名称', value: mockThirdPartyCustomer.name }), + React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockThirdPartyCustomer.creditCode }), + React.createElement(FormItemReadOnly, { label: '客户地址', value: mockThirdPartyCustomer.address }), + React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockThirdPartyCustomer.contact }), + React.createElement(FormItemReadOnly, { label: '客户电话', value: mockThirdPartyCustomer.phone }), + React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockThirdPartyCustomer.email }), + React.createElement(FormItemReadOnly, { label: '企业名称', value: mockThirdPartyCustomer.companyName }), + React.createElement(FormItemReadOnly, { label: '企业电话', value: mockThirdPartyCustomer.companyPhone }), + React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockThirdPartyCustomer.mailingAddress }), + React.createElement(FormItemReadOnly, { label: '开户银行', value: mockThirdPartyCustomer.bank }), + React.createElement(FormItemReadOnly, { label: '银行账号', value: mockThirdPartyCustomer.bankAccount }), + React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockThirdPartyCustomer.taxId }), + React.createElement(FormItemReadOnly, { label: '业务部门', value: mockThirdPartyCustomer.department }), + React.createElement(FormItemReadOnly, { label: '业务负责人', value: mockThirdPartyCustomer.responsible }) + ); + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }), React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }), @@ -256,10 +333,14 @@ const Component = function() { React.createElement('div', null, React.createElement('label', { style: styles.label }, '合同原件'), React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) }, - contractOriginal - ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, - React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name), - (contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + contractOriginalFiles && contractOriginalFiles.length + ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } }, + contractOriginalFiles.map(function(f, i) { + return React.createElement('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, f.name), + (f.size || f.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size || '') + (f.size && f.uploadTime ? ' · ' : '') + (f.uploadTime || '')) : null + ); + }) ) : '—' ) @@ -337,8 +418,8 @@ const Component = function() { { title: '服务费项目', key: 'serviceItems', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(i); } }, '管理'); } }, { title: '服务费', key: 'serviceFee', width: 90, render: function(_, row, i) { return calcRowServiceFee(row) + ' 元'; } }, { title: '保证金', key: 'deposit', width: 100, render: function(_, row, i) { return React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateNewVehicleRow(i, 'deposit', e.target.value); }, addonAfter: '元' }); } }, - { title: '备注', key: 'remark', width: 80, render: function(_, row, i) { return React.createElement(Input, { placeholder: '请输入', value: row.remark || '', onChange: function(e) { updateNewVehicleRow(i, 'remark', e.target.value); } }); } }, - { title: '操作', key: 'action', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeNewVehicleRow(i); } }, '删除'); } } + { title: '操作', key: 'action', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeNewVehicleRow(i); } }, '删除'); } }, + { title: '备注', key: 'remark', width: 120, render: function(_, row, i) { return React.createElement(Input, { placeholder: '选填', value: row.remark || '', onChange: function(e) { updateNewVehicleRow(i, 'remark', e.target.value); } }); } } ]; var newOrderSummaryEl = React.createElement('div', { style: styles.summaryList }, @@ -373,7 +454,7 @@ const Component = function() { ) : null; var newVehicleCardContent = React.createElement('div', null, - React.createElement('div', { style: styles.sectionTitle }, '新增订单'), + React.createElement('div', { style: styles.sectionTitle }, '新增车辆信息'), newOrderSummaryEl, React.createElement(Table, { rowKey: function(_, i) { return String(i); }, size: 'small', columns: newVehicleColumns, dataSource: newVehicleRows, pagination: false, scroll: { x: 1100 } }), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addNewVehicleRow }, '添加一行') @@ -381,19 +462,36 @@ const Component = function() { var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody); var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod)); - var attachmentContent = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '盖章合同附件')), - React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { color: '#666', fontSize: 14 } }, '租赁合同-盖章版.pdf · 1.2 MB · 2026-02-16 14:30')) - ); + var stampedAttachmentInner = stampedContractFiles && stampedContractFiles.length + ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10 } }, + stampedContractFiles.map(function(f, i) { + return React.createElement('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', padding: '8px 12px', backgroundColor: '#f5f5f5', borderRadius: 4, border: '1px solid #d9d9d9' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, f.name), + React.createElement('span', { style: { color: '#999', fontSize: 12 } }, f.size + ' · ' + f.uploadTime) + ); + }) + ) + : React.createElement('div', { style: { color: '#999', fontSize: 13 } }, '暂无附件'); - var changeHistorySorted = [{ changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' }]; + var changeHistoryRaw = [ + { changeTime: '2026-03-03 10:20', opType: '保存', operator: '张三', remark: '无' }, + { changeTime: '2026-03-02 16:10', opType: '变更为三方合同', operator: '李四', remark: '增加丙方客户“客户名称杭州某某供应链有限公司”' }, + { changeTime: '2026-03-01 11:40', opType: '添加车辆', operator: '王五', remark: '添加车辆“沪A30003”\n添加车辆“浙A10001”\n添加车辆“浙B20002”' }, + { changeTime: '2026-02-28 09:05', opType: '添加授权人', operator: '赵六', remark: '添加授权人“授权人张三”\n添加授权人“授权人李四”' }, + { changeTime: '2026-02-26 15:30', opType: '续签合同', operator: '周九', remark: '续签自“原合同编码JXZL20250210YW101100A”' }, + { changeTime: '2026-02-23 14:18', opType: '撤回合同', operator: '钱七', remark: '主动撤回' }, + { changeTime: '2026-02-20 10:00', opType: '终止合同', operator: '孙八', remark: '主动终止' }, + { changeTime: '2026-02-16 14:30', opType: '转正式合同', operator: '张经理', remark: '转正式合同自“原合同编码JXZL20260210YW101230B”' } + ]; + var changeHistorySorted = changeHistoryRaw.slice().sort(function(a, b) { return b.changeTime.localeCompare(a.changeTime); }); var historyTableRows = changeHistorySorted.map(function(row, index) { return React.createElement('tr', { key: index }, React.createElement('td', { style: styles.rentalTd }, index + 1), React.createElement('td', { style: styles.rentalTd }, row.changeTime), React.createElement('td', { style: styles.rentalTd }, row.opType), React.createElement('td', { style: styles.rentalTd }, row.operator), - React.createElement('td', { style: styles.rentalTd }, row.remark) + React.createElement('td', { style: styles.rentalTd }, React.createElement('button', { type: 'button', style: styles.historyLink, onClick: function() { window.open('#', '_blank'); } }, '查看变更前记录')), + React.createElement('td', { style: Object.assign({}, styles.rentalTd, { verticalAlign: 'top', whiteSpace: 'pre-line' }) }, row.remark) ); }); var historyTable = React.createElement('table', { style: styles.rentalTable }, @@ -401,48 +499,87 @@ const Component = function() { React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh, width: 60 }, '序号'), React.createElement('th', { style: styles.rentalTh, width: 140 }, '变更时间'), - React.createElement('th', { style: styles.rentalTh, width: 100 }, '操作类型'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '操作类型'), React.createElement('th', { style: styles.rentalTh, width: 90 }, '操作人'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '原始记录'), React.createElement('th', { style: styles.rentalTh }, '备注') ) ), React.createElement('tbody', null, historyTableRows) ); - var changeHistoryContent = React.createElement('div', { style: styles.card }, - React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '合同变更历史记录')), - React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { overflowX: 'auto' } }, historyTable)) - ); + + 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.客户地址:从该条合同自动反查;\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.合同编码:从该条合同自动反查,在新增租赁合同点击提交审核时生成;\n 合同编码由:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]组成;\n 4.2.1.地区简写:如上海为SH,嘉兴为JX;\n 4.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;\n 4.2.3.签约时间:显示合同签约时间,如20260216\n 4.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;\n 4.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团编号为1235的合同;\n 4.2.6.签署状态:A为正式合同,B为试用合同;\n 如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中编号为1235,也可以理解为第1235份合同;\n 4.2.7.如果该合同为续签合同,则自动在新合同编码后额外添加:(续签自:旧合同合同编码xxx);\n 4.2.8.如果该合同为转正式合同,则自动在新合同编码后额外添加:(转正式合同自:旧合同合同编码xxx);\n4.3.合同类型:从该条合同自动反查;\n4.4.生效日期:从该条合同自动反查;\n4.5.付款方式:从该条合同自动反查;\n4.6.主要车型:从该条合同自动反查;\n4.7.结束日期:从该条合同自动反查;\n 4.7.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 4.7.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 4.7.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n4.8.付款周期:从该条合同自动反查;\n4.9.签约公司:从该条合同自动反查;\n4.10.交车区域:从该条合同自动反查;\n4.11.交车地点:从该条合同自动反查;\n4.12.合同原件:从该条合同自动反查,显示「合同原件名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列,如果转为三方合同,则会显示新附件,显示「三方合同原件名称.格式」「文件大小」「上传时间」;\n4.13.备注:从该条合同自动反查;\n 4.13.1.如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx;\n 4.13.2.如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx;\n\n5.被授权人信息卡片:\n#显示所有被授权人信息,不可修改,如要新增被授权人,需要在列表中点击添加被授权人进行处理;\n5.1.被授权人:从该条合同自动反查;\n5.2.被授权人联系电话:从该条合同自动反查;\n5.3.被授权人身份证:从该条合同自动反查;\n\n6.租赁订单信息卡片:\n#显示租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n6.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 6.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 6.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 6.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 6.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n6.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 6.2.1.序号:从该条合同自动反查;\n 6.2.2.品牌:从该条合同自动反查;\n 6.2.3.型号:从该条合同自动反查;\n 6.2.4.车牌号:从该条合同自动反查;\n 6.2.5.车辆识别代码:从该条合同自动反查;\n 6.2.6.车辆月租金:从该条合同自动反查;\n 6.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 6.2.7.1.服务项目:从该条合同自动反查;\n 6.2.7.2.费用:从该条合同自动反查;\n 6.2.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 6.2.8.服务费:从该条合同自动反查;\n 6.2.9.保证金:从该条合同自动反查;\n 6.2.10.备注:从该条合同自动反查;\n6.3.氢费承担方:从该条合同自动反查;\n6.4.付款方式:从该条合同自动反查;\n6.5.氢气预付款:从该条合同自动反查;\n6.6.退还车氢气单价:从该条合同自动反查,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n7.新增车辆信息卡片(用户每次为合同新增车辆时,都会生成一个新增车辆信息卡片):\n#显示租赁合同对应新增车辆的明细费用、氢费明细费用等相关信息;\n7.1.卡片标题显示为:新增车辆信息(YYYY-MM-DD)\n7.2.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额等相关信息;\n 7.2.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 7.2.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 7.2.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n7.3.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、操作、备注;\n 7.3.1.序号:自动按照条数生成,规则为1、2、3....以此类推;\n 7.3.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;\n 7.3.3.型号:必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号;\n 7.3.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写;\n 7.3.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写;\n 7.3.6.车辆月租金:输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算;\n 7.3.7.服务费项目:点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;\n 7.3.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;\n 7.3.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;\n 7.3.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD;\n 7.3.7.4.操作:删除,点击删除直接删除该行数据;\n 7.3.7.5.新增一行数据:点击添加一行服务项目;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 7.3.8.服务费:自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元;\n 7.3.9.保证金:必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中;\n 7.3.10.备注:选填项,输入框,用于备注车辆复杂情况;\n 7.3.11.操作:删除,点击删除删除该行数据;\n 7.3.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息;\n\n8.其他费用信息卡片:\n#显示对应租赁费用模板证照补办费用、违约金费用、易损件费用、其他费用等信息;\n8.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 8.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,从该条合同自动反查;\n 8.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n 8.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,从该条合同自动反查;\n 8.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n\n9.账单计算方式卡片:\n#显示实际结算方式;\n\n10.盖章合同附件:\n#显示财务审核完成后,由法务上传的盖章版合同文件,显示「盖章合同名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列;如果转为三方合同,则会重新审批,法务上传显示新附件,显示「三方盖章合同名称.格式」「文件大小」「上传时间」\n\n11.合同变更历史记录:\n#显示合同变更历史,包括序号、变更时间、操作类型、操作人、原始记录、备注;\n11.1.序号:显示变更历史序号,按照变更时间倒序(从近到远)进行排序;\n11.2.变更时间:显示变更时间,格式为:YYYY-MM-DD HH:MM;\n11.3.操作类型:包括变更为三方合同、添加车辆、添加授权人、续签合同、撤回合同、终止合同、转正式合同、保存;\n11.4.操作人:显示对应操作类型操作人员;\n11.5.原始记录:显示查看变更前记录,点击打开新页面至查看原始合同快照;\n11.6.备注:备注改动内容,包括:\n 11.6.1.变更为三方合同时:增加丙方客户“客户名称xxxxxx”;\n 11.6.2.添加车辆时:添加车辆“车牌号xxx”、“车牌号xxx”、“车牌号xxx”,每辆车单独一行显示;\n 11.6.3.添加授权人时:添加授权人“授权人xxxx”、“授权人xxx”,每个授权人单独一行显示;\n 11.6.4.续签合同时:续签自”原合同编码xxxxxx“;\n 11.6.5.撤回合同时:主动撤回;\n 11.6.6.终止合同时:主动终止;\n 11.6.7.转正式合同时:转正式合同自“原合同编码xxxx”;\n 11.6.8.保存:无;\n\n12.最下方为提交并审核、保存、取消三个按钮;\n12.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同重新触发审核流程;\n12.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作;\n12.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; 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: '#1890ff' } }, '新增车辆')) + 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); } }, '查看需求说明') ), React.createElement('div', { style: styles.anchorWrap }, React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + isThreePartyContract ? React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-third-party'); } }, '丙方客户基本信息') : null, React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), - React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-new-vehicle'); } }, '新增车辆信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-new-vehicle'); } }, '新增车辆信息(' + addVehicleDate + ')'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'), React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录') ), React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + isThreePartyContract ? React.createElement('div', { id: 'card-third-party', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '丙方客户基本信息', collapsed: cc1b, setCollapsed: setCc1b }, thirdPartyCustomerFields)) : null, 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-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-new-vehicle', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '新增车辆信息', collapsed: cc7, setCollapsed: setCc7 }, newVehicleCardContent)), + React.createElement('div', { id: 'card-new-vehicle', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '新增车辆信息(' + addVehicleDate + ')', collapsed: cc7, setCollapsed: setCc7 }, newVehicleCardContent)), React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), 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), + React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '盖章合同附件', collapsed: cc8, setCollapsed: setCc8 }, stampedAttachmentInner)), + React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同变更历史记录', collapsed: cc9, setCollapsed: setCc9 }, React.createElement('div', { style: { overflowX: 'auto' } }, historyTable))), React.createElement('div', { style: { height: 60 } }), serviceModalContent, + 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' }) }, 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, + cancelConfirmOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setCancelConfirmOpen(false); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 520 }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, '提示'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '20px 24px' }) }, '取消将会丢失所有已填写内容,是否确认?'), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: function() { setCancelConfirmOpen(false); } }, '否'), + React.createElement(Button, { type: 'primary', onClick: function() { setCancelConfirmOpen(false); setEdited(false); message.info('已返回车辆租赁合同列表(原型)'); } }, '是') + ) + ) + ) : null, React.createElement('div', { style: styles.footer }, - React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { message.info('取消(原型)'); } }, '取消'), - React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function() { message.success('已提交审核,审核通过后生效(原型)'); } }, '提交审核') + React.createElement(Button, { type: 'primary', onClick: function() { + // 提交并审核:做必填校验(品牌/型号/月租金/保证金/服务项目/费用/生效时间) + var errs = []; + for (var i = 0; i < newVehicleRows.length; i++) { + var r = newVehicleRows[i]; + if (!r.brand) errs.push('第' + (i + 1) + '行品牌'); + if (!r.model) errs.push('第' + (i + 1) + '行型号'); + if (!r.monthRent || String(r.monthRent).trim() === '') errs.push('第' + (i + 1) + '行车辆月租金'); + if (!r.deposit || String(r.deposit).trim() === '') errs.push('第' + (i + 1) + '行保证金'); + var items = r.serviceItems || []; + for (var j = 0; j < items.length; j++) { + if (!items[j].project) errs.push('第' + (i + 1) + '行服务项目'); + if (!items[j].fee || String(items[j].fee).trim() === '') errs.push('第' + (i + 1) + '行服务费用'); + if (!items[j].effectiveDate) errs.push('第' + (i + 1) + '行生效时间'); + } + } + if (errs.length) { message.warning('请完善必填项:' + errs.join('、')); return; } + setEdited(false); + message.success('租赁合同已提交审核。同时该租赁合同重新触发审核流程;'); + } }, '提交并审核'), + React.createElement(Button, { onClick: function() { setEdited(false); message.success('已保存(不校验必填项),仅操作人可查看编辑'); } }, '保存'), + React.createElement(Button, { onClick: function() { if (edited) setCancelConfirmOpen(true); else message.info('已返回车辆租赁合同列表(原型)'); } }, '取消') ) ); }; diff --git a/web端/车辆租赁合同/查看租赁合同.jsx b/web端/车辆租赁合同/车辆租赁合同-查看.jsx similarity index 54% rename from web端/车辆租赁合同/查看租赁合同.jsx rename to web端/车辆租赁合同/车辆租赁合同-查看.jsx index b325197..5d03824 100644 --- a/web端/车辆租赁合同/查看租赁合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-查看.jsx @@ -5,6 +5,9 @@ const Component = function() { var cc1State = React.useState(false); var cc1 = cc1State[0]; var setCc1 = cc1State[1]; + var cc1bState = React.useState(false); + var cc1b = cc1bState[0]; + var setCc1b = cc1bState[1]; var cc2State = React.useState(false); var cc2 = cc2State[0]; var setCc2 = cc2State[1]; @@ -14,34 +17,44 @@ const Component = function() { var cc4State = React.useState(false); var cc4 = cc4State[0]; var setCc4 = cc4State[1]; + var cc4bState = React.useState(false); + var cc4b = cc4bState[0]; + var setCc4b = cc4bState[1]; var cc5State = React.useState(false); var cc5 = cc5State[0]; var setCc5 = cc5State[1]; var cc6State = React.useState(false); var cc6 = cc6State[0]; var setCc6 = cc6State[1]; - var attachmentHoverState = React.useState(false); - var attachmentHover = attachmentHoverState[0]; - var setAttachmentHover = attachmentHoverState[1]; + var stampedHoverIndexState = React.useState(null); + var stampedHoverIndex = stampedHoverIndexState[0]; + var setStampedHoverIndex = stampedHoverIndexState[1]; var reqSpecState = React.useState(false); var reqSpecOpen = reqSpecState[0]; var setReqSpecOpen = reqSpecState[1]; var servicePopoverRowState = React.useState(null); var servicePopoverRow = servicePopoverRowState[0]; var setServicePopoverRow = servicePopoverRowState[1]; + var servicePopoverRowNewVehicleState = React.useState(null); + var servicePopoverRowNewVehicle = servicePopoverRowNewVehicleState[0]; + var setServicePopoverRowNewVehicle = servicePopoverRowNewVehicleState[1]; - // 模拟已上传的盖章合同附件 - var uploadedFile = { name: '租赁合同-盖章版.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + // 模拟已上传的盖章合同附件(法务上传,可多附件;三方合同重新审批后会有新附件) + var stampedContractFiles = [ + { name: '盖章合同-租赁合同-盖章版.pdf', size: '1.2 MB', uploadTime: '2026-02-18 10:30' }, + { name: '盖章合同-补充协议-盖章版.pdf', size: '0.6 MB', uploadTime: '2026-02-18 10:35' } + ]; // 合同变更历史记录(变更时间倒序,序号 1.2.3...) var changeHistoryRaw = [ - { changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' }, - { changeTime: '2026-02-16 10:30', opType: '附加费用', operator: '李四', remark: '添加服务项目"保养费用",费用为"200",生效时间为"2026-03-01"\n添加服务项目"清洗费",费用为"80",生效时间为"2026-03-01"' }, - { changeTime: '2026-02-15 16:20', opType: '添加授权人', operator: '王五', remark: '添加授权人"李四"' }, - { changeTime: '2026-02-14 09:15', opType: '合同续签', operator: '赵六', remark: '原合同编码"JXZL20250210YW101100A"' }, - { changeTime: '2026-02-13 10:00', opType: '终止合同', operator: '周九', remark: '终止原因"客户提前解约"' }, - { changeTime: '2026-02-12 11:00', opType: '撤回合同', operator: '钱七', remark: '-' }, - { changeTime: '2026-02-10 14:30', opType: '转正式合同', operator: '孙八', remark: '原合同编码"JXZL20260210YW101230B"' } + { changeTime: '2026-03-03 10:20', opType: '保存', operator: '张三', remark: '无' }, + { changeTime: '2026-03-02 16:10', opType: '变更为三方合同', operator: '李四', remark: '增加丙方客户“客户名称杭州某某供应链有限公司”' }, + { changeTime: '2026-03-01 11:40', opType: '添加车辆', operator: '王五', remark: '添加车辆“沪A30003”\n添加车辆“浙A10001”\n添加车辆“浙B20002”' }, + { changeTime: '2026-02-28 09:05', opType: '添加授权人', operator: '赵六', remark: '添加授权人“授权人张三”\n添加授权人“授权人李四”' }, + { changeTime: '2026-02-26 15:30', opType: '续签合同', operator: '周九', remark: '续签自“原合同编码JXZL20250210YW101100A”' }, + { changeTime: '2026-02-23 14:18', opType: '撤回合同', operator: '钱七', remark: '主动撤回' }, + { changeTime: '2026-02-20 10:00', opType: '终止合同', operator: '孙八', remark: '主动终止' }, + { changeTime: '2026-02-16 14:30', opType: '转正式合同', operator: '张经理', remark: '转正式合同自“原合同编码JXZL20260210YW101230B”' } ]; var changeHistorySorted = changeHistoryRaw.slice().sort(function(a, b) { return b.changeTime.localeCompare(a.changeTime); }); @@ -154,7 +167,16 @@ const Component = function() { // 模拟只读数据(与新增合同同结构) var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }; - var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + var isThreePartyContract = true; + var mockThirdPartyCustomer = { name: '杭州某某供应链有限公司', creditCode: '91330100MA2YYYYY2', address: '浙江省杭州市余杭区未来科技城', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '杭州某某供应链有限公司', companyPhone: '0571-99999999', mailingAddress: '浙江省杭州市余杭区未来科技城', bank: '中国农业银行杭州分行', bankAccount: '6228481234567890123', taxId: '91330100MA2YYYYY2', department: '业务2部', responsible: '李专员' }; + var contractOriginalFiles = [ + { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }, + { name: '租赁合同-补充协议.pdf', size: '0.6 MB', uploadTime: '2026-02-16 14:35' } + ]; + if (isThreePartyContract) { + contractOriginalFiles = contractOriginalFiles.concat([{ name: '三方合同-原件.pdf', size: '0.8 MB', uploadTime: '2026-03-02 16:10' }]); + stampedContractFiles = stampedContractFiles.concat([{ name: '三方盖章合同-盖章版.pdf', size: '1.1 MB', uploadTime: '2026-03-03 09:50' }]); + } 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 = [ @@ -162,6 +184,11 @@ const Component = function() { { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', 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' }] } + ]; + var mockNewVehicleSummary = { vehicleCount: 1, totalRentService: '7400.00', totalDeposit: '8000.00' }; var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' }; var mockFeeTemplate = '标准费用模板A'; var mockBillingMethod = '按自然月结算'; @@ -221,6 +248,23 @@ const Component = function() { React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' }) ); + var thirdPartyCustomerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '客户名称', value: mockThirdPartyCustomer.name }), + React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockThirdPartyCustomer.creditCode }), + React.createElement(FormItemReadOnly, { label: '客户地址', value: mockThirdPartyCustomer.address }), + React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockThirdPartyCustomer.contact }), + React.createElement(FormItemReadOnly, { label: '客户电话', value: mockThirdPartyCustomer.phone }), + React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockThirdPartyCustomer.email }), + React.createElement(FormItemReadOnly, { label: '企业名称', value: mockThirdPartyCustomer.companyName }), + React.createElement(FormItemReadOnly, { label: '企业电话', value: mockThirdPartyCustomer.companyPhone }), + React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockThirdPartyCustomer.mailingAddress }), + React.createElement(FormItemReadOnly, { label: '开户银行', value: mockThirdPartyCustomer.bank }), + React.createElement(FormItemReadOnly, { label: '银行账号', value: mockThirdPartyCustomer.bankAccount }), + React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockThirdPartyCustomer.taxId }), + React.createElement(FormItemReadOnly, { label: '业务部门', value: mockThirdPartyCustomer.department }), + React.createElement(FormItemReadOnly, { label: '业务负责人', value: mockThirdPartyCustomer.responsible }) + ); + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }), React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }), @@ -236,10 +280,14 @@ const Component = function() { React.createElement('div', null, React.createElement('label', { style: styles.label }, '合同原件'), React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) }, - contractOriginal - ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, - React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name), - (contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + contractOriginalFiles && contractOriginalFiles.length + ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } }, + contractOriginalFiles.map(function(f, i) { + return React.createElement('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, f.name), + (f.size || f.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (f.size || '') + (f.size && f.uploadTime ? ' · ' : '') + (f.uploadTime || '')) : null + ); + }) ) : '—' ) @@ -303,7 +351,7 @@ const Component = function() { React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 } }, '查看')) : React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 }, - onClick: function() { setServicePopoverRow(servicePopoverRow === idx ? null : idx); } + onClick: function() { setServicePopoverRowNewVehicle(null); setServicePopoverRow(servicePopoverRow === idx ? null : idx); } }, '查看'); return React.createElement('tr', { key: idx }, React.createElement('td', { style: styles.rentalTd }, idx + 1), @@ -343,6 +391,52 @@ const Component = function() { ); var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly); + var newVehicleSummaryEl = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, mockNewVehicleSummary.vehicleCount + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, mockNewVehicleSummary.totalRentService + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, mockNewVehicleSummary.totalDeposit + ' 元')) + ); + var newVehicleTableBody = mockNewVehicleOrders.map(function(row, idx) { + var servicePopoverContent = renderServiceItemsPopover(row); + var serviceCell = Popover + ? React.createElement(Popover, { content: servicePopoverContent, title: '服务项目', trigger: 'click' }, + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 } }, '查看')) + : React.createElement('span', { + style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 }, + onClick: function() { setServicePopoverRow(null); setServicePopoverRowNewVehicle(idx); } + }, '查看'); + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTd }, idx + 1), + 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.vin)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')), + React.createElement('td', { style: styles.rentalTd }, serviceCell), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—')) + ); + }); + var newVehicleTableEl = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'), + 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: 120 }, '车辆月租金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '服务费项目'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注') + ) + ), + React.createElement('tbody', null, newVehicleTableBody) + ); + var newVehicleContent = React.createElement('div', null, newVehicleSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, newVehicleTableEl)); + var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody); var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod)); @@ -352,15 +446,21 @@ const Component = function() { React.createElement('span', { style: styles.cardTitle }, '盖章合同附件') ), React.createElement('div', { style: styles.cardBody }, - React.createElement('div', { style: styles.attachmentRow }, - React.createElement('button', { - type: 'button', - style: Object.assign({}, styles.attachmentFile, attachmentHover ? styles.attachmentFileHover : {}), - onMouseEnter: function() { setAttachmentHover(true); }, - onMouseLeave: function() { setAttachmentHover(false); }, - onClick: function() { window.open('#', '_blank'); } - }, '📄 ' + uploadedFile.name, React.createElement('span', { style: styles.attachmentMeta }, uploadedFile.size + ' · ' + uploadedFile.uploadTime)) - ) + stampedContractFiles && stampedContractFiles.length + ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10 } }, + stampedContractFiles.map(function(f, i) { + return React.createElement('div', { key: i, style: styles.attachmentRow }, + React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.attachmentFile, stampedHoverIndex === i ? styles.attachmentFileHover : {}), + onMouseEnter: function() { setStampedHoverIndex(i); }, + onMouseLeave: function() { setStampedHoverIndex(null); }, + onClick: function() { window.open('#', '_blank'); } + }, '📄 ' + f.name, React.createElement('span', { style: styles.attachmentMeta }, f.size + ' · ' + f.uploadTime)) + ); + }) + ) + : React.createElement('div', { style: { color: '#999', fontSize: 13 } }, '暂无附件') ) ); @@ -396,47 +496,43 @@ const Component = function() { ) ); - var reqSpecBlock = { marginBottom: 8 }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; - var reqSpecH2First = { marginTop: 0 }; - var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; - var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: Object.assign({}, reqSpecH2, reqSpecH2First) }, '1.其他部分与新增租赁合同相同,只是不可编辑,新增以下几块内容:')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.审批状态卡片:'), React.createElement('div', { style: reqSpecP }, '显示所有审批步骤及当前节点、审批人姓名、已通过/待审批/驳回三种状态;最下方为对应审批节点审批操作时间,格式为 YYYY-MM-DD HH:MM。')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.盖章合同附件:'), React.createElement('div', { style: reqSpecP }, '当法务未完成审核并上传盖章合同时,盖章合同附件显示为暂无附件;已上传盖章合同时,显示合同文件名称、文件大小、上传时间(YYYY-MM-DD HH:MM)。')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.合同变更历史记录:'), React.createElement('div', { style: reqSpecP }, '列表字段为序号、变更时间、操作类型、操作人、原始记录、备注。'), React.createElement('div', { style: reqSpecLi }, '4.1.序号:1.2.3...以此类推;'), React.createElement('div', { style: reqSpecLi }, '4.2.变更时间:YYYY-MM-DD HH:MM 格式,倒序展示数据;'), React.createElement('div', { style: reqSpecLi }, '4.3.操作类型:包括变更内容、合同续签、撤回合同、添加授权人、附加费用、转正式合同、终止合同;'), React.createElement('div', { style: reqSpecLi }, '4.4.操作人:记录对应操作用户名称;'), React.createElement('div', { style: reqSpecLi }, '4.5.原始记录:显示查看变更前记录,点击会新开页查看未修改前合同原始记录;'), React.createElement('div', { style: reqSpecLi }, '4.6.备注:'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.1.操作类型为变更内容时,显示:"字段名"由"xx"修改为"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.2.操作类型为合同续签时,显示:原合同编码"xxxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.3.操作类型为撤回合同时,显示:-;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.4.操作类型为添加授权人时,显示:添加授权人"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.5.操作类型为附加费用时,显示:添加服务项目"xxxxxx",费用为"xxxxxxx",生效时间为"YYYY-MM-DD",如果有多条附加费用,支持多行显示;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.6.操作类型为转正式合同时,显示:原合同编码"xxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.7.操作类型为终止合同时,显示:终止原因"xxxxxx"。')) - ); - 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', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + 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 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 servicePopoverContentCustom = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow] - ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setServicePopoverRow(null); } }, + var servicePopoverRentalOpen = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow]; + var servicePopoverNewVehicleOpen = !Popover && servicePopoverRowNewVehicle !== null && mockNewVehicleOrders[servicePopoverRowNewVehicle]; + var servicePopoverContentCustom = (servicePopoverRentalOpen || servicePopoverNewVehicleOpen) + ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) { setServicePopoverRow(null); setServicePopoverRowNewVehicle(null); } } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 480 }), onClick: function(e) { e.stopPropagation(); } }, - React.createElement('div', { style: styles.modalHeader }, '服务项明细'), - React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '16px 20px' }) }, renderServiceItemsPopover(mockRentalOrders[servicePopoverRow])), - 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() { setServicePopoverRow(null); } }, '关闭')) + React.createElement('div', { style: styles.modalHeader }, '服务项目'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '16px 20px' }) }, servicePopoverRentalOpen ? renderServiceItemsPopover(mockRentalOrders[servicePopoverRow]) : renderServiceItemsPopover(mockNewVehicleOrders[servicePopoverRowNewVehicle])), + 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() { setServicePopoverRow(null); setServicePopoverRowNewVehicle(null); } }, '关闭')) )) : null; return React.createElement('div', { style: styles.page }, servicePopoverContentCustom, - React.createElement('div', { style: { 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('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'); } }, '客户基本信息'), + isThreePartyContract ? React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-third-party'); } }, '丙方客户基本信息') : null, React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-new-vehicle'); } }, '新增车辆信息(' + addVehicleDate + ')'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'), React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录') ), React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + isThreePartyContract ? React.createElement('div', { id: 'card-third-party', style: { marginTop: 16 } }, React.createElement(CardBlock, { id: 'card-third-party', title: '丙方客户基本信息', collapsed: cc1b, setCollapsed: setCc1b }, thirdPartyCustomerFields)) : null, 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-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-new-vehicle', style: { marginTop: 16 } }, React.createElement(CardBlock, { id: 'card-new-vehicle', title: '新增车辆信息(' + addVehicleDate + ')', collapsed: cc4b, setCollapsed: setCc4b }, newVehicleContent)), React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), 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), diff --git a/web端/车辆租赁合同/合同续签.jsx b/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx similarity index 64% rename from web端/车辆租赁合同/合同续签.jsx rename to web端/车辆租赁合同/车辆租赁合同-续签合同.jsx index 88e6c5a..650eac6 100644 --- a/web端/车辆租赁合同/合同续签.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx @@ -30,14 +30,19 @@ const Component = function() { var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; var brandList = ['品牌A', '品牌B', '品牌C', '品牌D']; var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2', '型号C3'], '品牌D': ['型号D1'] }; - var vehicleList = [{ plateNo: '浙A10001', vin: 'L1234567890ABCDEF' }, { plateNo: '浙B20002', vin: 'L2234567890ABCDEF' }, { plateNo: '沪A30003', vin: 'L3234567890ABCDEF' }, { plateNo: '粤A40004', vin: 'L4234567890ABCDEF' }]; + var vehicleList = [ + { plateNo: '浙A10001', vin: 'L1234567890ABCDEF', brand: '品牌A', model: '型号A1' }, + { plateNo: '浙B20002', vin: 'L2234567890ABCDEF', brand: '品牌A', model: '型号A2' }, + { plateNo: '沪A30003', vin: 'L3234567890ABCDEF', brand: '品牌B', model: '型号B1' }, + { plateNo: '粤A40004', vin: 'L4234567890ABCDEF', brand: '品牌C', model: '型号C2' } + ]; var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板']; var prevContractSample = { customerId: '1', businessDept: 'YW1', businessOwner: '张经理', - contractOriginal: null, + contractOriginalFiles: [], projectName: '嘉兴氢能运输项目', contractType: '正式合同', effectiveDate: '2027-02-17', @@ -82,9 +87,9 @@ const Component = function() { var ownerFocusError = cs6[0]; var setOwnerFocusError = cs6[1]; var contractOriginalRef = React.useRef(null); - var csContractOriginal = React.useState(null); - var contractOriginal = csContractOriginal[0]; - var setContractOriginal = csContractOriginal[1]; + var csContractOriginal = React.useState([]); + var contractOriginalFiles = csContractOriginal[0]; + var setContractOriginalFiles = csContractOriginal[1]; var bs1 = React.useState(prevContractSample.projectName); var projectName = bs1[0]; @@ -98,7 +103,8 @@ const Component = function() { var bs4 = React.useState(prevContractSample.paymentMethod); var paymentMethod = bs4[0]; var setPaymentMethod = bs4[1]; - var bs6 = React.useState(prevContractSample.endDate); + // 续签合同:结束日期需重新填写 + var bs6 = React.useState(''); var endDate = bs6[0]; var setEndDate = bs6[1]; var bs7 = React.useState(prevContractSample.paymentPeriod); @@ -120,7 +126,7 @@ const Component = function() { var bs11 = React.useState(prevContractSample.deliveryLocation); var deliveryLocation = bs11[0]; var setDeliveryLocation = bs11[1]; - var bs12 = React.useState(prevContractSample.remarks); + var bs12 = React.useState('续签自:旧合同编码JXZL20260216YW101235A'); var remarks = bs12[0]; var setRemarks = bs12[1]; @@ -190,6 +196,12 @@ const Component = function() { var reqSpecState = React.useState(false); var reqSpecOpen = reqSpecState[0]; var setReqSpecOpen = reqSpecState[1]; + var cancelConfirmState = React.useState(false); + var cancelConfirmOpen = cancelConfirmState[0]; + var setCancelConfirmOpen = cancelConfirmState[1]; + var editedState = React.useState(false); + var edited = editedState[0]; + var setEdited = editedState[1]; var formErrorsState = React.useState({}); var formErrors = formErrorsState[0]; var setFormErrors = formErrorsState[1]; @@ -230,12 +242,14 @@ const Component = function() { var rentalTotalHydrogen = (hydrogenPaymentMethod === '预付' && hydrogenBearer === '客户') ? (parseFloat(hydrogenPrepay) || 0) : 0; var selectCustomer = function(c) { + setEdited(true); setSelectedCustomer(c); setCustomerSearch(c ? c.name : ''); setCustomerDropdownOpen(false); }; var deliveryRegionDisplay = deliveryProvince && deliveryCity ? deliveryProvince + ' / ' + deliveryCity : ''; var selectDeliveryRegion = function(province, city) { + setEdited(true); setDeliveryProvince(province); setDeliveryCity(city); setDeliveryRegionOpen(false); @@ -250,17 +264,57 @@ const Component = function() { }; var handleOwnerBlur = function() { setOwnerFocusError(''); }; var openContractOriginal = function() { - if (contractOriginal && contractOriginal.file) { - var url = URL.createObjectURL(contractOriginal.file); + // 兼容旧逻辑(保留函数名):不再使用单附件预览 + message.info('请点击附件列表中的文件名预览(原型)'); + }; + + var pad2 = function(n) { + var s = String(n); + return s.length === 1 ? ('0' + s) : s; + }; + + var addContractOriginalFiles = function(fileList) { + if (!fileList || !fileList.length) return; + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + for (var i = 0; i < fileList.length; i++) { + var f = fileList[i]; + if (!f) continue; + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : (now.getFullYear() + '-' + pad2(now.getMonth() + 1) + '-' + pad2(now.getDate()) + ' ' + pad2(now.getHours()) + ':' + pad2(now.getMinutes())); + next.push({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); + } + return next; + }); + }; + + var removeContractOriginalFile = function(index) { + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + next.splice(index, 1); + return next; + }); + }; + + var openContractOriginalFile = function(index) { + var f = contractOriginalFiles && contractOriginalFiles[index]; + if (!f) return; + if (f.file) { + var url = URL.createObjectURL(f.file); window.open(url); + return; } + message.info('原合同反写附件(原型),暂无可预览源文件'); }; var validateSubmitAndReview = function() { var errs = {}; if (!selectedCustomer) errs.customer = '请选择客户'; if (!businessDept) errs.businessDept = '请选择业务部门'; - if (!contractOriginal) errs.contractOriginal = '请上传合同原件'; + if (!contractOriginalFiles || contractOriginalFiles.length === 0) errs.contractOriginal = '请上传合同原件'; if (!businessOwner) errs.businessOwner = '请选择业务负责人'; if (!projectName || !projectName.trim()) errs.projectName = '请输入项目名称'; if (!contractType) errs.contractType = '请选择合同类型'; @@ -270,10 +324,25 @@ const Component = function() { if (!paymentPeriod) errs.paymentPeriod = '请选择付款周期'; if (!signingCompany) errs.signingCompany = '请选择签约公司'; if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; + if (!deliveryLocation || !deliveryLocation.trim()) errs.deliveryLocation = '请输入交车地点'; var authInvalid = authorizedList.some(function(a) { return !a.name || !a.name.trim() || !a.phone || !a.phone.trim() || !a.idCard || !a.idCard.trim(); }); if (authInvalid) errs.authorizedList = '请完整填写被授权人姓名、联系电话、身份证'; var rentalInvalid = rentalOrders.some(function(r) { return !r.brand || !r.model || !(r.monthRent && String(r.monthRent).trim()) || !(r.deposit && String(r.deposit).trim()); }); if (rentalInvalid) errs.rentalOrders = '请完整填写租赁订单的品牌、型号、车辆月租金、保证金'; + var serviceInvalid = rentalOrders.some(function(r) { + var items = r.serviceItems || []; + for (var i = 0; i < items.length; i++) { + var it = items[i] || {}; + var hasAny = (it.project && String(it.project).trim()) || (it.fee && String(it.fee).trim()) || (it.effectiveDate && String(it.effectiveDate).trim()); + if (hasAny) { + if (!(it.project && String(it.project).trim())) return true; + if (!(it.fee && String(it.fee).trim())) return true; + if (!(it.effectiveDate && String(it.effectiveDate).trim())) return true; + } + } + return false; + }); + if (serviceInvalid) errs.serviceItems = '请完整填写服务项目的服务项目、费用、生效时间'; if (!hydrogenBearer) errs.hydrogenBearer = '请选择氢费承担方'; if (hydrogenBearer === '客户' && !hydrogenPaymentMethod) errs.hydrogenPaymentMethod = '请选择付款方式'; if (hydrogenBearer === '客户' && hydrogenPaymentMethod === '预付' && (!hydrogenPrepay || !String(hydrogenPrepay).trim())) errs.hydrogenPrepay = '请输入氢气预付款'; @@ -282,7 +351,12 @@ const Component = function() { if (!billingMethod) errs.billingMethod = '请选择账单计算方式'; setFormErrors(errs); if (Object.keys(errs).length > 0) { - var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; + var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' + : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.deliveryLocation || errs.contractOriginal) ? 'card-contract' + : errs.authorizedList ? 'card-authorized' + : (errs.rentalOrders || errs.serviceItems || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' + : errs.feeTemplate ? 'card-fee' + : 'card-billing'; setCc1(false); setCc2(false); setCc3(false); setCc4(false); setCc5(false); setCc6(false); setTimeout(function() { var el = document.getElementById(firstId); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); return false; @@ -291,14 +365,17 @@ const Component = function() { }; var addAuthorized = function() { + setEdited(true); setAuthorizedList(authorizedList.concat([{ name: '', phone: '', idCard: '' }])); }; var removeAuthorized = function(index) { + setEdited(true); var next = authorizedList.slice(0); next.splice(index, 1); setAuthorizedList(next.length ? next : [{ name: '', phone: '', idCard: '' }]); }; var updateAuthorized = function(index, field, value) { + setEdited(true); var next = authorizedList.slice(0); var cur = next[index] || {}; var patch = {}; @@ -308,14 +385,17 @@ const Component = function() { }; var addRentalRow = function() { + setEdited(true); setRentalOrders(rentalOrders.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }])); }; var removeRentalRow = function(index) { + setEdited(true); var next = rentalOrders.slice(0); next.splice(index, 1); setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); }; var updateRentalOrder = function(index, field, value) { + setEdited(true); var next = rentalOrders.slice(0); var cur = next[index] || {}; var newRow = Object.assign({}, cur); @@ -323,6 +403,10 @@ const Component = function() { if (field === 'plateNo') { var v = vehicleList.find(function(x) { return x.plateNo === value; }); newRow.vin = v ? v.vin : ''; + if (v && v.brand) { + newRow.brand = v.brand; + newRow.model = v.model || ''; + } } if (field === 'brand') newRow.model = ''; next[index] = newRow; @@ -332,6 +416,7 @@ const Component = function() { var closeServiceModal = function() { setServiceModalRowIndex(null); }; var addServiceItem = function() { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); @@ -343,6 +428,7 @@ const Component = function() { }; var removeServiceItem = function(siIndex) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(0); @@ -356,6 +442,7 @@ const Component = function() { }; var updateServiceItem = function(siIndex, field, value) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(0); @@ -473,7 +560,7 @@ const Component = function() { 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 }, - 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: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, disabled: true, 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%' } })), React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), @@ -485,52 +572,50 @@ const Component = function() { React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), - React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), - 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); }))) + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setEdited(true); setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setEdited(true); 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 }, - 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.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) { 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 })), - React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))), + 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: '后付' }, '后付'))), React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : React.createElement('span', { style: { color: '#999' } }, '根据租赁订单自动反写'))), - 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(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(); 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.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 } }, '续签时需重新上传合同原件附件'), - contractOriginal - ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, - React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginal(); } }, contractOriginal.name), - contractOriginal.size || contractOriginal.uploadTime ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size ? contractOriginal.size : '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null - ), - React.createElement(Button, { type: 'button', size: 'small', danger: true, onClick: function() { setContractOriginal(null); } }, '删除') - ) - : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement('input', { ref: contractOriginalRef, type: 'file', style: { display: 'none' }, onChange: function(e) { - var f = e.target.files && e.target.files[0]; - if (f) { - var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; - var now = window.moment ? window.moment() : new Date(); - var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); - setContractOriginal({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); - } - 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('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: '交车地点' }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, style: { width: '100%' } })) + 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 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('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, 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' } })), @@ -541,10 +626,10 @@ const Component = function() { var hydrogenFormRow = (function() { var hydrogenFields = []; - hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); - if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } - var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); - 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%' } }); + hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setEdited(true); setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); + if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setEdited(true); setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } + var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setEdited(true); setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setEdited(true); 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)); @@ -591,7 +676,14 @@ const Component = function() { var rentalTableTbody = React.createElement('tbody', null, rentalTableBody); var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody); var rentalTableWrap = React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl); - var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); + var rentalContent = React.createElement('div', null, + rentalSummary, + formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, + formErrors.serviceItems ? React.createElement('div', { style: styles.errMsg }, formErrors.serviceItems) : null, + rentalTableWrap, + React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), + hydrogenFormRow + ); var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); @@ -617,7 +709,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('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) { setEdited(true); 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, @@ -633,8 +725,8 @@ const Component = function() { var billingBtnBase = { padding: '12px 16px', border: '1px solid #d9d9d9', borderRadius: 0, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14, textAlign: 'left', flex: 1, minWidth: 0, width: 0, height: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', overflow: 'visible' }; var billingContent = React.createElement('div', null, React.createElement('div', { style: { display: 'flex', border: formErrors.billingMethod ? '1px solid #ff4d4f' : '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden', alignItems: 'stretch' } }, - React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), - React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setEdited(true); setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setEdited(true); setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) ), formErrors.billingMethod ? React.createElement('div', { style: styles.errMsg }, formErrors.billingMethod) : null ); @@ -642,31 +734,20 @@ const Component = function() { var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; - var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; - var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; - var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; - var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; - var reqSpecBlock = { marginBottom: 8 }; - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-新增租赁合同')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) - ); - var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) - ); - var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) - ); - 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: 640 }), 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, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + 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.合同原件:从原合同自动反写,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.12.备注:从原合同自动反写,如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx,文本域,支持自定义输入备注信息;\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' }) }, + 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; 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: '#1890ff' } }, '合同续签')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + 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); } }, '查看需求说明')), 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'); } }, '客户基本信息'), @@ -685,6 +766,20 @@ const Component = function() { React.createElement('div', { style: { height: 60 } }), serviceModalContent, reqSpecModalContent, - React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { message.success('租赁合同已提交审核。'); } } }, '提交并审核'), React.createElement(Button, { onClick: function() { message.info('保存,加入租赁合同列表(仅操作人可查看编辑)'); } }, '保存'), React.createElement(Button, { onClick: function() { message.info('取消'); } }, '取消')) + cancelConfirmOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setCancelConfirmOpen(false); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 520 }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, '提示'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '20px 24px' }) }, '取消将会丢失所有已填写内容,是否确认?'), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: function() { setCancelConfirmOpen(false); } }, '否'), + React.createElement(Button, { type: 'primary', onClick: function() { setCancelConfirmOpen(false); setEdited(false); message.info('已返回车辆租赁合同列表(原型)'); } }, '是') + ) + ) + ) : null, + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { setEdited(false); message.success('租赁合同已提交审核。同时该租赁合同重新进入租赁合同审核列表中;'); } } }, '提交并审核'), + React.createElement(Button, { onClick: function() { setEdited(false); message.success('已保存(不校验必填项),该条数据只能保存人自己查看并编辑,其他人无法操作;'); } }, '保存'), + React.createElement(Button, { onClick: function() { if (edited) setCancelConfirmOpen(true); else message.info('已返回车辆租赁合同列表(原型)'); } }, '取消') + ) ); }; diff --git a/web端/车辆租赁合同/转正式合同.jsx b/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx similarity index 64% rename from web端/车辆租赁合同/转正式合同.jsx rename to web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx index 83a78af..1bb1fea 100644 --- a/web端/车辆租赁合同/转正式合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx @@ -30,7 +30,12 @@ const Component = function() { var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; var brandList = ['品牌A', '品牌B', '品牌C', '品牌D']; var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2', '型号C3'], '品牌D': ['型号D1'] }; - var vehicleList = [{ plateNo: '浙A10001', vin: 'L1234567890ABCDEF' }, { plateNo: '浙B20002', vin: 'L2234567890ABCDEF' }, { plateNo: '沪A30003', vin: 'L3234567890ABCDEF' }, { plateNo: '粤A40004', vin: 'L4234567890ABCDEF' }]; + var vehicleList = [ + { plateNo: '浙A10001', vin: 'L1234567890ABCDEF', brand: '品牌A', model: '型号A1' }, + { plateNo: '浙B20002', vin: 'L2234567890ABCDEF', brand: '品牌A', model: '型号A2' }, + { plateNo: '沪A30003', vin: 'L3234567890ABCDEF', brand: '品牌B', model: '型号B1' }, + { plateNo: '粤A40004', vin: 'L4234567890ABCDEF', brand: '品牌C', model: '型号C2' } + ]; var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板']; var prevContractSample = { @@ -39,16 +44,16 @@ const Component = function() { businessOwner: '张经理', contractOriginal: null, projectName: '嘉兴氢能运输项目', - contractType: '正式合同', + contractType: '试用合同', effectiveDate: '2027-02-17', paymentMethod: '预付', - endDate: '2028-02-17', + endDate: '', paymentPeriod: '1', signingCompany: '嘉兴羚牛', deliveryProvince: '浙江省', deliveryCity: '嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', - remarks: '转正式合同自原合同 JXZL20260216YW101235A', + remarks: '', authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], rentalOrders: [ { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }], deposit: '10000', remark: '' }, @@ -82,14 +87,17 @@ const Component = function() { var ownerFocusError = cs6[0]; var setOwnerFocusError = cs6[1]; var contractOriginalRef = React.useRef(null); - var csContractOriginal = React.useState(null); - var contractOriginal = csContractOriginal[0]; - var setContractOriginal = csContractOriginal[1]; + var csContractOriginal = React.useState([ + { name: '租赁合同-试用版原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30', isOriginal: true } + ]); + var contractOriginalFiles = csContractOriginal[0]; + var setContractOriginalFiles = csContractOriginal[1]; var bs1 = React.useState(prevContractSample.projectName); var projectName = bs1[0]; var setProjectName = bs1[1]; - var bs2 = React.useState(prevContractSample.contractType); + // 转正式合同:合同类型自动变更为正式合同 + var bs2 = React.useState('正式合同'); var contractType = bs2[0]; var setContractType = bs2[1]; var bs3 = React.useState(prevContractSample.effectiveDate); @@ -98,7 +106,7 @@ const Component = function() { var bs4 = React.useState(prevContractSample.paymentMethod); var paymentMethod = bs4[0]; var setPaymentMethod = bs4[1]; - var bs6 = React.useState(prevContractSample.endDate); + var bs6 = React.useState(''); var endDate = bs6[0]; var setEndDate = bs6[1]; var bs7 = React.useState(prevContractSample.paymentPeriod); @@ -120,7 +128,7 @@ const Component = function() { var bs11 = React.useState(prevContractSample.deliveryLocation); var deliveryLocation = bs11[0]; var setDeliveryLocation = bs11[1]; - var bs12 = React.useState(prevContractSample.remarks); + var bs12 = React.useState('转正式合同自:旧合同编码JXZL20260216YW101235A'); var remarks = bs12[0]; var setRemarks = bs12[1]; @@ -190,6 +198,12 @@ const Component = function() { var reqSpecState = React.useState(false); var reqSpecOpen = reqSpecState[0]; var setReqSpecOpen = reqSpecState[1]; + var cancelConfirmState = React.useState(false); + var cancelConfirmOpen = cancelConfirmState[0]; + var setCancelConfirmOpen = cancelConfirmState[1]; + var editedState = React.useState(false); + var edited = editedState[0]; + var setEdited = editedState[1]; var formErrorsState = React.useState({}); var formErrors = formErrorsState[0]; var setFormErrors = formErrorsState[1]; @@ -230,12 +244,14 @@ const Component = function() { var rentalTotalHydrogen = (hydrogenPaymentMethod === '预付' && hydrogenBearer === '客户') ? (parseFloat(hydrogenPrepay) || 0) : 0; var selectCustomer = function(c) { + setEdited(true); setSelectedCustomer(c); setCustomerSearch(c ? c.name : ''); setCustomerDropdownOpen(false); }; var deliveryRegionDisplay = deliveryProvince && deliveryCity ? deliveryProvince + ' / ' + deliveryCity : ''; var selectDeliveryRegion = function(province, city) { + setEdited(true); setDeliveryProvince(province); setDeliveryCity(city); setDeliveryRegionOpen(false); @@ -249,31 +265,80 @@ const Component = function() { else setOwnerFocusError(''); }; var handleOwnerBlur = function() { setOwnerFocusError(''); }; - var openContractOriginal = function() { - if (contractOriginal && contractOriginal.file) { - var url = URL.createObjectURL(contractOriginal.file); + var pad2 = function(n) { + var s = String(n); + return s.length === 1 ? ('0' + s) : s; + }; + var addContractOriginalFiles = function(fileList) { + if (!fileList || !fileList.length) return; + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + for (var i = 0; i < fileList.length; i++) { + var f = fileList[i]; + if (!f) continue; + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : (now.getFullYear() + '-' + pad2(now.getMonth() + 1) + '-' + pad2(now.getDate()) + ' ' + pad2(now.getHours()) + ':' + pad2(now.getMinutes())); + next.push({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr, isOriginal: false }); + } + return next; + }); + }; + var removeContractOriginalFile = function(index) { + var f = contractOriginalFiles && contractOriginalFiles[index]; + if (f && f.isOriginal) return; + setEdited(true); + setContractOriginalFiles(function(prev) { + var next = (prev || []).slice(); + next.splice(index, 1); + return next; + }); + }; + var openContractOriginalFile = function(index) { + var f = contractOriginalFiles && contractOriginalFiles[index]; + if (!f) return; + if (f.file) { + var url = URL.createObjectURL(f.file); window.open(url); + return; } + message.info('原合同反写附件(原型),暂无可预览源文件'); }; var validateSubmitAndReview = function() { var errs = {}; if (!selectedCustomer) errs.customer = '请选择客户'; if (!businessDept) errs.businessDept = '请选择业务部门'; - if (!contractOriginal) errs.contractOriginal = '请上传合同原件'; + if (!contractOriginalFiles || contractOriginalFiles.length === 0) errs.contractOriginal = '请上传合同原件'; if (!businessOwner) errs.businessOwner = '请选择业务负责人'; if (!projectName || !projectName.trim()) errs.projectName = '请输入项目名称'; - if (!contractType) errs.contractType = '请选择合同类型'; + // 转正式合同:合同类型固定为正式合同,无需校验 if (!effectiveDate) errs.effectiveDate = '请选择生效日期'; if (!paymentMethod) errs.paymentMethod = '请选择付款方式'; if (!endDate) errs.endDate = '请选择结束日期'; if (!paymentPeriod) errs.paymentPeriod = '请选择付款周期'; if (!signingCompany) errs.signingCompany = '请选择签约公司'; if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; + if (!deliveryLocation || !deliveryLocation.trim()) errs.deliveryLocation = '请输入交车地点'; var authInvalid = authorizedList.some(function(a) { return !a.name || !a.name.trim() || !a.phone || !a.phone.trim() || !a.idCard || !a.idCard.trim(); }); if (authInvalid) errs.authorizedList = '请完整填写被授权人姓名、联系电话、身份证'; var rentalInvalid = rentalOrders.some(function(r) { return !r.brand || !r.model || !(r.monthRent && String(r.monthRent).trim()) || !(r.deposit && String(r.deposit).trim()); }); if (rentalInvalid) errs.rentalOrders = '请完整填写租赁订单的品牌、型号、车辆月租金、保证金'; + var serviceInvalid = rentalOrders.some(function(r) { + var items = r.serviceItems || []; + for (var i = 0; i < items.length; i++) { + var it = items[i] || {}; + var hasAny = (it.project && String(it.project).trim()) || (it.fee && String(it.fee).trim()) || (it.effectiveDate && String(it.effectiveDate).trim()); + if (hasAny) { + if (!(it.project && String(it.project).trim())) return true; + if (!(it.fee && String(it.fee).trim())) return true; + if (!(it.effectiveDate && String(it.effectiveDate).trim())) return true; + } + } + return false; + }); + if (serviceInvalid) errs.serviceItems = '请完整填写服务项目的服务项目、费用、生效时间'; if (!hydrogenBearer) errs.hydrogenBearer = '请选择氢费承担方'; if (hydrogenBearer === '客户' && !hydrogenPaymentMethod) errs.hydrogenPaymentMethod = '请选择付款方式'; if (hydrogenBearer === '客户' && hydrogenPaymentMethod === '预付' && (!hydrogenPrepay || !String(hydrogenPrepay).trim())) errs.hydrogenPrepay = '请输入氢气预付款'; @@ -282,7 +347,7 @@ const Component = function() { if (!billingMethod) errs.billingMethod = '请选择账单计算方式'; setFormErrors(errs); if (Object.keys(errs).length > 0) { - var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; + var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.deliveryLocation || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.serviceItems || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; setCc1(false); setCc2(false); setCc3(false); setCc4(false); setCc5(false); setCc6(false); setTimeout(function() { var el = document.getElementById(firstId); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); return false; @@ -291,14 +356,17 @@ const Component = function() { }; var addAuthorized = function() { + setEdited(true); setAuthorizedList(authorizedList.concat([{ name: '', phone: '', idCard: '' }])); }; var removeAuthorized = function(index) { + setEdited(true); var next = authorizedList.slice(0); next.splice(index, 1); setAuthorizedList(next.length ? next : [{ name: '', phone: '', idCard: '' }]); }; var updateAuthorized = function(index, field, value) { + setEdited(true); var next = authorizedList.slice(0); var cur = next[index] || {}; var patch = {}; @@ -308,14 +376,17 @@ const Component = function() { }; var addRentalRow = function() { + setEdited(true); setRentalOrders(rentalOrders.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }])); }; var removeRentalRow = function(index) { + setEdited(true); var next = rentalOrders.slice(0); next.splice(index, 1); setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); }; var updateRentalOrder = function(index, field, value) { + setEdited(true); var next = rentalOrders.slice(0); var cur = next[index] || {}; var newRow = Object.assign({}, cur); @@ -323,6 +394,10 @@ const Component = function() { if (field === 'plateNo') { var v = vehicleList.find(function(x) { return x.plateNo === value; }); newRow.vin = v ? v.vin : ''; + if (v && v.brand) { + newRow.brand = v.brand; + newRow.model = v.model || ''; + } } if (field === 'brand') newRow.model = ''; next[index] = newRow; @@ -332,6 +407,7 @@ const Component = function() { var closeServiceModal = function() { setServiceModalRowIndex(null); }; var addServiceItem = function() { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); @@ -343,6 +419,7 @@ const Component = function() { }; var removeServiceItem = function(siIndex) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(0); @@ -356,6 +433,7 @@ const Component = function() { }; var updateServiceItem = function(siIndex, field, value) { if (serviceModalRowIndex === null) return; + setEdited(true); var next = rentalOrders.slice(0); var row = next[serviceModalRowIndex]; var items = (row.serviceItems || []).slice(0); @@ -473,7 +551,7 @@ const Component = function() { 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 }, - 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: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, disabled: true, 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%' } })), React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), @@ -485,52 +563,50 @@ const Component = function() { React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), - React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), - 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); }))) + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setEdited(true); setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setEdited(true); 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 }, - 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.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) { 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 })), - React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))), + 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: '后付' }, '后付'))), React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : React.createElement('span', { style: { color: '#999' } }, '根据租赁订单自动反写'))), - 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(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(); 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.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 } }, '转正式合同时需重新上传合同原件附件'), - contractOriginal - ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, - React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginal(); } }, contractOriginal.name), - contractOriginal.size || contractOriginal.uploadTime ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size ? contractOriginal.size : '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null - ), - React.createElement(Button, { type: 'button', size: 'small', danger: true, onClick: function() { setContractOriginal(null); } }, '删除') - ) - : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement('input', { ref: contractOriginalRef, type: 'file', style: { display: 'none' }, onChange: function(e) { - var f = e.target.files && e.target.files[0]; - if (f) { - var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; - var now = window.moment ? window.moment() : new Date(); - var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); - setContractOriginal({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); - } - 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('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: '交车地点' }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, style: { width: '100%' } })) + 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 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('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, 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' } })), @@ -541,10 +617,10 @@ const Component = function() { var hydrogenFormRow = (function() { var hydrogenFields = []; - hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); - if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } - var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); - 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%' } }); + hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setEdited(true); setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); + if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setEdited(true); setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } + var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setEdited(true); setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setEdited(true); 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)); @@ -591,7 +667,7 @@ const Component = function() { var rentalTableTbody = React.createElement('tbody', null, rentalTableBody); var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody); var rentalTableWrap = React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl); - var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); + var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, formErrors.serviceItems ? React.createElement('div', { style: styles.errMsg }, formErrors.serviceItems) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); @@ -617,7 +693,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('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) { setEdited(true); 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, @@ -633,8 +709,8 @@ const Component = function() { var billingBtnBase = { padding: '12px 16px', border: '1px solid #d9d9d9', borderRadius: 0, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14, textAlign: 'left', flex: 1, minWidth: 0, width: 0, height: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', overflow: 'visible' }; var billingContent = React.createElement('div', null, React.createElement('div', { style: { display: 'flex', border: formErrors.billingMethod ? '1px solid #ff4d4f' : '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden', alignItems: 'stretch' } }, - React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), - React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setEdited(true); setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setEdited(true); setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) ), formErrors.billingMethod ? React.createElement('div', { style: styles.errMsg }, formErrors.billingMethod) : null ); @@ -642,31 +718,119 @@ const Component = function() { var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; - var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; - var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; - var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; - var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; - var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; - var reqSpecBlock = { marginBottom: 8 }; - var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-转正式合同')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) - ); - var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) - ); - var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), - React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) - ); - 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: 640 }), 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, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + var requirementContent = `车辆租赁合同-转正式合同(2026年3月3日版本) +「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「转正式合同」模块,在车辆租赁合同操作列点击「转正式合同」进行操作; + +1.面包屑: +#业务管理-车辆租赁合同-转正式合同 + +2.客户基本信息卡片: +#用于从客户列表中选择客户,并将该合同绑定到业务部门及业务负责人(绑定业务部门/业务负责人主要为了后期从部门/业务负责人维度进行数据统计); +2.1.客户名称:从原合同自动反写,不可编辑,如原客户信息发生改变则按照新信息进行反写;必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户(只显示已通过审核的客户); +2.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段; +2.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段; +2.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段; +2.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段; +2.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段; +2.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段; +2.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段; +2.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段; +2.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段; +2.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段; +2.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段; +2.13.业务部门:从该条合同自动反写,必选项,选择器,从部门表中选择该租赁合同对应部门; +2.14.业务负责人:从该条合同自动反写,必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人字段不可选; + +3.合同基本信息卡片: +#用于定义租赁合同基本情况和付款方式; +3.1.项目名称:从原合同自动反写,必填项,输入框,用于定义该合同项目名称,默认提示信息"请输入项目名称"; +3.2.合同类型:自动变更为正式合同,必选项,选择器,合同类型分为「正式合同」「试用合同」; +3.3.生效日期:从原合同自动反写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期; +3.4.付款方式:从原合同自动反写,必选项,付款方式分为「预付」「后付」两种; + 3.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户; + 3.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户; +3.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称; +3.6.结束日期:需要重新填写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天; + 3.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台); + 3.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单; + 3.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理; +3.7.付款周期:从原合同自动反写,必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单; +3.8.签约公司:从原合同自动反写,必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据; +3.9.交车区域:从原合同自动反写,必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员; +3.10.交车地点:从原合同自动反写,必填项,输入框,支持自定义输入交车地点; +3.11.合同原件:从原合同自动反写,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式); +3.12.备注:从原合同自动反写,如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx,文本域,支持自定义输入备注信息; + +4.被授权人信息卡片: +#用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认; +4.1.被授权人:从原合同自动反写,必填项,输入框,用于输入被授权人信息; +4.2.被授权人联系电话:从原合同自动反写,必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接; +4.3.被授权人身份证:从原合同自动反写,必填项,输入框,用于输入被授权人身份证信息; +4.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可从多个授权人中选择接收授权人; + +5.租赁订单信息卡片: +#用于定义租赁合同对应车辆明细费用、氢费明细费用等相关信息; +5.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息; + 5.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车; + 5.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额; + 5.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额; + 5.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额; +5.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注,续签合同时,根据续签时的车辆信息填写(包括车牌号); + 5.2.1.序号:从原合同自动反写,自动按照条数生成,规则为1、2、3....以此类推; + 5.2.2.品牌:从原合同自动反写,必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌; + 5.2.3.型号:从原合同自动反写,必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号; + 5.2.4.车牌号:从原合同自动反写,选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写; + 5.2.5.车辆识别代码:从原合同自动反写,输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写; + 5.2.6.车辆月租金:从原合同自动反写,输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算; + 5.2.7.服务费项目:从原合同自动反写,点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作; + 5.2.7.1.服务项目:从原合同自动反写,必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板; + 5.2.7.2.费用:从原合同自动反写,必填项,输入框,支持2位小数,输入框后缀为元; + 5.2.7.3.生效时间:从原合同自动反写,必选项,日期选择器,格式为YYYY-MM-DD; + 5.2.7.4.操作:删除,点击删除直接删除该行数据; + 5.2.7.5.新增一行数据:点击添加一行服务项目; + #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如: + 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算; + 5.2.8.服务费:从原合同自动反写,自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元; + 5.2.9.保证金:从原合同自动反写,必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中; + 5.2.10.备注:选填项,输入框,用于备注车辆复杂情况; + 5.2.11.操作:删除,点击删除删除该行数据; + 5.2.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息; +5.3.氢费承担方:从原合同自动反写,必选项,选择器,选项为:「我方」、「客户」; + 5.3.1.选择「我方」:不显示付款方式、氢气预付款; + 5.3.2.选择「客户」:付款方式字段默认为预付,可手动修改; +5.4.付款方式:从原合同自动反写,必选项,选择器,选项为:「预付」、「月付款」、「自行结算」; + 5.4.1.预付:选择「预付」,需要填写:「氢气预付款」,氢气预付款指合同签署时客户就需预先付出的氢费款项,该部分款项会自动计入提车应收款中氢气预付款金额; + 5.4.2.月付款:选择「月付款」,提车应收款中不进行收费,而是由业务人员按照实际情况,通过氢费账单功能生成对应氢费账单,单独与客户进行结算; + 5.4.3.自行结算:选择「自行结算」,指合同签署后,所有氢气费用由客户自行承担; +5.5.氢气预付款:从原合同自动反写,选择「客户」「预付」时显示,必填项,输入框,支持2位小数,氢气预付款金额会计算入该合同交车应收款中,并计入5.1.4.氢气预付款金额中; +5.6.退还车氢气单价:从原合同自动反写,必填项,输入框,支持2位小数,后缀为元,不管氢费承担方和付款方式选择任何选项都需要进行维护,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算; + +6.其他费用信息卡片: +#用于选择对应租赁费用模板,选择后展示证照补办费用、违约金费用、易损件费用、其他费用等信息,租赁费用模板管理功能位于「车辆租赁合同」列表左上角; +6.1.选择费用模板:从原合同自动反写,如果费用模板有更新,则按照更新后内容显示,必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中; + 6.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显; + 6.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显; + 6.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显; + 6.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显; + +7.账单计算方式卡片: +#必选项,从原合同自动反写,填充按钮组,可手动修改,用于定义租赁合同的账单计算方式,分为「按自然月结算」、「按付款周期天数结算」两种方式; +7.1.按付款周期天数结算:账单按照合同基本信息卡片中每隔付款周期*30天形成一期账单; + 例如付款周期为2个月,则交车任务交车成功时,提车应收款从交车任务配置的开始计费时间开始,根据60天收取车辆租金,此后每隔60天生成一期租赁账单; +7.2.按自然月结算:账单按照第一个月计费开始日期到当月最后一天为第一期,之后按照付款周期所选月份间隔,从开始月份第一天到间隔月份最后一天的自然月方式形成一期账单; + 例如付款周期为2个月,则交车任务交车成功时,提车应收款车辆租金需要收取≥2个月租金作为标准流程,<2个月租金作为非标流程; + 租赁账单首期从交车任务配置的开始计费时间开始,如付款周期为2个月,则首期账单结束时间为第二个月最后一天,付款周期为3个月,则首期账单结束时间为第三个月最后一天,此后每一期按照实际自然月开始-结束形成账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推; + +8.最下方为提交并审核、保存、取消三个按钮; +8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同重新进入租赁合同审核列表中; +8.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作; +8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页; + +所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;`; + 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' }) }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent)), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; return React.createElement('div', { style: styles.page }, - React.createElement('div', { style: { 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('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); } }, '查看需求说明')), 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'); } }, '客户基本信息'), @@ -685,6 +849,20 @@ const Component = function() { React.createElement('div', { style: { height: 60 } }), serviceModalContent, reqSpecModalContent, - React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { message.success('租赁合同已提交审核。'); } } }, '提交并审核'), React.createElement(Button, { onClick: function() { message.info('保存,加入租赁合同列表(仅操作人可查看编辑)'); } }, '保存'), React.createElement(Button, { onClick: function() { message.info('取消'); } }, '取消')) + cancelConfirmOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setCancelConfirmOpen(false); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 520 }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, '提示'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '20px 24px' }) }, '取消将会丢失所有已填写内容,是否确认?'), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: function() { setCancelConfirmOpen(false); } }, '否'), + React.createElement(Button, { type: 'primary', onClick: function() { setCancelConfirmOpen(false); setEdited(false); message.info('已返回车辆租赁合同列表(原型)'); } }, '是') + ) + ) + ) : null, + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { setEdited(false); message.success('租赁合同已提交审核。同时该租赁合同重新进入租赁合同审核列表中。'); } } }, '提交并审核'), + React.createElement(Button, { onClick: function() { setEdited(false); message.success('已保存(不校验必填项),该条数据只能保存人自己查看并编辑,其他人无法操作。'); } }, '保存'), + React.createElement(Button, { onClick: function() { if (edited) setCancelConfirmOpen(true); else message.info('已返回车辆租赁合同列表(原型)'); } }, '取消') + ) ); }; diff --git a/web端/车辆租赁合同/车辆租赁合同.jsx b/web端/车辆租赁合同/车辆租赁合同.jsx index 1ef0f38..d446de9 100644 --- a/web端/车辆租赁合同/车辆租赁合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同.jsx @@ -61,6 +61,8 @@ const Component = function() { var _deleteModalRecord = useState(null); var _withdrawModalVisible = useState(false); var _withdrawModalRecord = useState(null); + var _terminateModalVisible = useState(false); + var _terminateModalRecord = useState(null); var _requirementModalVisible = useState(false); // 模拟选项(与新增租赁合同保持一致) @@ -91,22 +93,24 @@ const Component = function() { { value: '上海羚牛', label: '上海羚牛' }, { value: '广东羚牛', label: '广东羚牛' } ]; + // 审批状态:待审批、审批中、审批通过、审批驳回、未提交(无已结束,已结束归属合同状态) var approvalStatusOptions = [ { value: '全部', label: '全部' }, { value: '待审批', label: '待审批' }, { value: '审批中', label: '审批中' }, { value: '审批通过', label: '审批通过' }, { value: '审批驳回', label: '审批驳回' }, - { value: '未提交', label: '未提交' }, - { value: '已结束', label: '已结束' } + { value: '未提交', label: '未提交' } ]; + // 合同状态:草稿、变更、合同进行中、到期合同、已提交审批、已结束 var contractStatusOptions = [ { value: '全部', label: '全部' }, { value: '草稿', label: '草稿' }, { value: '变更', label: '变更' }, { value: '合同进行中', label: '合同进行中' }, { value: '到期合同', label: '到期合同' }, - { value: '已提交审批', label: '已提交审批' } + { value: '已提交审批', label: '已提交审批' }, + { value: '已结束', label: '已结束' } ]; var contractTypeOptions = [ { value: '全部', label: '全部' }, @@ -127,8 +131,7 @@ const Component = function() { ]; // 模拟列表数据:按审批状态 × 合同状态组合生成样例 - // 审批状态:待审批、审批中、审批通过、审批驳回、未提交、已结束 - // 合同状态:草稿、变更、合同进行中、到期合同、已提交审批 + // 合同状态:草稿、变更、合同进行中、到期合同、已提交审批、已结束 var rawList = [ // 1. 未提交 + 草稿(仅保存未提交) { @@ -335,7 +338,7 @@ const Component = function() { updateTime: '2025-02-23 09:00', remark: '驳回原因:费用条款需调整' }, - // 9. 已结束 + 到期合同(审批已通过但合同结束日期已过) + // 9. 审批通过 + 到期合同(审批已通过但合同结束日期已过) { id: '9', contractCode: 'HT-ZL-2024-009', @@ -345,7 +348,7 @@ const Component = function() { { 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' } ], - approvalStatus: '已结束', + approvalStatus: '审批通过', contractStatus: '到期合同', customerName: '杭州某某租赁有限公司', signingCompany: '嘉兴羚牛', @@ -361,7 +364,7 @@ const Component = function() { updateTime: '2024-12-20 16:00', remark: '已到期可续签' }, - // 10. 已结束 + 到期合同(合同日期已过) + // 10. 审批通过 + 已结束(操作列终止合同并完成审核) { id: '10', contractCode: 'HT-ZL-2024-010', @@ -370,8 +373,8 @@ const Component = function() { vehicles: [ { vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '苏L00100', actualDelivery: '2024-06-01 11:00' } ], - approvalStatus: '已结束', - contractStatus: '到期合同', + approvalStatus: '审批通过', + contractStatus: '已结束', customerName: '嘉兴某某物流有限公司', signingCompany: '嘉兴羚牛', businessDept: '业务1部', @@ -613,12 +616,12 @@ const Component = function() { var approval = record.approvalStatus; var items = []; if (status === '草稿') { - items.push({ key: 'edit', label: '编辑合同', onClick: function() { message.info('编辑合同(原型)'); } }); + items.push({ key: 'edit', label: '编辑', onClick: function() { message.info('编辑(原型)'); } }); items.push({ key: 'del', label: '删除合同', danger: true, onClick: function() { _deleteModalRecord[1](record); _deleteModalVisible[1](true); } }); } if (status === '合同进行中') { items.push({ key: 'addVehicle', label: '新增车辆', onClick: function() { message.info('新增车辆(原型)'); } }); - items.push({ key: 'renew', label: '合同续签', onClick: function() { message.info('合同续签(原型)'); } }); + items.push({ key: 'renew', label: '续签合同', onClick: function() { message.info('续签合同(原型)'); } }); items.push({ key: 'authorized', label: '添加被授权人', onClick: function() { _authorizedModalRecord[1](record); _authorizedList[1]([{ name: '', phone: '', idCard: '' }]); _authorizedModalVisible[1](true); } }); items.push({ key: 'extraFee', label: '附加费用', onClick: function() { _extraFeeModalRecord[1](record); @@ -636,15 +639,17 @@ const Component = function() { })); _extraFeeModalVisible[1](true); } }); + items.push({ key: 'toTripartite', label: '变更为三方合同', onClick: function() { message.info('变更为三方合同(原型)'); } }); + items.push({ key: 'terminate', label: '终止合同', danger: true, onClick: function() { _terminateModalRecord[1](record); _terminateModalVisible[1](true); } }); } if (status === '到期合同') { - items.push({ key: 'renew2', label: '合同续签', onClick: function() { message.info('合同续签(原型)'); } }); + items.push({ key: 'renew2', label: '续签合同', onClick: function() { message.info('续签合同(原型)'); } }); } if (status === '已提交审批' && approval !== '审批驳回') { items.push({ key: 'withdraw', label: '撤回合同', onClick: function() { _withdrawModalRecord[1](record); _withdrawModalVisible[1](true); } }); } if (approval === '审批驳回') { - items.push({ key: 'editReject', label: '编辑合同', onClick: function() { message.info('编辑合同(原型)'); } }); + items.push({ key: 'editReject', label: '编辑', onClick: function() { message.info('编辑(原型)'); } }); } if (type === '试用合同') { items.push({ key: 'toFormal', label: '转正式合同', onClick: function() { message.info('转正式合同(原型)'); } }); @@ -667,7 +672,7 @@ const Component = function() { return React.createElement( Space, { size: 4, wrap: true }, - React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('查看合同(原型)'); } }, '查看合同'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('查看(跳转车辆租赁合同-查看)'); } }, '查看'), moreDropdown ); } @@ -891,7 +896,7 @@ const Component = function() { React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 } }, React.createElement(Button, { onClick: function() { message.info('租赁费用模板(原型)'); } }, '租赁费用模板'), React.createElement('div', { style: { display: 'flex', gap: 8 } }, - React.createElement(Button, { type: 'primary', onClick: function() { message.info('请在原型菜单列表中点击:业务管理-车辆租赁合同-新建合同 查看具体原型'); } }, '新建'), + React.createElement(Button, { type: 'primary', onClick: function() { message.info('请在原型菜单列表中点击:业务管理-车辆租赁合同-新建合同 查看具体原型'); } }, '新增'), React.createElement(Button, { onClick: function() { message.info('根据筛选条件,导出相应记录'); } }, '导出') ) ), @@ -934,6 +939,18 @@ const Component = function() { okText: '确定', cancelText: '取消' }), + React.createElement(Modal, { + title: '是否确认终止合同', + open: _terminateModalVisible[0], + onCancel: function() { _terminateModalVisible[1](false); _terminateModalRecord[1](null); }, + onOk: function() { + message.success('已提交审核(原型),审核通过后合同状态变更为已结束'); + _terminateModalVisible[1](false); + _terminateModalRecord[1](null); + }, + okText: '提交审核', + cancelText: '取消' + }), React.createElement(Modal, { title: '添加被授权人', open: _authorizedModalVisible[0], @@ -993,7 +1010,8 @@ const Component = function() { 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: reqTitleStyle }, '租赁合同管理'), + React.createElement('div', { style: reqTitleStyle }, '车辆租赁合同(2026年3月3日版本)'), + React.createElement('div', { style: { fontSize: 14, marginBottom: 12, color: 'rgba(0,0,0,0.85)' } }, '一个「数字化资产ONEOS运管平台」中的「车辆租赁合同」模块'), React.createElement('div', { style: reqSectionStyle }, '1.面包屑:'), React.createElement('div', { style: reqSubStyle }, '1.1.业务管理-车辆租赁合同'), React.createElement('div', { style: reqSectionStyle }, '2.筛选:'), @@ -1002,15 +1020,15 @@ const Component = function() { React.createElement('div', { style: reqItemStyle }, '2.1.2.项目名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), React.createElement('div', { style: reqItemStyle }, '2.1.3.客户名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), React.createElement('div', { style: reqItemStyle }, '2.1.4.签约公司:选择器,显示租赁合同所有签约公司;'), - React.createElement('div', { style: reqItemStyle }, '2.1.5.审批状态:选择器,支持全选或多选,选项为:全部、待审批、审批中、审批通过、审批驳回、未提交、已结束,默认显示全部;'), - React.createElement('div', { style: reqItemStyle }, '2.1.6.合同状态:选择器,支持全选或多选,选项为:全部、草稿、变更、合同进行中、到期合同、已提交审批;'), + React.createElement('div', { style: reqItemStyle }, '2.1.5.审批状态:选择器,支持全选或多选,选项为:全部、待审批、审批中、审批通过、审批驳回、未提交,默认显示全部;'), + React.createElement('div', { style: reqItemStyle }, '2.1.6.合同状态:选择器,支持全选或多选,选项为:全部、草稿、变更、合同进行中、到期合同、已提交审批、已结束;'), React.createElement('div', { style: reqItemStyle }, '2.1.7.业务部门:选择器,支持全选或多选,拉取部门下所有业务相关部门;'), React.createElement('div', { style: reqItemStyle }, '2.1.8.业务负责人:选择器,支持全选或多选,拉取所有业务相关部门下所有用户姓名;'), React.createElement('div', { style: reqItemStyle }, '2.1.9.合同类型:选择器,支持全选或多选,选项为:全部、正式合同、试用合同;'), 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.车辆数:显示车辆数,点击车辆数,显示气泡卡片,卡片中列表显示:车辆类型、品牌、型号、车牌号、实际交车日期;'), @@ -1019,19 +1037,19 @@ const Component = function() { React.createElement('div', { style: reqSubItemStyle }, '3.1.3.3.型号:显示租赁合同中对应车辆型号;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.3.4.车牌号:显示租赁合同中对应车牌号,无则显示为-,有则显示具体车牌号;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.3.5.实际交车日期:显示租赁合同中对应车辆对应实际交车日期,精确至分钟,格式为YYYY-MM-DD HH:MM;'), - React.createElement('div', { style: reqItemStyle }, '3.1.4.审批状态:显示租赁合同实际审批状态,状态分为:待审批、审批中、审批通过、审批驳回、未提交、已结束;'), + React.createElement('div', { style: reqItemStyle }, '3.1.4.审批状态:显示租赁合同实际审批状态,状态分为:待审批、审批中、审批通过、审批驳回、未提交;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.4.1.待审批:发起人已提交,但还没有任何流程节点完成审批;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.4.2.审批中:发起人已提交,已有1个以上节点完成审批,但未完成最终节点审批;'), 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: reqItemStyle }, '3.1.5.合同状态:显示租赁合同状态,状态分为:草稿、变更、合同进行中、到期合同、已提交审批、已结束;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.5.1.草稿:发起人仅保存,但未提交审批;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.5.2.变更:发起人提交后,合同已通过审批的基础上,进行了「变更内容」操作,且已提交审批,但未完成最终节点审批;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.5.3.合同进行中:发起人提交后,合同完成最终节点审批;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.5.4.到期合同:发起人已提交,最终节点完成审批,但当前日期超过合同结束日期;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.5.5.已提交审批:发起人初次提交,但未完成最终节点审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.6.已结束:当前日期超过合同结束日期或在操作列终止合同并完成审核;'), React.createElement('div', { style: reqItemStyle }, '3.1.6.客户名称:显示租赁合同创建时所选客户名称,客户名称来自「客户管理」-「客户名称」;'), React.createElement('div', { style: reqItemStyle }, '3.1.7.签约公司:显示租赁合同创建时所选签约公司,签约公司来自组织机构表;'), React.createElement('div', { style: reqItemStyle }, '3.1.8.业务部门:显示租赁合同创建时所选业务部门,业务部门来自部门表(业务组);'), @@ -1045,19 +1063,21 @@ const Component = function() { React.createElement('div', { style: reqItemStyle }, '3.1.16.更新人:显示租赁合同最后一次更新人姓名,取自操作用户姓名,如无则显示:-;'), React.createElement('div', { style: reqItemStyle }, '3.1.17.最后更新时间:显示租赁合同最后一次更新时间,精确至分钟,格式为YYYY-MM-DD HH:MM,如无则显示:-;'), React.createElement('div', { style: reqItemStyle }, '3.1.18.备注:显示租赁合同创建时输入的备注信息,如无则显示:-;'), - React.createElement('div', { style: reqItemStyle }, '3.1.19.操作:操作分为:查看合同、编辑合同、新增车辆、合同续签、删除合同、撤回合同、添加被授权人、附加费用、转正式合同;'), - React.createElement('div', { style: reqSubItemStyle }, '3.1.19.1.查看合同:查看租赁合同详情;'), - React.createElement('div', { style: reqSubItemStyle }, '3.1.19.2.编辑合同:当「合同状态」为「草稿」时显示,点击跳转编辑租赁合同页面,编辑租赁合同页面可输入项参考「新增租赁合同」页面,并支持对保存时已填内容进行修改;'), + React.createElement('div', { style: reqItemStyle }, '3.1.19.操作:操作分为:查看、编辑、新增车辆、续签合同、删除合同、撤回合同、添加被授权人、附加费用、变更为三方合同、转正式合同、终止合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.1.查看:跳转车辆租赁合同-查看;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.2.编辑:当「合同状态」为「草稿」时显示,点击跳转编辑租赁合同页面,编辑租赁合同页面可输入项参考「新增租赁合同」页面,并支持对保存时已填内容进行修改;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.19.3.新增车辆:当「合同状态」为「合同进行中」时显示,仅能在租赁订单信息卡片下新订单中进行车辆新增;新增车辆提交后触发重新审核流程,审核通过后生效(不影响原有合同正常业务);'), - React.createElement('div', { style: reqSubItemStyle }, '3.1.19.4.合同续签:当「合同状态」为「合同进行中」、「合同到期」时显示,点击后跳转新增租赁合同页面,同时反写所有旧合同内信息,支持修改。此外,合同续签操作产生的合同,合同编码一栏中编码后方显示:(续签自:旧合同编码xxxx),同时查看新合同时下方变更记录中显示续签自哪个合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.4.续签合同:当「合同状态」为「合同进行中」、「合同到期」时显示,点击跳转车辆租赁合同-续签合同页,提交时重新触发租赁合同审核流程;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.19.5.删除合同:当「合同状态」为「草稿」时显示,点击删除合同时进行二次确认,提示语:是否确认删除该合同草稿;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.19.6.撤回合同:当「合同状态」为「已提交审核」时显示,点击撤回合同时进行二次确认,提示语:是否确认撤回该合同;'), React.createElement('div', { style: reqSubItemStyle }, '3.1.19.7.添加被授权人:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中可编辑被授权人、被授权人联系电话、被授权人身份证,同时支持添加一行、删除已有行等操作;添加被授权人触发重新审核流程,审核通过后生效;'), - React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.附加费用:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中显示该租赁合同内:车辆类型、品牌、型号、车牌号,同时可对服务项目、费用、生效时间进行编辑;'), - React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.1.服务项目:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), - React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.2.费用:输入框,后缀为元,支持2位小数输入;'), - React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.3.生效时间:日期选择器,精确至日;'), - React.createElement('div', { style: reqSubItemStyle }, '3.1.19.9.转正式合同:当「合同类型」为「试用合同」时显示,点击后跳转新增租赁合同页面,同时反写所有旧合同内信息,支持修改。此外,合同续签操作产生的合同,合同编码一栏中编码后方显示:(续签自:旧合同编码xxxx),同时查看新合同时下方变更记录中显示续签自哪个合同;') + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.附加费用:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中显示该租赁合同内:车辆类型、品牌、型号、车牌号,同时可对服务项目、费用、生效时间进行编辑,触发租赁合同审核流程,审核通过后生效;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.1.服务项目:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.2.费用:输入框,后缀为元,支持2位小数输入;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.3.生效时间:日期选择器,精确至日;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.9.变更为三方合同:当「合同状态」为「合同进行中」时显示,点击跳转车辆租赁合同-变更为三方合同页面,提交时重新触发租赁合同审核流程;;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.10.转正式合同:当「合同类型」为「试用合同」时显示,点击后跳转车辆租赁合同-转正式合同页面,提交时重新触发租赁合同审核流程;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.11.终止合同:当「合同状态」为「合同进行中」时显示,点击后进行二次确认,提示语:是否确认终止合同,确认按钮为提交审核,会重新发起合同审核流程,审核通过后,合同状态变更为:已结束;') )) ) );