diff --git a/web端/业务管理/查看交车任务.jsx b/web端/业务管理/查看交车任务.jsx index a49b522..b6245ff 100644 --- a/web端/业务管理/查看交车任务.jsx +++ b/web端/业务管理/查看交车任务.jsx @@ -1,15 +1,59 @@ // 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 -// 数字化资产ONEOS运管平台 - 查看交车任务模块(只读,布局参照新增交车任务) +// 数字化资产ONEOS运管平台 - 查看交车任务模块(只读表单 + 车辆列表还车操作) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; -const Component = function() { var antd = window.antd; var Input = antd.Input; var Button = antd.Button; var Modal = antd.Modal; + var Drawer = antd.Drawer; + var DatePicker = antd.DatePicker; + var RangePicker = DatePicker.RangePicker; + var Cascader = antd.Cascader; + var Space = antd.Space; + var Checkbox = antd.Checkbox; + var message = antd.message; - var reqModalOpen = React.useState(false); + var reqModalOpen = useState(false); + /** 交车单是否已点击「车辆到达」(到达后不可撤销还车) */ + var vehicleArrivedPair = useState(false); + var vehicleArrived = vehicleArrivedPair[0]; + var setVehicleArrived = vehicleArrivedPair[1]; + + var returnRegionOptions = [ + { value: '浙江省', label: '浙江省', children: [{ value: '杭州市', label: '杭州市' }, { value: '嘉兴市', label: '嘉兴市' }, { value: '宁波市', label: '宁波市' }] }, + { value: '上海市', label: '上海市', children: [{ value: '上海市', label: '上海市' }] }, + { value: '江苏省', label: '江苏省', children: [{ value: '南京市', label: '南京市' }, { value: '苏州市', label: '苏州市' }] }, + { value: '广东省', label: '广东省', children: [{ value: '广州市', label: '广州市' }, { value: '深圳市', label: '深圳市' }] }, + { value: '北京市', label: '北京市', children: [{ value: '北京市', label: '北京市' }] } + ]; + + var initialVehicles = [ + { seq: 1, brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliverySuccess: true, returnTaskCreated: false, returnTimeStart: null, returnTimeEnd: null, returnProvince: null, returnCity: null }, + { seq: 2, brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌', deliverySuccess: false, returnTaskCreated: false, returnTimeStart: null, returnTimeEnd: null, returnProvince: null, returnCity: null }, + { seq: 3, brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliverySuccess: true, returnTaskCreated: true, returnTimeStart: '2026-04-10', returnTimeEnd: '2026-04-12', returnProvince: '浙江省', returnCity: '嘉兴市' } + ]; + + var vehiclesPair = useState(initialVehicles); + var vehicles = vehiclesPair[0]; + var setVehicles = vehiclesPair[1]; + + var drawerOpenPair = useState(false); + var drawerOpen = drawerOpenPair[0]; + var setDrawerOpen = drawerOpenPair[1]; + var drawerRowIndexPair = useState(-1); + var drawerRowIndex = drawerRowIndexPair[0]; + var setDrawerRowIndex = drawerRowIndexPair[1]; + var drawerRangePair = useState(null); + var drawerRange = drawerRangePair[0]; + var setDrawerRange = drawerRangePair[1]; + var drawerRegionPair = useState([]); + var drawerRegion = drawerRegionPair[0]; + var setDrawerRegion = drawerRegionPair[1]; - // Mock:当前查看的交车任务详情(从列表跳转带入或根据 id 拉取) var task = { projectName: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', @@ -17,21 +61,109 @@ const Component = function() { deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', planDeliveryDisplay: '2026-03-01至2026-03-05', - billingStartDate: '2026-03-06', - vehicles: [ - { seq: 1, brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车' }, - { seq: 2, brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌' }, - { seq: 3, brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '' } - ] + billingStartDate: '2026-03-06' }; - var vehicleList = task.vehicles || []; + function toYmd(d) { + if (d == null) return ''; + if (typeof d === 'string') return d.slice(0, 10); + if (typeof d.format === 'function') return d.format('YYYY-MM-DD'); + return ''; + } + + function formatReturnTimeDisplay(row) { + if (!row.returnTaskCreated || !row.returnTimeStart) return '-'; + var s = row.returnTimeStart; + var e = row.returnTimeEnd || row.returnTimeStart; + if (s === e) return s; + return s + '至' + e; + } + + function formatReturnAreaDisplay(row) { + if (!row.returnProvince || !row.returnCity) return '-'; + return row.returnProvince + '-' + row.returnCity; + } + + function returnStatusText(row) { + return row.returnTaskCreated ? '已还车' : '未还车'; + } + + var openReturnDrawer = useCallback(function (index) { + setDrawerRowIndex(index); + setDrawerRange(null); + setDrawerRegion([]); + setDrawerOpen(true); + }, [setDrawerOpen, setDrawerRowIndex, setDrawerRange, setDrawerRegion]); + + var closeReturnDrawer = useCallback(function () { + setDrawerOpen(false); + setDrawerRowIndex(-1); + setDrawerRange(null); + setDrawerRegion([]); + }, [setDrawerOpen, setDrawerRowIndex, setDrawerRange, setDrawerRegion]); + + var submitReturnDrawer = useCallback(function () { + if (drawerRowIndex < 0) return; + var dates = drawerRange; + if (!dates || !dates[0] || !dates[1]) { + message.warning('请选择还车时间'); + return; + } + if (!drawerRegion || drawerRegion.length < 2) { + message.warning('请选择还车区域(省-市)'); + return; + } + var startStr = toYmd(dates[0]); + var endStr = toYmd(dates[1]); + var prov = drawerRegion[0]; + var city = drawerRegion[1]; + setVehicles(function (prev) { + var next = prev.slice(); + var r = Object.assign({}, next[drawerRowIndex]); + r.returnTaskCreated = true; + r.returnTimeStart = startStr; + r.returnTimeEnd = endStr; + r.returnProvince = prov; + r.returnCity = city; + next[drawerRowIndex] = r; + return next; + }); + message.success('还车任务已创建'); + closeReturnDrawer(); + }, [drawerRowIndex, drawerRange, drawerRegion, setVehicles, closeReturnDrawer]); + + var revokeReturn = useCallback(function (index) { + if (vehicleArrived) { + message.warning('交车单已确认车辆到达,无法撤销还车'); + return; + } + Modal.confirm({ + title: '确认撤销还车?', + content: '撤销后将清除已填写的还车时间与还车区域,需重新发起还车。', + okText: '确定', + cancelText: '取消', + onOk: function () { + setVehicles(function (prev) { + var next = prev.slice(); + var r = Object.assign({}, next[index]); + r.returnTaskCreated = false; + r.returnTimeStart = null; + r.returnTimeEnd = null; + r.returnProvince = null; + r.returnCity = null; + next[index] = r; + return next; + }); + message.success('已撤销还车'); + } + }); + }, [vehicleArrived, setVehicles]); 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' }, - card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'visible' }, cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' }, cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, cardBody: { padding: '20px 24px' }, @@ -39,14 +171,35 @@ const Component = function() { formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, label: { display: 'block', marginBottom: 6, color: '#333' }, inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', 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 }, - tableWrap: { marginTop: 16, overflowX: 'auto' }, - table: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, - th: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, - td: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' } + 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', alignItems: 'center', zIndex: 99 }, + tableWrap: { marginTop: 16, width: '100%', maxWidth: '100%', overflowX: 'auto', WebkitOverflowScrolling: 'touch' }, + /** 随列内容撑开总宽,窄屏由外层横向滚动 */ + table: { width: 'max-content', maxWidth: 'none', borderCollapse: 'collapse', fontSize: 13, tableLayout: 'auto' }, + th: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600, whiteSpace: 'nowrap' }, + td: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + /** 表体单元格内输入框:按内容宽度,避免被 100% 拉满整表 */ + inputInTable: { boxSizing: 'border-box', minWidth: 72, maxWidth: 280, width: 'auto', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 }, + thStickyRight: { + position: 'sticky', + right: 0, + zIndex: 4, + backgroundColor: '#fafafa', + boxShadow: '-6px 0 8px -4px rgba(0,0,0,0.12)', + borderLeft: '1px solid #e8e8e8' + }, + tdStickyRight: { + position: 'sticky', + right: 0, + zIndex: 3, + backgroundColor: '#fff', + boxShadow: '-6px 0 8px -4px rgba(0,0,0,0.1)', + borderLeft: '1px solid #f0f0f0' + }, + drawerFieldLabel: { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }, + protoHint: { fontSize: 12, color: '#999', marginTop: 8 } }; - var FormItemReadOnly = function(props) { + var FormItemReadOnly = function (props) { return React.createElement('div', { style: styles.formCol }, React.createElement('label', { style: styles.label }, props.label), React.createElement(Input, { value: props.value != null ? props.value : '', disabled: true, style: styles.inputDisabled }) @@ -76,38 +229,122 @@ const Component = function() { 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: Object.assign({}, styles.th, styles.thStickyRight, { width: 140, minWidth: 140 }) }, '操作') ) ); + var renderOperationCell = function (row, i) { + if (!row.deliverySuccess) { + return React.createElement('span', { style: { color: '#999' } }, '—'); + } + if (!row.returnTaskCreated) { + return React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { openReturnDrawer(i); } }, '还车'); + } + return React.createElement(Button, { + type: 'link', + size: 'small', + style: { padding: 0 }, + disabled: vehicleArrived, + onClick: function () { revokeReturn(i); } + }, '撤销还车'); + }; + var tableBody = React.createElement('tbody', null, - vehicleList.length === 0 - ? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '暂无车辆')) - : vehicleList.map(function(row, i) { - return React.createElement('tr', { key: i }, - React.createElement('td', { style: styles.td }, row.seq != null ? row.seq : '—'), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.vin, disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.monthRent + '元', disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.serviceFee + '元', disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.deposit + '元', disabled: true, style: styles.inputDisabled })), - React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.remark || '-', disabled: true, style: styles.inputDisabled })) - ); - }) + vehicles.length === 0 + ? React.createElement('tr', null, React.createElement('td', { colSpan: 13, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '暂无车辆')) + : vehicles.map(function (row, i) { + return React.createElement('tr', { key: row.vin || i }, + React.createElement('td', { style: styles.td }, row.seq != null ? row.seq : '—'), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.vin, disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.monthRent + '元', disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.serviceFee + '元', disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.deposit + '元', disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.remark || '-', disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: formatReturnTimeDisplay(row), disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: formatReturnAreaDisplay(row), disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: styles.td }, React.createElement(Input, { value: returnStatusText(row), disabled: true, style: styles.inputInTable })), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdStickyRight, { minWidth: 140 }) }, renderOperationCell(row, i)) + ); + }) ); var tableEl = React.createElement('div', { style: styles.tableWrap }, React.createElement('table', { style: styles.table }, tableHeader, tableBody) ); - var handleBack = function() { + var handleBack = function () { if (window.history && window.history.back) window.history.back(); - else antd.message.info('返回'); + else message.info('返回'); }; - var reqSpecText = '查看交车任务\n一个「数字化资产ONEOS运管平台」中的「交车任务」「查看」模块。\n\n1.面包屑:业务管理-交车任务-查看交车任务\n\n2.表单(只读):项目名称、合同编码、客户名称、交车区域、交车地点、预计交车日期、开始计费日期等,根据交车任务数据反显。\n\n3.车辆列表(只读):\n3.1.序号:与租赁合同车辆序号对应;\n3.2.品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注。\n\n4.底部为返回按钮。'; + var drawerTitle = '还车'; + if (drawerRowIndex >= 0 && vehicles[drawerRowIndex]) { + var dr = vehicles[drawerRowIndex]; + drawerTitle = '还车(序号 ' + dr.seq + (dr.plateNo ? ' · ' + dr.plateNo : '') + ')'; + } + + var returnDrawer = React.createElement(Drawer, { + title: drawerTitle, + placement: 'right', + width: 420, + open: drawerOpen, + onClose: closeReturnDrawer, + destroyOnClose: true, + footer: React.createElement('div', { style: { textAlign: 'right' } }, + React.createElement(Space, null, + React.createElement(Button, { onClick: closeReturnDrawer }, '取消'), + React.createElement(Button, { type: 'primary', onClick: submitReturnDrawer }, '提交') + ) + ) + }, + React.createElement('div', { style: { paddingBottom: 8 } }, + React.createElement('div', { style: styles.drawerFieldLabel }, '还车时间'), + React.createElement(RangePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: ['开始日期', '结束日期(单日请选同一天)'], + value: drawerRange, + onChange: function (dates) { setDrawerRange(dates && dates.length === 2 ? dates : null); } + }), + React.createElement('div', { style: { marginTop: 4, fontSize: 12, color: '#999' } }, '单日还车请将开始、结束选为同一天;跨天则为开始至结束时间段。') + ), + React.createElement('div', { style: { marginTop: 20 } }, + React.createElement('div', { style: styles.drawerFieldLabel }, '还车区域'), + React.createElement(Cascader, { + style: { width: '100%' }, + options: returnRegionOptions, + value: drawerRegion && drawerRegion.length ? drawerRegion : undefined, + onChange: function (v) { setDrawerRegion(v || []); }, + placeholder: '请选择省 / 市', + showSearch: true + }) + ) + ); + + var reqSpecText = + '查看交车任务\n' + + '一个「数字化资产ONEOS运管平台」中的「交车任务」「查看」模块。\n\n' + + '1.面包屑:业务管理-交车任务-查看交车任务\n\n' + + '2.表单(只读):项目名称、合同编码、客户名称、交车区域、交车地点、预计交车日期、开始计费日期等,根据交车任务数据反显。\n\n' + + '3.车辆列表:\n' + + '3.1.序号、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注(只读);\n' + + '3.2.还车时间:展示操作「还车」提交时填写的还车时间;单日为 YYYY-MM-DD,时间段为 YYYY-MM-DD至YYYY-MM-DD;未提交还车前显示为「-」;\n' + + '3.3.还车区域:展示提交还车时选择的省-市(格式:省-市);未提交前显示为「-」;\n' + + '3.4.还车状态:已还车(已创建还车任务)/ 未还车;\n' + + '3.5.操作列:\n' + + ' · 仅「交车成功」的车辆显示操作;未交车成功的行操作列为「—」;\n' + + ' · 未创建还车任务时显示「还车」:点击后右侧抽屉打开,含「还车时间」(日期区间选择器,单输入框双日历,单日将起止选同一天)、「还车区域」(省-市二级级联),底部「提交」「取消」;提交校验通过后创建还车任务并关闭抽屉;\n' + + ' · 已创建还车任务后隐藏「还车」,显示「撤销还车」;点击「撤销还车」须二次确认(确认弹窗说明将清除还车时间与区域);确认后撤销还车任务;交车单已点击「车辆到达」后「撤销还车」按钮置灰不可点;\n' + + '3.6.表格布局:表格总宽度随列内容撑开;容器内超出宽度时支持横向滚动;「操作」列固定在可视区域右侧(横向滚动时保持可见)。\n\n' + + '4.原型演示:列表下方提供「模拟交车单已点击车辆到达」勾选,用于联调前验证「撤销还车」置灰逻辑;正式对接后由交车单状态接口驱动,可移除该勾选。\n\n' + + '5.页面底部为「返回」按钮。'; return React.createElement('div', { style: styles.page }, React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, @@ -117,22 +354,26 @@ const Component = function() { React.createElement('span', null, '交车任务'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '查看交车任务')), - React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { reqModalOpen[1](true); } }, '查看需求说明')), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqModalOpen[1](true); } }, '查看需求说明')), React.createElement('div', { style: styles.card }, React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), React.createElement('div', { style: styles.cardBody }, formRow1, formRow2, - tableEl)), + tableEl, + React.createElement('div', { style: styles.protoHint }, + React.createElement(Checkbox, { checked: vehicleArrived, onChange: function (e) { setVehicleArrived(e.target.checked); } }), + ' 原型演示:模拟交车单已点击「车辆到达」(勾选后「撤销还车」置灰,对接接口后由交车单状态驱动)'))), React.createElement('div', { style: { height: 60 } }), React.createElement('div', { style: styles.footer }, React.createElement(Button, { onClick: handleBack }, '返回')), + returnDrawer, React.createElement(Modal, { title: '需求说明', open: reqModalOpen[0], - onCancel: function() { reqModalOpen[1](false); }, + onCancel: function () { reqModalOpen[1](false); }, width: 560, - footer: React.createElement(Button, { onClick: function() { reqModalOpen[1](false); } }, '关闭'), + footer: React.createElement(Button, { onClick: function () { reqModalOpen[1](false); } }, '关闭'), bodyStyle: { maxHeight: '70vh', overflow: 'auto' } }, React.createElement('div', { style: { padding: '8px 0', whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, reqSpecText)) ); diff --git a/web端/数据分析/业务部业绩明细.jsx b/web端/数据分析/业务部业绩明细.jsx new file mode 100644 index 0000000..2bec01e --- /dev/null +++ b/web端/数据分析/业务部业绩明细.jsx @@ -0,0 +1,812 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 数据分析 - 业务部业绩(明细 + 业绩/成本/利润汇总 Tab) +// 明细:进入页面默认统计「上一自然月」;跨月后重新进入或刷新页面,默认月份随系统日期更新为上一个月。 + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var App = antd.App; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Button = antd.Button; + var Table = antd.Table; + var Select = antd.Select; + var DatePicker = antd.DatePicker; + var Row = antd.Row; + var Col = antd.Col; + var Tabs = antd.Tabs; + var Space = antd.Space; + var message = antd.message; + + var TableSummary = Table.Summary; + var SummaryRow = TableSummary.Row; + var SummaryCell = TableSummary.Cell; + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + /** 表格正文:千分位 + 两位小数;空或 0 显示 -(利润等可为负,仍正常显示) */ + function fmtCell(n) { + if (n === null || n === undefined || n === '') return '-'; + var x = Number(n); + if (isNaN(x)) return '-'; + if (x === 0) return '-'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + + /** 总计行 */ + function fmtSum(n) { + var x = Number(n); + if (isNaN(x)) return '-'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 6 }); + } + + function escapeCsv(v) { + var s = v == null ? '' : String(v); + if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1 || s.indexOf('\r') !== -1) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; + } + + function downloadCsv(filename, lines) { + var csv = lines.map(function (row) { return row.map(escapeCsv).join(','); }).join('\n'); + var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + /** 统计月份键 YYYY-MM(上一自然月;每月初「上月」随系统日期变化,刷新/重新进入页面即更新) */ + function getLastMonthStatKey() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').format('YYYY-MM'); + } catch (e1) {} + var d = new Date(); + d.setDate(1); + d.setMonth(d.getMonth() - 1); + var y = d.getFullYear(); + var mo = d.getMonth() + 1; + return y + '-' + (mo < 10 ? '0' + mo : '' + mo); + } + + /** 月份选择器默认值(与 getLastMonthStatKey 同一自然月) */ + function getLastMonthPickerValue() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').startOf('month'); + } catch (e1) {} + try { + if (window.moment) return window.moment().subtract(1, 'month').startOf('month'); + } catch (e2) {} + return null; + } + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + /** 与筛选项同一行时,将操作列推到右侧 */ + var filterActionsColStyle = { flex: '0 0 auto', marginLeft: 'auto' }; + var tableSingleLineStyle = + '.biz-dept-perf-table .ant-table-thead th,.biz-dept-perf-table .ant-table-tbody td,.biz-dept-perf-table .ant-table-summary td{white-space:nowrap;}'; + var tabsBarStyle = '.biz-dept-perf-tabs .ant-tabs-nav{margin-bottom:0;}'; + + var monthlyMetricKeys = ['logistics', 'lease', 'sales', 'hydrogen', 'electricity', 'etc', 'other', 'total']; + + function sumMonthlyRows(rows, keys) { + var sums = {}; + keys.forEach(function (k) { + sums[k] = (rows || []).reduce(function (acc, row) { + var v = row[k]; + var n = v === null || v === undefined || v === '' ? 0 : Number(v); + return acc + (isNaN(n) ? 0 : n); + }, 0); + }); + return sums; + } + + function monthlyColumnsFor(suffix) { + if (suffix === '业绩') { + return [ + { title: '月份', dataIndex: 'month', key: 'month', width: 72, align: 'center' }, + { title: '物流业绩', dataIndex: 'logistics', key: 'logistics', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '租赁业绩', dataIndex: 'lease', key: 'lease', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '销售业绩', dataIndex: 'sales', key: 'sales', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '氢费业绩', dataIndex: 'hydrogen', key: 'hydrogen', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '电费业绩', dataIndex: 'electricity', key: 'electricity', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: 'ETC业绩', dataIndex: 'etc', key: 'etc', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '其他', dataIndex: 'other', key: 'other', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 120, align: 'center', render: function (v) { return fmtCell(v); } } + ]; + } + if (suffix === '成本') { + return [ + { title: '月份', dataIndex: 'month', key: 'month', width: 72, align: 'center' }, + { title: '物流成本', dataIndex: 'logistics', key: 'logistics', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '租赁成本', dataIndex: 'lease', key: 'lease', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '销售成本', dataIndex: 'sales', key: 'sales', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '氢费成本', dataIndex: 'hydrogen', key: 'hydrogen', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '电费成本', dataIndex: 'electricity', key: 'electricity', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: 'ETC成本', dataIndex: 'etc', key: 'etc', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '其他', dataIndex: 'other', key: 'other', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 120, align: 'center', render: function (v) { return fmtCell(v); } } + ]; + } + return [ + { title: '月份', dataIndex: 'month', key: 'month', width: 72, align: 'center' }, + { title: '物流利润', dataIndex: 'logistics', key: 'logistics', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '租赁利润', dataIndex: 'lease', key: 'lease', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '销售利润', dataIndex: 'sales', key: 'sales', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '氢费利润', dataIndex: 'hydrogen', key: 'hydrogen', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '电费利润', dataIndex: 'electricity', key: 'electricity', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: 'ETC利润', dataIndex: 'etc', key: 'etc', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '其他', dataIndex: 'other', key: 'other', width: 110, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 120, align: 'center', render: function (v) { return fmtCell(v); } } + ]; + } + + function rowTotal(r) { + var keys = ['logistics', 'lease', 'sales', 'hydrogen', 'electricity', 'etc', 'other']; + var s = 0; + var any = false; + keys.forEach(function (k) { + var v = r[k]; + if (v === null || v === undefined || v === '') return; + var n = Number(v); + if (isNaN(n)) return; + any = true; + s += n; + }); + return any ? s : null; + } + + function finalizeMonthlyRow(r) { + var t = r.total != null && r.total !== '' ? r.total : rowTotal(r); + return Object.assign({}, r, { total: t }); + } + + /** 原型:2026 年按月业绩汇总 */ + var monthlyPerf2026 = useMemo(function () { + var raw = [ + { month: 1, logistics: 1005557.94, lease: 4004.73, sales: 1495355.18, hydrogen: null, electricity: 88200.5, etc: 12000, other: 5000, total: null }, + { month: 2, logistics: 250000, lease: 180000, sales: null, hydrogen: 3200, electricity: null, etc: null, other: null, total: null }, + { month: 3, logistics: 180000, lease: 95000, sales: 220000, hydrogen: null, electricity: 45000, etc: 8000, other: 1200, total: null }, + { month: 4, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 5, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 6, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 7, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 8, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 9, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 10, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 11, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 12, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null } + ]; + return raw.map(function (x) { + return finalizeMonthlyRow(Object.assign({ key: 'perf-' + x.month }, x)); + }); + }, []); + + /** 原型:2026 年按月成本汇总 */ + var monthlyCost2026 = useMemo(function () { + var raw = [ + { month: 1, logistics: 1167787.05, lease: 320000, sales: 980000, hydrogen: 15000, electricity: 72000, etc: 9800, other: 2400, total: null }, + { month: 2, logistics: 210000, lease: 165000, sales: null, hydrogen: 2800, electricity: null, etc: null, other: null, total: null }, + { month: 3, logistics: 155000, lease: 88000, sales: 195000, hydrogen: null, electricity: 38000, etc: 6500, other: 800, total: null }, + { month: 4, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 5, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 6, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 7, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 8, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 9, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 10, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 11, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 12, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null } + ]; + return raw.map(function (x) { + return finalizeMonthlyRow(Object.assign({ key: 'cost-' + x.month }, x)); + }); + }, []); + + /** 原型:2026 年按月利润汇总(可为负) */ + var monthlyProfit2026 = useMemo(function () { + var raw = [ + { month: 1, logistics: -162229.11, lease: 45000, sales: null, hydrogen: -1200.5, electricity: 6200, etc: null, other: 800, total: null }, + { month: 2, logistics: null, lease: 15000, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 3, logistics: null, lease: 7000, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 4, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 5, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 6, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 7, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 8, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 9, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 10, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 11, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null }, + { month: 12, logistics: null, lease: null, sales: null, hydrogen: null, electricity: null, etc: null, other: null, total: null } + ]; + return raw.map(function (x) { + return finalizeMonthlyRow(Object.assign({ key: 'profit-' + x.month }, x)); + }); + }, []); + + var deptOptions = useMemo(function () { + return [ + { value: '华东业务部', label: '华东业务部' }, + { value: '华南业务部', label: '华南业务部' }, + { value: '华北业务部', label: '华北业务部' }, + { value: '西南业务部', label: '西南业务部' } + ]; + }, []); + + var salespersonOptions = useMemo(function () { + return [ + { value: '尚建华', label: '尚建华' }, + { value: '刘念念', label: '刘念念' }, + { value: '谯云', label: '谯云' }, + { value: '董剑煜', label: '董剑煜' } + ]; + }, []); + + var lastMonthStatKey = useMemo(function () { + return getLastMonthStatKey(); + }, []); + + var allRows = useMemo(function () { + var sm = lastMonthStatKey; + return [ + { + key: '1', + statMonth: sm, + salesperson: '尚建华', + dept: '华东业务部', + logistics: 208868.38, + lease: 36000, + sales: null, + hydrogen: null, + electricity: null, + etc: null, + other: 55950.23, + total: 300818.61 + }, + { + key: '2', + statMonth: sm, + salesperson: '刘念念', + dept: '华南业务部', + logistics: 397181.78, + lease: 223800, + sales: 120000, + hydrogen: 4500.12, + electricity: 25000, + etc: null, + other: 224.14, + total: 770706.04 + }, + { + key: '3', + statMonth: sm, + salesperson: '谯云', + dept: '华东业务部', + logistics: 391153.33, + lease: 15000, + sales: null, + hydrogen: null, + electricity: null, + etc: 8665.43, + other: null, + total: 414818.76 + }, + { + key: '4', + statMonth: sm, + salesperson: '董剑煜', + dept: '西南业务部', + logistics: 8354.46, + lease: null, + sales: null, + hydrogen: null, + electricity: null, + etc: null, + other: 657.3, + total: 9011.76 + } + ]; + }, [lastMonthStatKey]); + + var mainTabState = useState('detail'); + var mainTab = mainTabState[0]; + var setMainTab = mainTabState[1]; + + var draftState = useState(function () { + return { + month: getLastMonthPickerValue(), + dept: undefined, + salesperson: undefined + }; + }); + var draft = draftState[0]; + var setDraft = draftState[1]; + + var appliedState = useState(function () { + return { + month: getLastMonthPickerValue(), + dept: undefined, + salesperson: undefined + }; + }); + var applied = appliedState[0]; + var setApplied = appliedState[1]; + + function initialYear2026() { + try { + if (window.dayjs) return window.dayjs('2026-01-01'); + } catch (e1) {} + return null; + } + + var summaryYearDraftState = useState(initialYear2026); + var summaryYearDraft = summaryYearDraftState[0]; + var setSummaryYearDraft = summaryYearDraftState[1]; + + var summaryYearAppliedState = useState(initialYear2026); + var summaryYearApplied = summaryYearAppliedState[0]; + var setSummaryYearApplied = summaryYearAppliedState[1]; + + var filteredRows = useMemo(function () { + return (allRows || []).filter(function (r) { + if (applied.month && applied.month.format) { + var mk = applied.month.format('YYYY-MM'); + if (r.statMonth !== mk) return false; + } + if (applied.dept && r.dept !== applied.dept) return false; + if (applied.salesperson && r.salesperson !== applied.salesperson) return false; + return true; + }); + }, [allRows, applied.month, applied.dept, applied.salesperson]); + + var metricKeys = useMemo(function () { + return ['logistics', 'lease', 'sales', 'hydrogen', 'electricity', 'etc', 'other', 'total']; + }, []); + + var columnSums = useMemo(function () { + var sums = {}; + metricKeys.forEach(function (k) { + sums[k] = filteredRows.reduce(function (acc, row) { + var v = row[k]; + var n = v === null || v === undefined || v === '' ? 0 : Number(v); + return acc + (isNaN(n) ? 0 : n); + }, 0); + }); + return sums; + }, [filteredRows, metricKeys]); + + var bizReportTableTitleStyle = { + textAlign: 'center', + marginBottom: 16, + fontSize: 16, + fontWeight: 600, + color: 'rgba(0,0,0,0.88)' + }; + + /** 与「查询」后已选月份一致:x年x月业绩明细表 */ + var detailTableTitle = useMemo(function () { + if (applied.month && applied.month.format) { + var y = applied.month.format('YYYY'); + var m = applied.month.format('M'); + return y + '年' + m + '月业绩明细表'; + } + return '业绩明细表'; + }, [applied.month]); + + /** 与汇总 Tab「查询」后已选年份一致 */ + var perfSumTableTitle = useMemo(function () { + if (summaryYearApplied && summaryYearApplied.format) { + return summaryYearApplied.format('YYYY') + '年业绩汇总'; + } + return '业绩汇总'; + }, [summaryYearApplied]); + + var costSumTableTitle = useMemo(function () { + if (summaryYearApplied && summaryYearApplied.format) { + return summaryYearApplied.format('YYYY') + '年成本汇总'; + } + return '成本汇总'; + }, [summaryYearApplied]); + + var profitSumTableTitle = useMemo(function () { + if (summaryYearApplied && summaryYearApplied.format) { + return summaryYearApplied.format('YYYY') + '年利润汇总'; + } + return '利润汇总'; + }, [summaryYearApplied]); + + var monthlyDataSource = useCallback(function (rows) { + var y = summaryYearApplied && summaryYearApplied.format ? summaryYearApplied.format('YYYY') : null; + if (!y) return []; + if (y !== '2026') return []; + return rows; + }, [summaryYearApplied]); + + var perfMonthlyRows = useMemo(function () { return monthlyDataSource(monthlyPerf2026); }, [monthlyDataSource, monthlyPerf2026]); + var costMonthlyRows = useMemo(function () { return monthlyDataSource(monthlyCost2026); }, [monthlyDataSource, monthlyCost2026]); + var profitMonthlyRows = useMemo(function () { return monthlyDataSource(monthlyProfit2026); }, [monthlyDataSource, monthlyProfit2026]); + + var perfMonthlySums = useMemo(function () { return sumMonthlyRows(perfMonthlyRows, monthlyMetricKeys); }, [perfMonthlyRows]); + var costMonthlySums = useMemo(function () { return sumMonthlyRows(costMonthlyRows, monthlyMetricKeys); }, [costMonthlyRows]); + var profitMonthlySums = useMemo(function () { return sumMonthlyRows(profitMonthlyRows, monthlyMetricKeys); }, [profitMonthlyRows]); + + var handleQuery = useCallback(function () { + setApplied(Object.assign({}, draft)); + }, [draft]); + + var handleReset = useCallback(function () { + var def = { month: getLastMonthPickerValue(), dept: undefined, salesperson: undefined }; + setDraft(def); + setApplied(def); + }, []); + + var handleSummaryQuery = useCallback(function () { + setSummaryYearApplied(summaryYearDraft); + }, [summaryYearDraft]); + + var handleSummaryReset = useCallback(function () { + var y0 = initialYear2026(); + setSummaryYearDraft(y0); + setSummaryYearApplied(y0); + }, []); + + var handleExportDetail = useCallback(function () { + var rows = filteredRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出'); + return; + } + var headers = [ + '业务员', + '物流业绩', + '租赁业绩', + '销售业绩', + '氢费业绩', + '电费业绩', + 'ETC业绩', + '其他', + '合计' + ]; + var body = [headers].concat( + rows.map(function (r) { + return [ + r.salesperson, + fmtCell(r.logistics), + fmtCell(r.lease), + fmtCell(r.sales), + fmtCell(r.hydrogen), + fmtCell(r.electricity), + fmtCell(r.etc), + fmtCell(r.other), + fmtCell(r.total) + ]; + }) + ); + body.push(['总计'].concat(metricKeys.map(function (k) { return fmtSum(columnSums[k] || 0); }))); + downloadCsv('业绩明细_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 条记录'); + }, [filteredRows, metricKeys, columnSums]); + + var exportMonthly = useCallback(function (rows, sums, headerTitles, filePrefix) { + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出,请先选择年份并查询'); + return; + } + var body = [headerTitles].concat( + rows.map(function (r) { + return [ + String(r.month), + fmtCell(r.logistics), + fmtCell(r.lease), + fmtCell(r.sales), + fmtCell(r.hydrogen), + fmtCell(r.electricity), + fmtCell(r.etc), + fmtCell(r.other), + fmtCell(r.total) + ]; + }) + ); + body.push(['总计'].concat(monthlyMetricKeys.map(function (k) { return fmtSum(sums[k] || 0); }))); + downloadCsv(filePrefix + '_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 个月度数据'); + }, []); + + var handleExportPerfMonthly = useCallback(function () { + exportMonthly( + perfMonthlyRows, + perfMonthlySums, + ['月份', '物流业绩', '租赁业绩', '销售业绩', '氢费业绩', '电费业绩', 'ETC业绩', '其他', '合计'], + '业绩汇总' + ); + }, [exportMonthly, perfMonthlyRows, perfMonthlySums]); + + var handleExportCostMonthly = useCallback(function () { + exportMonthly( + costMonthlyRows, + costMonthlySums, + ['月份', '物流成本', '租赁成本', '销售成本', '氢费成本', '电费成本', 'ETC成本', '其他', '合计'], + '成本汇总' + ); + }, [exportMonthly, costMonthlyRows, costMonthlySums]); + + var handleExportProfitMonthly = useCallback(function () { + exportMonthly( + profitMonthlyRows, + profitMonthlySums, + ['月份', '物流利润', '租赁利润', '销售利润', '氢费利润', '电费利润', 'ETC利润', '其他', '合计'], + '利润汇总' + ); + }, [exportMonthly, profitMonthlyRows, profitMonthlySums]); + + var columns = useMemo(function () { + return [ + { title: '业务员', dataIndex: 'salesperson', key: 'salesperson', width: 110, align: 'center' }, + { title: '物流业绩', dataIndex: 'logistics', key: 'logistics', width: 130, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '租赁业绩', dataIndex: 'lease', key: 'lease', width: 130, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '销售业绩', dataIndex: 'sales', key: 'sales', width: 130, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '氢费业绩', dataIndex: 'hydrogen', key: 'hydrogen', width: 130, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '电费业绩', dataIndex: 'electricity', key: 'electricity', width: 140, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: 'ETC业绩', dataIndex: 'etc', key: 'etc', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '其他', dataIndex: 'other', key: 'other', width: 120, align: 'center', render: function (v) { return fmtCell(v); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 130, align: 'center', render: function (v) { return fmtCell(v); } } + ]; + }, []); + + var tableSummary = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center' }, '总计'), + metricKeys.map(function (k, idx) { + return React.createElement( + SummaryCell, + { key: k, index: idx + 1, align: 'center' }, + fmtSum(columnSums[k] || 0) + ); + }) + ) + ); + }, [metricKeys, columnSums]); + + function renderMonthlySummary(sums) { + return function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center' }, '总计'), + monthlyMetricKeys.map(function (k, idx) { + return React.createElement( + SummaryCell, + { key: k, index: idx + 1, align: 'center' }, + fmtSum(sums[k] || 0) + ); + }) + ) + ); + }; + } + + var columnsPerfMonthly = useMemo(function () { return monthlyColumnsFor('业绩'); }, []); + var columnsCostMonthly = useMemo(function () { return monthlyColumnsFor('成本'); }, []); + var columnsProfitMonthly = useMemo(function () { return monthlyColumnsFor('利润'); }, []); + + var renderYearFilterCard = useCallback(function () { + return React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择年份'), + React.createElement(DatePicker, { + picker: 'year', + style: filterControlStyle, + placeholder: '请选择统计年份', + format: 'YYYY', + value: summaryYearDraft, + onChange: function (v) { setSummaryYearDraft(v); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6, style: filterActionsColStyle }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '\u00a0'), + React.createElement(Space, { size: 8, wrap: true }, + React.createElement(Button, { onClick: handleSummaryReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleSummaryQuery }, '查询') + ) + ) + ) + ) + ); + }, [summaryYearDraft, handleSummaryReset, handleSummaryQuery]); + + var tabItems = [ + { + key: 'detail', + label: '业绩明细', + children: React.createElement(React.Fragment, null, + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择月份'), + React.createElement(DatePicker, { + picker: 'month', + style: filterControlStyle, + placeholder: '选择器选择月份', + format: 'YYYY-MM', + value: draft.month, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { month: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择业务部门'), + React.createElement(Select, { + placeholder: '选择器选择业务部门', + style: filterControlStyle, + allowClear: true, + options: deptOptions, + value: draft.dept, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { dept: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择业务员'), + React.createElement(Select, { + placeholder: '选择器选择业务员', + style: filterControlStyle, + allowClear: true, + options: salespersonOptions, + value: draft.salesperson, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { salesperson: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6, style: filterActionsColStyle }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '\u00a0'), + React.createElement(Space, { size: 8, wrap: true }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询') + ) + ) + ) + ) + ), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportDetail }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, detailTableTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'biz-dept-perf-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columns, + dataSource: filteredRows, + pagination: false, + size: 'small', + summary: tableSummary, + scroll: { x: 1200 } + }) + ) + ) + ) + }, + { + key: 'perfSum', + label: '业绩汇总', + children: React.createElement(React.Fragment, null, + renderYearFilterCard(), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportPerfMonthly }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, perfSumTableTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'biz-dept-perf-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsPerfMonthly, + dataSource: perfMonthlyRows, + pagination: false, + size: 'small', + summary: renderMonthlySummary(perfMonthlySums), + scroll: { x: 1180 } + }) + ) + ) + ) + }, + { + key: 'costSum', + label: '成本汇总', + children: React.createElement(React.Fragment, null, + renderYearFilterCard(), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportCostMonthly }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, costSumTableTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'biz-dept-perf-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsCostMonthly, + dataSource: costMonthlyRows, + pagination: false, + size: 'small', + summary: renderMonthlySummary(costMonthlySums), + scroll: { x: 1180 } + }) + ) + ) + ) + }, + { + key: 'profitSum', + label: '利润汇总', + children: React.createElement(React.Fragment, null, + renderYearFilterCard(), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportProfitMonthly }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, profitSumTableTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'biz-dept-perf-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsProfitMonthly, + dataSource: profitMonthlyRows, + pagination: false, + size: 'small', + summary: renderMonthlySummary(profitMonthlySums), + scroll: { x: 1180 } + }) + ) + ) + ) + } + ]; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement(Breadcrumb, { + style: { marginBottom: 16 }, + items: [{ title: '数据分析' }, { title: '业务部业绩' }] + }), + React.createElement(Card, null, + React.createElement('style', null, tabsBarStyle), + React.createElement(Tabs, { + className: 'biz-dept-perf-tabs', + activeKey: mainTab, + onChange: function (k) { setMainTab(k); }, + items: tabItems + }) + ) + ) + ); +}; diff --git a/web端/数据分析/物流业务月度统计.jsx b/web端/数据分析/物流业务月度统计.jsx new file mode 100644 index 0000000..04edb96 --- /dev/null +++ b/web端/数据分析/物流业务月度统计.jsx @@ -0,0 +1,843 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 数据分析 - 物流业务月度统计:Tab「物流业务人员明细」+「物流盈亏月度汇总」(1–12 月汇总,交互同业务部业绩汇总) +// 人员明细:进入页面默认统计「上一自然月」 + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var App = antd.App; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Button = antd.Button; + var Table = antd.Table; + var Select = antd.Select; + var DatePicker = antd.DatePicker; + var Row = antd.Row; + var Col = antd.Col; + var Tabs = antd.Tabs; + var message = antd.message; + + var TableSummary = Table.Summary; + var SummaryRow = TableSummary.Row; + var SummaryCell = TableSummary.Cell; + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + function fmtCell(n) { + if (n === null || n === undefined || n === '') return '—'; + var x = Number(n); + if (isNaN(x)) return '—'; + if (x === 0) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + + function fmtIntCell(n) { + if (n === null || n === undefined || n === '') return '—'; + var x = Number(n); + if (isNaN(x)) return '—'; + if (x === 0) return '—'; + return String(Math.round(x)); + } + + /** 盈亏:允许负数、零显示 0.00 */ + function fmtProfit(n) { + if (n === null || n === undefined || n === '') return '—'; + var x = Number(n); + if (isNaN(x)) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + + function fmtSum(n) { + var x = Number(n); + if (isNaN(x)) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 6 }); + } + + function escapeCsv(v) { + var s = v == null ? '' : String(v); + if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1 || s.indexOf('\r') !== -1) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; + } + + function downloadCsv(filename, lines) { + var csv = lines.map(function (row) { return row.map(escapeCsv).join(','); }).join('\n'); + var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + function getLastMonthStatKey() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').format('YYYY-MM'); + } catch (e1) {} + var d = new Date(); + d.setDate(1); + d.setMonth(d.getMonth() - 1); + var y = d.getFullYear(); + var mo = d.getMonth() + 1; + return y + '-' + (mo < 10 ? '0' + mo : '' + mo); + } + + function getLastMonthPickerValue() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').startOf('month'); + } catch (e1) {} + try { + if (window.moment) return window.moment().subtract(1, 'month').startOf('month'); + } catch (e2) {} + return null; + } + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + var tableSingleLineStyle = + '.logistics-monthly-stat-table .ant-table-thead th,.logistics-monthly-stat-table .ant-table-tbody td,.logistics-monthly-stat-table .ant-table-summary td{white-space:nowrap;}'; + var tabsBarStyle = '.logistics-monthly-stat-tabs .ant-tabs-nav{margin-bottom:0;}'; + + var bizReportTableTitleStyle = { + textAlign: 'center', + marginBottom: 16, + fontSize: 16, + fontWeight: 600, + color: 'rgba(0,0,0,0.88)' + }; + + var profitNegStyle = { background: '#fff1f0', padding: '2px 8px', borderRadius: 4, display: 'inline-block' }; + + var numericSumKeys = [ + 'income', + 'hydrogenFee', + 'laborCost', + 'etcFee', + 'electricityFee', + 'salary', + 'vehicleCount', + 'tireCost', + 'deprecInsuranceFee', + 'socialSecurityFee', + 'trailerFee', + 'parkingFee', + 'vehicleTotalCost', + 'totalCost', + 'profitLoss' + ]; + + function sumRows(rows, keys) { + var sums = {}; + keys.forEach(function (k) { + sums[k] = (rows || []).reduce(function (acc, row) { + var v = row[k]; + var n = v === null || v === undefined || v === '' ? 0 : Number(v); + return acc + (isNaN(n) ? 0 : n); + }, 0); + }); + return sums; + } + + var lastMonthStatKey = useMemo(function () { + return getLastMonthStatKey(); + }, []); + + var rawRowsTemplate = useMemo(function () { + var sm = lastMonthStatKey; + return [ + { + key: 'lm-1', + statMonth: sm, + salesperson: '谈云', + businessName: '上海虹钦物流有限公司', + vehicleModel: '帕立安4.5T', + income: 1005557.94, + hydrogenFee: 185420.5, + laborCost: 128800, + etcFee: 45230.12, + electricityFee: 62300.8, + salary: 96000, + vehicleCount: 12, + tireCost: 8900, + deprecInsuranceFee: 156000, + socialSecurityFee: 28400, + trailerFee: 12000, + parkingFee: 6800, + vehicleTotalCost: 420000, + totalCost: 918851.42, + profitLoss: 86706.52, + remark: '' + }, + { + key: 'lm-2', + statMonth: sm, + salesperson: '刘念念', + businessName: '杭州绿道城配科技有限公司', + vehicleModel: '古道车队', + income: 680200, + hydrogenFee: 142000, + laborCost: 98000, + etcFee: 32100, + electricityFee: 41000, + salary: 72000, + vehicleCount: 8, + tireCost: 5600, + deprecInsuranceFee: 112000, + socialSecurityFee: 21000, + trailerFee: 8000, + parkingFee: 4200, + vehicleTotalCost: 298000, + totalCost: 832900, + profitLoss: -152700, + remark: '' + }, + { + key: 'lm-3', + statMonth: sm, + salesperson: '谈云', + businessName: '宁波港联氢运物流有限公司', + vehicleModel: '飞越49T', + income: 892300.5, + hydrogenFee: 210000, + laborCost: 145000, + etcFee: 38900, + electricityFee: 55000, + salary: 88000, + vehicleCount: 10, + tireCost: 7200, + deprecInsuranceFee: 198000, + socialSecurityFee: 25600, + trailerFee: 15000, + parkingFee: 5500, + vehicleTotalCost: 512000, + totalCost: 1284200.5, + profitLoss: -391900, + remark: '淡季线路调整' + }, + { + key: 'lm-4', + statMonth: sm, + salesperson: '谯云', + businessName: '嘉兴南湖氢能示范运营', + vehicleModel: '帕立安4.5T', + income: 325000, + hydrogenFee: 52000, + laborCost: 38000, + etcFee: 12000, + electricityFee: 18500, + salary: 28000, + vehicleCount: 4, + tireCost: 2100, + deprecInsuranceFee: 48000, + socialSecurityFee: 9200, + trailerFee: null, + parkingFee: 1800, + vehicleTotalCost: 125000, + totalCost: 334600, + profitLoss: -9600, + remark: '' + }, + { + key: 'lm-5', + statMonth: sm, + salesperson: '董剑煜', + businessName: '温州瓯江冷链专线', + vehicleModel: '宇通49T', + income: 456780, + hydrogenFee: 88000, + laborCost: 56000, + etcFee: 22000, + electricityFee: 31000, + salary: 42000, + vehicleCount: 5, + tireCost: 4500, + deprecInsuranceFee: 72000, + socialSecurityFee: 11800, + trailerFee: 6000, + parkingFee: 2400, + vehicleTotalCost: 198000, + totalCost: 583700, + profitLoss: -126920, + remark: '' + } + ]; + }, [lastMonthStatKey]); + + var salespersonOptions = useMemo(function () { + var set = {}; + (rawRowsTemplate || []).forEach(function (r) { + if (r.salesperson) set[r.salesperson] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [rawRowsTemplate]); + + var businessNameOptions = useMemo(function () { + var set = {}; + (rawRowsTemplate || []).forEach(function (r) { + if (r.businessName) set[r.businessName] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [rawRowsTemplate]); + + /** 物流盈亏月度汇总:按自然月 1–12 列示(原型仅 2026 年有数据) */ + var monthlyPlMetricKeys = numericSumKeys; + + var monthlyPlSummary2026 = useMemo(function () { + function mk(m, p) { + var r = Object.assign({ key: 'plm-' + m, month: m }, p); + if (r.profitLoss == null && r.income != null && r.totalCost != null) { + r.profitLoss = Number(r.income) - Number(r.totalCost); + } + return r; + } + function empty(m) { + return mk(m, {}); + } + return [ + mk(1, { + income: 3350838.44, + hydrogenFee: 537400, + laborCost: 418800, + etcFee: 148230.12, + electricityFee: 228800.8, + salary: 386000, + vehicleCount: 39, + tireCost: 28100, + deprecInsuranceFee: 586000, + socialSecurityFee: 96000, + trailerFee: 53000, + parkingFee: 19600, + vehicleTotalCost: 1553000, + totalCost: 4123731.44, + profitLoss: -772893 + }), + mk(2, { + income: 2680000, + hydrogenFee: 412000, + laborCost: 298000, + etcFee: 98500, + electricityFee: 156000, + salary: 265000, + vehicleCount: 26, + tireCost: 19200, + deprecInsuranceFee: 445000, + socialSecurityFee: 68400, + trailerFee: 41000, + parkingFee: 15800, + vehicleTotalCost: 1180000, + totalCost: 3550900, + profitLoss: -870900 + }), + mk(3, { + income: 3012000, + hydrogenFee: 468000, + laborCost: 352000, + etcFee: 108000, + electricityFee: 172500, + salary: 298000, + vehicleCount: 31, + tireCost: 22100, + deprecInsuranceFee: 502000, + socialSecurityFee: 75200, + trailerFee: 38500, + parkingFee: 16900, + vehicleTotalCost: 1285000, + totalCost: 3488200, + profitLoss: -476200 + }), + empty(4), + empty(5), + empty(6), + empty(7), + empty(8), + empty(9), + empty(10), + empty(11), + empty(12) + ]; + }, []); + + function initialYear2026() { + try { + if (window.dayjs) return window.dayjs('2026-01-01'); + } catch (e) {} + return null; + } + + var mainTabState = useState('personDetail'); + var mainTab = mainTabState[0]; + var setMainTab = mainTabState[1]; + + var summaryYearDraftState = useState(initialYear2026); + var summaryYearDraft = summaryYearDraftState[0]; + var setSummaryYearDraft = summaryYearDraftState[1]; + + var summaryYearAppliedState = useState(initialYear2026); + var summaryYearApplied = summaryYearAppliedState[0]; + var setSummaryYearApplied = summaryYearAppliedState[1]; + + var draftState = useState(function () { + return { + month: getLastMonthPickerValue(), + salesperson: undefined, + businessName: undefined + }; + }); + var draft = draftState[0]; + var setDraft = draftState[1]; + + var appliedState = useState(function () { + return { + month: getLastMonthPickerValue(), + salesperson: undefined, + businessName: undefined + }; + }); + var applied = appliedState[0]; + var setApplied = appliedState[1]; + + var filteredRows = useMemo(function () { + return (rawRowsTemplate || []).filter(function (r) { + if (applied.month && applied.month.format) { + var mk = applied.month.format('YYYY-MM'); + if (r.statMonth !== mk) return false; + } + if (applied.salesperson && r.salesperson !== applied.salesperson) return false; + if (applied.businessName && r.businessName !== applied.businessName) return false; + return true; + }); + }, [rawRowsTemplate, applied.month, applied.salesperson, applied.businessName]); + + var columnSums = useMemo(function () { + return sumRows(filteredRows, numericSumKeys); + }, [filteredRows]); + + var reportTitle = useMemo(function () { + if (applied.month && applied.month.format) { + var y = applied.month.format('YYYY'); + var m = applied.month.format('M'); + return y + '年' + m + '月浙江羚牛氢能自运营物流业务盈亏月度汇总表'; + } + return '浙江羚牛氢能自运营物流业务盈亏月度汇总表'; + }, [applied.month]); + + var plSumTableTitle = useMemo(function () { + if (summaryYearApplied && summaryYearApplied.format) { + return summaryYearApplied.format('YYYY') + '年物流盈亏月度汇总'; + } + return '物流盈亏月度汇总'; + }, [summaryYearApplied]); + + var monthlyPlDataSource = useCallback(function (rows) { + var y = summaryYearApplied && summaryYearApplied.format ? summaryYearApplied.format('YYYY') : null; + if (!y) return []; + if (y !== '2026') return []; + return rows; + }, [summaryYearApplied]); + + var plMonthlyRows = useMemo(function () { + return monthlyPlDataSource(monthlyPlSummary2026); + }, [monthlyPlDataSource, monthlyPlSummary2026]); + + var plMonthlySums = useMemo(function () { + return sumRows(plMonthlyRows, monthlyPlMetricKeys); + }, [plMonthlyRows]); + + var handleQuery = useCallback(function () { + setApplied(Object.assign({}, draft)); + }, [draft]); + + var handleReset = useCallback(function () { + var def = { month: getLastMonthPickerValue(), salesperson: undefined, businessName: undefined }; + setDraft(def); + setApplied(def); + }, []); + + var handleSummaryQuery = useCallback(function () { + setSummaryYearApplied(summaryYearDraft); + }, [summaryYearDraft]); + + var handleSummaryReset = useCallback(function () { + var y0 = initialYear2026(); + setSummaryYearDraft(y0); + setSummaryYearApplied(y0); + }, []); + + var handleExport = useCallback(function () { + var rows = filteredRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出'); + return; + } + var headers = [ + '业务员', + '业务名称', + '系统车型', + '收入', + '氢费', + '人工费用', + 'ETC', + '电费', + '薪资', + '投入车数', + '轮胎', + '(折旧、年审、保险)费用', + '社保服务费', + '挂车费用', + '停车费', + '车总费用', + '总成本', + '盈亏', + '备注' + ]; + var rowLine = function (r) { + return [ + r.salesperson, + r.businessName, + r.vehicleModel, + fmtCell(r.income), + fmtCell(r.hydrogenFee), + fmtCell(r.laborCost), + fmtCell(r.etcFee), + fmtCell(r.electricityFee), + fmtCell(r.salary), + fmtIntCell(r.vehicleCount), + fmtCell(r.tireCost), + fmtCell(r.deprecInsuranceFee), + fmtCell(r.socialSecurityFee), + fmtCell(r.trailerFee), + fmtCell(r.parkingFee), + fmtCell(r.vehicleTotalCost), + fmtCell(r.totalCost), + fmtProfit(r.profitLoss), + r.remark || '' + ]; + }; + var body = [headers].concat(rows.map(rowLine)); + var sumLine = ['总计', '', ''].concat( + numericSumKeys.map(function (k, idx) { + if (k === 'vehicleCount') return fmtIntCell(columnSums[k]); + if (k === 'profitLoss') return fmtSum(columnSums[k]); + return fmtSum(columnSums[k]); + }) + ); + body.push(sumLine); + downloadCsv('物流业务月度统计_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 条记录'); + }, [filteredRows, columnSums]); + + var plMonthlyExportHeaders = [ + '月份', + '收入', + '氢费', + '人工费用', + 'ETC', + '电费', + '薪资', + '投入车数', + '轮胎', + '(折旧、年审、保险)费用', + '社保服务费', + '挂车费用', + '停车费', + '车总费用', + '总成本', + '盈亏月度合计' + ]; + + var handleExportPlMonthly = useCallback(function () { + var rows = plMonthlyRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出,请先选择年份并查询'); + return; + } + var rowLine = function (r) { + return [ + String(r.month), + fmtCell(r.income), + fmtCell(r.hydrogenFee), + fmtCell(r.laborCost), + fmtCell(r.etcFee), + fmtCell(r.electricityFee), + fmtCell(r.salary), + fmtIntCell(r.vehicleCount), + fmtCell(r.tireCost), + fmtCell(r.deprecInsuranceFee), + fmtCell(r.socialSecurityFee), + fmtCell(r.trailerFee), + fmtCell(r.parkingFee), + fmtCell(r.vehicleTotalCost), + fmtCell(r.totalCost), + fmtProfit(r.profitLoss) + ]; + }; + var body = [plMonthlyExportHeaders].concat(rows.map(rowLine)); + body.push(['总计'].concat( + monthlyPlMetricKeys.map(function (k) { + if (k === 'vehicleCount') return fmtIntCell(plMonthlySums[k]); + return fmtSum(plMonthlySums[k] || 0); + }) + )); + downloadCsv('物流盈亏月度汇总_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 个月度数据'); + }, [plMonthlyRows, plMonthlySums]); + + var columns = useMemo(function () { + return [ + { title: '业务员', dataIndex: 'salesperson', key: 'salesperson', width: 88, align: 'center', fixed: 'left' }, + { title: '业务名称', dataIndex: 'businessName', key: 'businessName', width: 200, ellipsis: true, fixed: 'left' }, + { title: '系统车型', dataIndex: 'vehicleModel', key: 'vehicleModel', width: 110, align: 'center' }, + { title: '收入', dataIndex: 'income', key: 'income', width: 120, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '氢费', dataIndex: 'hydrogenFee', key: 'hydrogenFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '人工费用', dataIndex: 'laborCost', key: 'laborCost', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: 'ETC', dataIndex: 'etcFee', key: 'etcFee', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '电费', dataIndex: 'electricityFee', key: 'electricityFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '薪资', dataIndex: 'salary', key: 'salary', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '投入车数', dataIndex: 'vehicleCount', key: 'vehicleCount', width: 92, align: 'right', render: function (v) { return fmtIntCell(v); } }, + { title: '轮胎', dataIndex: 'tireCost', key: 'tireCost', width: 88, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '(折旧、年审、保险)费用', dataIndex: 'deprecInsuranceFee', key: 'deprecInsuranceFee', width: 160, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '社保服务费', dataIndex: 'socialSecurityFee', key: 'socialSecurityFee', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '挂车费用', dataIndex: 'trailerFee', key: 'trailerFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '停车费', dataIndex: 'parkingFee', key: 'parkingFee', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '车总费用', dataIndex: 'vehicleTotalCost', key: 'vehicleTotalCost', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '总成本', dataIndex: 'totalCost', key: 'totalCost', width: 120, align: 'right', render: function (v) { return fmtCell(v); } }, + { + title: '盈亏', + dataIndex: 'profitLoss', + key: 'profitLoss', + width: 120, + align: 'right', + render: function (v) { + var s = fmtProfit(v); + var neg = v !== null && v !== undefined && v !== '' && !isNaN(Number(v)) && Number(v) < 0; + return neg ? React.createElement('span', { style: profitNegStyle }, s) : s; + } + }, + { title: '备注', dataIndex: 'remark', key: 'remark', width: 140, ellipsis: true } + ]; + }, []); + + var tableSummary = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center', colSpan: 3 }, '总计'), + numericSumKeys.map(function (k, idx) { + var text; + if (k === 'vehicleCount') text = fmtIntCell(columnSums[k]); + else text = fmtSum(columnSums[k]); + var isNegProfit = k === 'profitLoss' && Number(columnSums[k]) < 0; + var child = isNegProfit ? React.createElement('span', { style: profitNegStyle }, text) : text; + return React.createElement(SummaryCell, { key: k, index: idx + 3, align: 'right' }, child); + }), + React.createElement(SummaryCell, { index: numericSumKeys.length + 3, align: 'left' }, '') + ) + ); + }, [columnSums]); + + var columnsPlMonthly = useMemo(function () { + return [ + { title: '月份', dataIndex: 'month', key: 'month', width: 64, align: 'center' }, + { title: '收入', dataIndex: 'income', key: 'income', width: 118, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '氢费', dataIndex: 'hydrogenFee', key: 'hydrogenFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '人工费用', dataIndex: 'laborCost', key: 'laborCost', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: 'ETC', dataIndex: 'etcFee', key: 'etcFee', width: 88, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '电费', dataIndex: 'electricityFee', key: 'electricityFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '薪资', dataIndex: 'salary', key: 'salary', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '投入车数', dataIndex: 'vehicleCount', key: 'vehicleCount', width: 92, align: 'right', render: function (v) { return fmtIntCell(v); } }, + { title: '轮胎', dataIndex: 'tireCost', key: 'tireCost', width: 84, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '(折旧、年审、保险)费用', dataIndex: 'deprecInsuranceFee', key: 'deprecInsuranceFee', width: 168, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '社保服务费', dataIndex: 'socialSecurityFee', key: 'socialSecurityFee', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '挂车费用', dataIndex: 'trailerFee', key: 'trailerFee', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '停车费', dataIndex: 'parkingFee', key: 'parkingFee', width: 88, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '车总费用', dataIndex: 'vehicleTotalCost', key: 'vehicleTotalCost', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '总成本', dataIndex: 'totalCost', key: 'totalCost', width: 118, align: 'right', render: function (v) { return fmtCell(v); } }, + { + title: '盈亏月度合计', + dataIndex: 'profitLoss', + key: 'profitLoss', + width: 128, + align: 'right', + render: function (v) { + var s = fmtProfit(v); + var neg = v !== null && v !== undefined && v !== '' && !isNaN(Number(v)) && Number(v) < 0; + return neg ? React.createElement('span', { style: profitNegStyle }, s) : s; + } + } + ]; + }, []); + + var tableSummaryPlMonthly = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center' }, '总计'), + monthlyPlMetricKeys.map(function (k, idx) { + var text = k === 'vehicleCount' ? fmtIntCell(plMonthlySums[k]) : fmtSum(plMonthlySums[k] || 0); + var isNeg = k === 'profitLoss' && Number(plMonthlySums[k]) < 0; + var child = isNeg ? React.createElement('span', { style: profitNegStyle }, text) : text; + return React.createElement(SummaryCell, { key: k, index: idx + 1, align: 'right' }, child); + }) + ) + ); + }, [plMonthlySums]); + + var renderYearFilterCard = useCallback(function () { + return React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择年份'), + React.createElement(DatePicker, { + picker: 'year', + style: filterControlStyle, + placeholder: '请选择统计年份', + format: 'YYYY', + value: summaryYearDraft, + onChange: function (v) { setSummaryYearDraft(v); } + }) + ) + ) + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleSummaryReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleSummaryQuery }, '查询') + ) + ); + }, [summaryYearDraft, handleSummaryReset, handleSummaryQuery]); + + var tabItems = [ + { + key: 'personDetail', + label: '物流业务人员明细', + children: React.createElement(React.Fragment, null, + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '统计月份'), + React.createElement(DatePicker, { + picker: 'month', + style: filterControlStyle, + placeholder: '请选择年-月', + format: 'YYYY-MM', + value: draft.month, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { month: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务员'), + React.createElement(Select, { + placeholder: '请选择业务员', + style: filterControlStyle, + allowClear: true, + options: salespersonOptions, + value: draft.salesperson, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { salesperson: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务名称'), + React.createElement(Select, { + placeholder: '请选择业务名称', + style: filterControlStyle, + allowClear: true, + options: businessNameOptions, + value: draft.businessName, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { businessName: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ) + ), + 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(Card, { + extra: React.createElement(Button, { onClick: handleExport }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, reportTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'logistics-monthly-stat-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columns, + dataSource: filteredRows, + pagination: false, + size: 'small', + summary: tableSummary, + scroll: { x: 2600 } + }) + ) + ) + ) + }, + { + key: 'plMonthly', + label: '物流盈亏月度汇总', + children: React.createElement(React.Fragment, null, + renderYearFilterCard(), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportPlMonthly }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, plSumTableTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'logistics-monthly-stat-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsPlMonthly, + dataSource: plMonthlyRows, + pagination: false, + size: 'small', + summary: tableSummaryPlMonthly, + scroll: { x: 2480 } + }) + ) + ) + ) + } + ]; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement(Breadcrumb, { + style: { marginBottom: 16 }, + items: [{ title: '数据分析' }, { title: '物流业务月度统计' }] + }), + React.createElement(Card, null, + React.createElement('style', null, tabsBarStyle), + React.createElement(Tabs, { + className: 'logistics-monthly-stat-tabs', + activeKey: mainTab, + onChange: function (k) { setMainTab(k); }, + items: tabItems + }) + ) + ) + ); +}; diff --git a/web端/数据分析/租赁客户氢费台账.jsx b/web端/数据分析/租赁客户氢费台账.jsx new file mode 100644 index 0000000..a4551d6 --- /dev/null +++ b/web端/数据分析/租赁客户氢费台账.jsx @@ -0,0 +1,694 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 数据分析 - 租赁客户氢费台账:Tab「租赁客户氢费明细」+「租赁客户氢费总计」(按客户×月汇总金额) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var App = antd.App; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Button = antd.Button; + var Table = antd.Table; + var Select = antd.Select; + var DatePicker = antd.DatePicker; + var Row = antd.Row; + var Col = antd.Col; + var Tabs = antd.Tabs; + var message = antd.message; + + var TableSummary = Table.Summary; + var SummaryRow = TableSummary.Row; + var SummaryCell = TableSummary.Cell; + var RangePicker = DatePicker.RangePicker; + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + function fmtCell(n) { + if (n === null || n === undefined || n === '') return '—'; + var x = Number(n); + if (isNaN(x)) return '—'; + if (x === 0) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + + function fmtSum(n) { + var x = Number(n); + if (isNaN(x)) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 6 }); + } + + function fmtDateDash(iso) { + if (!iso) return '—'; + try { + if (window.dayjs) { + var d = window.dayjs(iso); + if (!d.isValid()) return '—'; + return d.format('YYYY-MM-DD'); + } + } catch (e1) {} + try { + var p = String(iso).split(/[-/]/); + if (p.length >= 3) { + var y = parseInt(p[0], 10); + var m = parseInt(p[1], 10); + var day = parseInt(p[2], 10); + if (!isNaN(y) && !isNaN(m) && !isNaN(day)) { + return y + '-' + (m < 10 ? '0' + m : m) + '-' + (day < 10 ? '0' + day : day); + } + } + } catch (e2) {} + return String(iso); + } + + function escapeCsv(v) { + var s = v == null ? '' : String(v); + if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1 || s.indexOf('\r') !== -1) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; + } + + function downloadCsv(filename, lines) { + var csv = lines.map(function (row) { return row.map(escapeCsv).join(','); }).join('\n'); + var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + function getLastMonthPickerValue() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').startOf('month'); + } catch (e1) {} + try { + if (window.moment) return window.moment().subtract(1, 'month').startOf('month'); + } catch (e2) {} + return null; + } + + function getStatMonthKey(monthsAgo) { + var off = monthsAgo == null ? 1 : monthsAgo; + try { + if (window.dayjs) return window.dayjs().subtract(off, 'month').format('YYYY-MM'); + } catch (e1) {} + var d = new Date(); + d.setDate(1); + d.setMonth(d.getMonth() - off); + var y = d.getFullYear(); + var mo = d.getMonth() + 1; + return y + '-' + (mo < 10 ? '0' + mo : '' + mo); + } + + function getLastMonthRange() { + try { + if (window.dayjs) { + var s = window.dayjs().subtract(1, 'month').startOf('month'); + var e = window.dayjs().subtract(1, 'month').endOf('month'); + return [s, e]; + } + } catch (e1) {} + return [null, null]; + } + + function rowDayMs(iso) { + if (!iso) return NaN; + var t = Date.parse(String(iso).length <= 10 ? String(iso) + 'T12:00:00' : iso); + return isNaN(t) ? NaN : t; + } + + function inRange(iso, range) { + if (!range || !range[0] || !range[1]) return true; + var t = rowDayMs(iso); + if (isNaN(t)) return false; + var s = range[0].startOf ? range[0].startOf('day').valueOf() : NaN; + var e = range[1].endOf ? range[1].endOf('day').valueOf() : NaN; + if (isNaN(s) || isNaN(e)) return true; + return t >= s && t <= e; + } + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + /** 与「车辆租赁合同」列表表一致:单行不换行;本页含合计行故增加 summary 单元格 */ + var tableSingleLineStyle = + '.contract-list-table .ant-table-thead th,.contract-list-table .ant-table-tbody td,.contract-list-table .ant-table-summary td{white-space:nowrap;}'; + var listToolbarStyle = { marginBottom: 16, display: 'flex', justifyContent: 'flex-end', alignItems: 'center', flexWrap: 'wrap', gap: 8 }; + var tabsBarStyle = '.lease-h2-ledger-tabs .ant-tabs-nav{margin-bottom:0;}'; + + var bizReportTableTitleStyle = { + textAlign: 'center', + marginBottom: 16, + fontSize: 16, + fontWeight: 600, + color: 'rgba(0,0,0,0.88)' + }; + + var boldNumStyle = { fontWeight: 600 }; + + /** 原型明细(statMonth / bizDate 随「上一月、上上月」变化,与默认筛选区间一致) */ + var allDetailRows = useMemo(function () { + var m0 = getStatMonthKey(1); + var m1 = getStatMonthKey(2); + var p0 = m0.split('-'); + var p1 = m1.split('-'); + var y0 = parseInt(p0[0], 10); + var mo0 = parseInt(p0[1], 10); + var y1 = parseInt(p1[0], 10); + var mo1 = parseInt(p1[1], 10); + function iso(y, mo, day) { + return y + '-' + (mo < 10 ? '0' + mo : mo) + '-' + (day < 10 ? '0' + day : day); + } + return [ + { + key: 'h2-1', + statMonth: m0, + bizDate: iso(y0, mo0, 1), + salesperson: '刘念念', + customerName: '嘉兴古道物流有限公司', + plateNo: '沪A68122F', + quantityKg: 45.6, + location: '嘉兴嘉锦亭桥-北综合供能服务站', + purchasePrice: 28.5, + costAmount: 1299.6, + unitPrice: 32, + amount: 1459.2 + }, + { + key: 'h2-2', + statMonth: m0, + bizDate: iso(y0, mo0, 3), + salesperson: '尚建华', + customerName: '嘉兴古道物流有限公司', + plateNo: '粤AGP3513', + quantityKg: 38.2, + location: '嘉兴嘉锦亭桥-北综合供能服务站', + purchasePrice: 28.5, + costAmount: 1088.7, + unitPrice: 32, + amount: 1222.4 + }, + { + key: 'h2-3', + statMonth: m0, + bizDate: iso(y0, mo0, 5), + salesperson: '刘念念', + customerName: '杭州绿道城配科技有限公司', + plateNo: '浙A88888F', + quantityKg: 52, + location: '杭州钱塘综合能源站', + purchasePrice: 29, + costAmount: 1508, + unitPrice: 33.5, + amount: 1742 + }, + { + key: 'h2-4', + statMonth: m0, + bizDate: iso(y0, mo0, 12), + salesperson: '尚建华', + customerName: '宁波港联氢运物流有限公司', + plateNo: '浙B12345F', + quantityKg: 60.5, + location: '宁波北仑氢能示范站', + purchasePrice: 27.8, + costAmount: 1681.9, + unitPrice: 31.2, + amount: 1887.6 + }, + { + key: 'h2-5', + statMonth: m1, + bizDate: iso(y1, mo1, 2), + salesperson: '刘念念', + customerName: '嘉兴古道物流有限公司', + plateNo: '沪A68122F', + quantityKg: 41, + location: '嘉兴嘉锦亭桥-北综合供能服务站', + purchasePrice: 28.6, + costAmount: 1172.6, + unitPrice: 32, + amount: 1312 + }, + { + key: 'h2-6', + statMonth: m1, + bizDate: iso(y1, mo1, 18), + salesperson: '刘念念', + customerName: '杭州绿道城配科技有限公司', + plateNo: '浙A88888F', + quantityKg: 48.5, + location: '杭州钱塘综合能源站', + purchasePrice: 29.1, + costAmount: 1411.35, + unitPrice: 33.5, + amount: 1624.75 + } + ]; + }, []); + + var salespersonOptions = useMemo(function () { + var set = {}; + allDetailRows.forEach(function (r) { + if (r.salesperson) set[r.salesperson] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [allDetailRows]); + + var customerOptions = useMemo(function () { + var set = {}; + allDetailRows.forEach(function (r) { + if (r.customerName) set[r.customerName] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [allDetailRows]); + + var mainTabState = useState('detail'); + var mainTab = mainTabState[0]; + var setMainTab = mainTabState[1]; + + var rangeDefault = useMemo(function () { return getLastMonthRange(); }, []); + + var draftDetailState = useState(function () { + return { + dateRange: rangeDefault[0] && rangeDefault[1] ? [rangeDefault[0], rangeDefault[1]] : null, + salesperson: undefined, + customerName: undefined + }; + }); + var draftDetail = draftDetailState[0]; + var setDraftDetail = draftDetailState[1]; + + var appliedDetailState = useState(function () { + return { + dateRange: rangeDefault[0] && rangeDefault[1] ? [rangeDefault[0], rangeDefault[1]] : null, + salesperson: undefined, + customerName: undefined + }; + }); + var appliedDetail = appliedDetailState[0]; + var setAppliedDetail = appliedDetailState[1]; + + var draftTotalState = useState(function () { + return { + month: getLastMonthPickerValue(), + customerName: undefined + }; + }); + var draftTotal = draftTotalState[0]; + var setDraftTotal = draftTotalState[1]; + + var appliedTotalState = useState(function () { + return { + month: getLastMonthPickerValue(), + customerName: undefined + }; + }); + var appliedTotal = appliedTotalState[0]; + var setAppliedTotal = appliedTotalState[1]; + + var filteredDetailRows = useMemo(function () { + return (allDetailRows || []).filter(function (r) { + if (appliedDetail.dateRange && appliedDetail.dateRange[0] && appliedDetail.dateRange[1]) { + if (!inRange(r.bizDate, appliedDetail.dateRange)) return false; + } + if (appliedDetail.salesperson && r.salesperson !== appliedDetail.salesperson) return false; + if (appliedDetail.customerName && r.customerName !== appliedDetail.customerName) return false; + return true; + }); + }, [allDetailRows, appliedDetail.dateRange, appliedDetail.salesperson, appliedDetail.customerName]); + + var detailSums = useMemo(function () { + var q = 0; + var c = 0; + var a = 0; + filteredDetailRows.forEach(function (r) { + q += isNaN(Number(r.quantityKg)) ? 0 : Number(r.quantityKg); + c += isNaN(Number(r.costAmount)) ? 0 : Number(r.costAmount); + a += isNaN(Number(r.amount)) ? 0 : Number(r.amount); + }); + return { quantityKg: q, costAmount: c, amount: a }; + }, [filteredDetailRows]); + + var totalByCustomerMonthRows = useMemo(function () { + var mk = ''; + if (appliedTotal.month && appliedTotal.month.format) mk = appliedTotal.month.format('YYYY-MM'); + if (!mk) return []; + var map = {}; + allDetailRows.forEach(function (r) { + if (r.statMonth !== mk) return; + if (appliedTotal.customerName && r.customerName !== appliedTotal.customerName) return; + var k = r.customerName || ''; + if (!map[k]) { + map[k] = { key: 'sum-' + mk + '-' + k, customerName: k, statMonth: mk, quantityKg: 0, amount: 0 }; + } + map[k].quantityKg += isNaN(Number(r.quantityKg)) ? 0 : Number(r.quantityKg); + map[k].amount += isNaN(Number(r.amount)) ? 0 : Number(r.amount); + }); + return Object.keys(map).map(function (name) { return map[name]; }); + }, [allDetailRows, appliedTotal.month, appliedTotal.customerName]); + + var totalTabSums = useMemo(function () { + var q = 0; + var a = 0; + totalByCustomerMonthRows.forEach(function (r) { + q += r.quantityKg || 0; + a += r.amount || 0; + }); + return { quantityKg: q, amount: a }; + }, [totalByCustomerMonthRows]); + + var detailTitle = useMemo(function () { + return '租赁客户氢费明细表'; + }, []); + + var totalTitle = useMemo(function () { + if (appliedTotal.month && appliedTotal.month.format) { + return appliedTotal.month.format('YYYY年M月') + '租赁客户氢费总计'; + } + return '租赁客户氢费总计'; + }, [appliedTotal.month]); + + var handleQueryDetail = useCallback(function () { + setAppliedDetail(Object.assign({}, draftDetail)); + }, [draftDetail]); + + var handleResetDetail = useCallback(function () { + var def = { + dateRange: rangeDefault[0] && rangeDefault[1] ? [rangeDefault[0], rangeDefault[1]] : null, + salesperson: undefined, + customerName: undefined + }; + setDraftDetail(def); + setAppliedDetail(def); + }, [rangeDefault]); + + var handleQueryTotal = useCallback(function () { + setAppliedTotal(Object.assign({}, draftTotal)); + }, [draftTotal]); + + var handleResetTotal = useCallback(function () { + var def = { month: getLastMonthPickerValue(), customerName: undefined }; + setDraftTotal(def); + setAppliedTotal(def); + }, []); + + var handleExportDetail = useCallback(function () { + var rows = filteredDetailRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出'); + return; + } + var headers = [ + '业务员', + '客户名称', + '日期', + '车牌号', + '加氢数量', + '加氢地点', + '进价', + '成本金额', + '单价', + '金额' + ]; + var line = function (r) { + return [ + r.salesperson, + r.customerName, + fmtDateDash(r.bizDate), + r.plateNo, + fmtCell(r.quantityKg), + r.location, + fmtCell(r.purchasePrice), + fmtCell(r.costAmount), + fmtCell(r.unitPrice), + fmtCell(r.amount) + ]; + }; + var body = [headers].concat(rows.map(line)); + body.push([ + '汇总', + '', + '', + '', + fmtSum(detailSums.quantityKg), + '', + '', + fmtSum(detailSums.costAmount), + '', + fmtSum(detailSums.amount) + ]); + downloadCsv('租赁客户氢费明细_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 条记录'); + }, [filteredDetailRows, detailSums]); + + var handleExportTotal = useCallback(function () { + var rows = totalByCustomerMonthRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出,请选择统计月份并查询'); + return; + } + var headers = ['客户名称', '统计月份', '加氢数量合计', '金额合计']; + var body = [headers].concat( + rows.map(function (r) { + return [r.customerName, r.statMonth, fmtSum(r.quantityKg), fmtSum(r.amount)]; + }) + ); + body.push(['总计', '', fmtSum(totalTabSums.quantityKg), fmtSum(totalTabSums.amount)]); + downloadCsv('租赁客户氢费总计_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 条记录'); + }, [totalByCustomerMonthRows, totalTabSums]); + + function renderBoldMoney(v) { + var s = fmtCell(v); + return React.createElement('span', { style: boldNumStyle }, s); + } + + var columnsDetail = useMemo(function () { + return [ + { title: '业务员', dataIndex: 'salesperson', key: 'salesperson', width: 100, align: 'left' }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 200, ellipsis: true, align: 'left' }, + { title: '日期', dataIndex: 'bizDate', key: 'bizDate', width: 118, align: 'left', render: function (_v, r) { return fmtDateDash(r.bizDate); } }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120, align: 'left' }, + { title: '加氢数量', dataIndex: 'quantityKg', key: 'quantityKg', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '加氢地点', dataIndex: 'location', key: 'location', width: 260, ellipsis: true, align: 'left' }, + { title: '进价', dataIndex: 'purchasePrice', key: 'purchasePrice', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '成本金额', dataIndex: 'costAmount', key: 'costAmount', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '单价', dataIndex: 'unitPrice', key: 'unitPrice', width: 90, align: 'right', render: function (v) { return renderBoldMoney(v); } }, + { title: '金额', dataIndex: 'amount', key: 'amount', width: 110, align: 'right', render: function (v) { return renderBoldMoney(v); } } + ]; + }, []); + + var columnsTotal = useMemo(function () { + return [ + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 240, ellipsis: true, align: 'left' }, + { title: '统计月份', dataIndex: 'statMonth', key: 'statMonth', width: 110, align: 'center' }, + { title: '加氢数量合计', dataIndex: 'quantityKg', key: 'quantityKg', width: 130, align: 'right', render: function (v) { return fmtSum(v); } }, + { title: '金额合计', dataIndex: 'amount', key: 'amount', width: 130, align: 'right', render: function (v) { return React.createElement('span', { style: boldNumStyle }, fmtSum(v)); } } + ]; + }, []); + + var detailTableSummary = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center', colSpan: 4 }, '汇总'), + React.createElement(SummaryCell, { index: 4, align: 'right' }, fmtSum(detailSums.quantityKg)), + React.createElement(SummaryCell, { index: 5, align: 'center' }, '—'), + React.createElement(SummaryCell, { index: 6, align: 'right' }, '—'), + React.createElement(SummaryCell, { index: 7, align: 'right' }, fmtSum(detailSums.costAmount)), + React.createElement(SummaryCell, { index: 8, align: 'center' }, '—'), + React.createElement(SummaryCell, { index: 9, align: 'right' }, + React.createElement('span', { style: boldNumStyle }, fmtSum(detailSums.amount)) + ) + ) + ); + }, [detailSums]); + + var totalTableSummary = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center', colSpan: 2 }, '总计'), + React.createElement(SummaryCell, { index: 2, align: 'right' }, fmtSum(totalTabSums.quantityKg)), + React.createElement(SummaryCell, { index: 3, align: 'right' }, + React.createElement('span', { style: boldNumStyle }, fmtSum(totalTabSums.amount)) + ) + ) + ); + }, [totalTabSums]); + + var tabItems = [ + { + key: 'detail', + label: '租赁客户氢费明细', + children: React.createElement(React.Fragment, null, + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 10, lg: 8 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '日期范围'), + React.createElement(RangePicker, { + style: filterControlStyle, + placeholder: ['开始日期', '结束日期'], + format: 'YYYY-MM-DD', + value: draftDetail.dateRange, + onChange: function (v) { setDraftDetail(function (p) { return Object.assign({}, p, { dateRange: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 7, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务员'), + React.createElement(Select, { + placeholder: '请选择业务员', + style: filterControlStyle, + allowClear: true, + options: salespersonOptions, + value: draftDetail.salesperson, + onChange: function (v) { setDraftDetail(function (p) { return Object.assign({}, p, { salesperson: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 7, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请选择客户名称', + style: filterControlStyle, + allowClear: true, + options: customerOptions, + value: draftDetail.customerName, + onChange: function (v) { setDraftDetail(function (p) { return Object.assign({}, p, { customerName: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ) + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleResetDetail }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQueryDetail }, '查询') + ) + ), + React.createElement('div', { style: listToolbarStyle }, + React.createElement(Button, { onClick: handleExportDetail }, '导出') + ), + React.createElement(Card, null, + React.createElement('div', { style: bizReportTableTitleStyle }, detailTitle), + React.createElement(React.Fragment, null, + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'contract-list-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsDetail, + dataSource: filteredDetailRows, + pagination: false, + size: 'small', + summary: detailTableSummary, + scroll: { x: 1320 } + }) + ) + ) + ) + ) + }, + { + key: 'total', + label: '租赁客户氢费总计', + children: React.createElement(React.Fragment, null, + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '统计月份'), + React.createElement(DatePicker, { + picker: 'month', + style: filterControlStyle, + placeholder: '请选择年-月', + format: 'YYYY-MM', + value: draftTotal.month, + onChange: function (v) { setDraftTotal(function (p) { return Object.assign({}, p, { month: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '全部客户', + style: filterControlStyle, + allowClear: true, + options: customerOptions, + value: draftTotal.customerName, + onChange: function (v) { setDraftTotal(function (p) { return Object.assign({}, p, { customerName: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ) + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleResetTotal }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQueryTotal }, '查询') + ) + ), + React.createElement('div', { style: listToolbarStyle }, + React.createElement(Button, { onClick: handleExportTotal }, '导出') + ), + React.createElement(Card, null, + React.createElement('div', { style: bizReportTableTitleStyle }, totalTitle), + React.createElement(React.Fragment, null, + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'contract-list-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsTotal, + dataSource: totalByCustomerMonthRows, + pagination: false, + size: 'small', + summary: totalTableSummary, + scroll: { x: 720 } + }) + ) + ) + ) + ) + } + ]; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement(Breadcrumb, { + style: { marginBottom: 16 }, + items: [{ title: '数据分析' }, { title: '租赁客户氢费台账' }] + }), + React.createElement(Card, null, + React.createElement('style', null, tabsBarStyle), + React.createElement(Tabs, { + className: 'lease-h2-ledger-tabs', + activeKey: mainTab, + onChange: function (k) { setMainTab(k); }, + items: tabItems + }) + ) + ) + ); +}; diff --git a/web端/数据分析/租赁车辆收入明细.jsx b/web端/数据分析/租赁车辆收入明细.jsx new file mode 100644 index 0000000..8252d03 --- /dev/null +++ b/web端/数据分析/租赁车辆收入明细.jsx @@ -0,0 +1,801 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 数据分析 - 租赁车辆收入明细(租赁业务明细 Tab + 租赁业务盈亏月度汇总 Tab,汇总交互参考「业务部业绩明细」) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var App = antd.App; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Button = antd.Button; + var Table = antd.Table; + var Select = antd.Select; + var DatePicker = antd.DatePicker; + var Row = antd.Row; + var Col = antd.Col; + var Tabs = antd.Tabs; + var message = antd.message; + + var TableSummary = Table.Summary; + var SummaryRow = TableSummary.Row; + var SummaryCell = TableSummary.Cell; + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + function fmtCell(n) { + if (n === null || n === undefined || n === '') return '—'; + var x = Number(n); + if (isNaN(x)) return '—'; + if (x === 0) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + } + + function fmtSum(n) { + var x = Number(n); + if (isNaN(x)) return '—'; + return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 6 }); + } + + function fmtTextSlash(s) { + if (s === null || s === undefined || String(s).trim() === '') return '/'; + return String(s); + } + + function escapeCsv(v) { + var s = v == null ? '' : String(v); + if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1 || s.indexOf('\r') !== -1) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; + } + + function downloadCsv(filename, lines) { + var csv = lines.map(function (row) { return row.map(escapeCsv).join(','); }).join('\n'); + var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + function getLastMonthStatKey() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').format('YYYY-MM'); + } catch (e1) {} + var d = new Date(); + d.setDate(1); + d.setMonth(d.getMonth() - 1); + var y = d.getFullYear(); + var mo = d.getMonth() + 1; + return y + '-' + (mo < 10 ? '0' + mo : '' + mo); + } + + function getLastMonthPickerValue() { + try { + if (window.dayjs) return window.dayjs().subtract(1, 'month').startOf('month'); + } catch (e1) {} + try { + if (window.moment) return window.moment().subtract(1, 'month').startOf('month'); + } catch (e2) {} + return null; + } + + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + var tableSingleLineStyle = + '.lease-income-detail-table .ant-table-thead th,.lease-income-detail-table .ant-table-tbody td,.lease-income-detail-table .ant-table-summary td{white-space:nowrap;}'; + var tableMonthlyLineStyle = + '.lease-pl-monthly-table .ant-table-thead th,.lease-pl-monthly-table .ant-table-tbody td,.lease-pl-monthly-table .ant-table-summary td{white-space:nowrap;}'; + var tabsBarStyle = '.lease-income-tabs .ant-tabs-nav{margin-bottom:0;}'; + + var bizReportTableTitleStyle = { + textAlign: 'center', + marginBottom: 16, + fontSize: 16, + fontWeight: 600, + color: 'rgba(0,0,0,0.88)' + }; + + var outstandingPositiveStyle = { color: '#cf1322' }; + + var numericSumKeys = [ + 'deposit', + 'receivable', + 'received', + 'outstanding', + 'naturalMonthIncome', + 'hydrogenPrepay', + 'baseCost', + 'brokerage', + 'hydrogenFee', + 'totalCost' + ]; + + /** 月度盈亏汇总表:各月金额列 + 盈亏(自然月收入 − 总成本,可负) */ + var monthlyPLKeys = numericSumKeys.concat(['profit']); + + function sumMonthlyRows(rows, keys) { + var sums = {}; + keys.forEach(function (k) { + sums[k] = (rows || []).reduce(function (acc, row) { + var v = row[k]; + var n = v === null || v === undefined || v === '' ? 0 : Number(v); + return acc + (isNaN(n) ? 0 : n); + }, 0); + }); + return sums; + } + + function finalizeLeaseMonthlyRow(r) { + var inc = r.naturalMonthIncome; + var tc = r.totalCost; + var profit = null; + if (inc != null && inc !== '' && tc != null && tc !== '') { + var a = Number(inc); + var b = Number(tc); + if (!isNaN(a) && !isNaN(b)) profit = a - b; + } + return Object.assign({}, r, { profit: profit }); + } + + function sumRows(rows, keys) { + var sums = {}; + keys.forEach(function (k) { + sums[k] = (rows || []).reduce(function (acc, row) { + var v = row[k]; + var n = v === null || v === undefined || v === '' ? 0 : Number(v); + return acc + (isNaN(n) ? 0 : n); + }, 0); + }); + return sums; + } + + var lastMonthStatKey = useMemo(function () { + return getLastMonthStatKey(); + }, []); + + var deptOptions = useMemo(function () { + return [ + { value: '业务二部', label: '业务二部' }, + { value: '业务一部', label: '业务一部' } + ]; + }, []); + + var rawRowsTemplate = useMemo(function () { + var sm = lastMonthStatKey; + return [ + { + key: 'lv-1', + statMonth: sm, + plateNo: '沪A52898F', + vehicleType: '18T', + salesperson: '刘念忠', + nature: '纯租赁', + systemModel: '飞驰18T', + customerName: '上海虹钦物流有限公司', + contractDate: '2025.12.1-2026.11.30', + pickupDate: '2025-12-08', + deposit: 20000, + receivable: 18500, + received: 18500, + outstanding: 0, + naturalMonthIncome: 18500, + paymentDate: '2026-02-05', + hydrogenPrepay: 5000, + paymentMethod: '月付指付', + invoiceApplyDate: '2026-02-06', + baseCost: 12000, + brokerage: 800, + hydrogenFee: 3200, + totalCost: 16000 + }, + { + key: 'lv-2', + statMonth: sm, + plateNo: '粤AGF4535', + vehicleType: '4.5T', + salesperson: '冉建华', + nature: '试用车', + systemModel: '现代4.5T', + customerName: '杭州绿道城配科技有限公司', + contractDate: '2026.1.1-2026.12.31', + pickupDate: '', + deposit: 4500, + receivable: 4200, + received: 3000, + outstanding: 1200, + naturalMonthIncome: 4200, + paymentDate: '', + hydrogenPrepay: 0, + paymentMethod: '季度指付', + invoiceApplyDate: '', + baseCost: 2800, + brokerage: 0, + hydrogenFee: 900, + totalCost: 3700 + }, + { + key: 'lv-3', + statMonth: sm, + plateNo: '浙A88888F', + vehicleType: '49T', + salesperson: '刘念忠', + nature: '纯租赁', + systemModel: '苏龙18T', + customerName: '宁波港联氢运物流有限公司', + contractDate: '2025.11.15-2026.11.14', + pickupDate: '2025-11-20', + deposit: 50000, + receivable: 26800, + received: 20000, + outstanding: 6800, + naturalMonthIncome: 26800, + paymentDate: '2026-01-28', + hydrogenPrepay: 12000, + paymentMethod: '月付指付', + invoiceApplyDate: '2026-01-30', + baseCost: 18500, + brokerage: 1500, + hydrogenFee: 6200, + totalCost: 26200 + }, + { + key: 'lv-4', + statMonth: sm, + plateNo: '苏E66666F', + vehicleType: '4.5T', + salesperson: '冉建华', + nature: '纯租赁', + systemModel: '现代4.5T', + customerName: '嘉兴南湖氢能示范运营', + contractDate: '2026.2.1-2027.1.31', + pickupDate: '2026-02-10', + deposit: 8000, + receivable: 3800, + received: 3800, + outstanding: 0, + naturalMonthIncome: 3800, + paymentDate: '2026-02-12', + hydrogenPrepay: 2000, + paymentMethod: '月付指付', + invoiceApplyDate: '2026-02-15', + baseCost: 2100, + brokerage: 200, + hydrogenFee: 650, + totalCost: 2950 + } + ]; + }, [lastMonthStatKey]); + + var salespersonOptions = useMemo(function () { + var set = {}; + (rawRowsTemplate || []).forEach(function (r) { + if (r.salesperson) set[r.salesperson] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [rawRowsTemplate]); + + var customerOptions = useMemo(function () { + var set = {}; + (rawRowsTemplate || []).forEach(function (r) { + if (r.customerName) set[r.customerName] = true; + }); + return Object.keys(set).map(function (n) { return { value: n, label: n }; }); + }, [rawRowsTemplate]); + + /** 原型:按年 1–12 月租赁业务金额汇总(联调后由接口按年返回) */ + var monthlyLeasePL2026 = useMemo(function () { + var raw = [ + { month: 1, deposit: 120000, receivable: 98500, received: 92000, outstanding: 6500, naturalMonthIncome: 92000, hydrogenPrepay: 18000, baseCost: 62000, brokerage: 4200, hydrogenFee: 15000, totalCost: 81200 }, + { month: 2, deposit: 45000, receivable: 53200, received: 48000, outstanding: 5200, naturalMonthIncome: 50500, hydrogenPrepay: 8000, baseCost: 31000, brokerage: 800, hydrogenFee: 9800, totalCost: 41600 }, + { month: 3, deposit: null, receivable: 66800, received: 66800, outstanding: 0, naturalMonthIncome: 66800, hydrogenPrepay: null, baseCost: 40200, brokerage: 1500, hydrogenFee: 12100, totalCost: 53800 }, + { month: 4, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 5, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 6, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 7, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 8, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 9, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 10, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 11, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null }, + { month: 12, deposit: null, receivable: null, received: null, outstanding: null, naturalMonthIncome: null, hydrogenPrepay: null, baseCost: null, brokerage: null, hydrogenFee: null, totalCost: null } + ]; + return raw.map(function (x) { + return finalizeLeaseMonthlyRow(Object.assign({ key: 'lease-pl-' + x.month }, x)); + }); + }, []); + + var mainTabState = useState('detail'); + var mainTab = mainTabState[0]; + var setMainTab = mainTabState[1]; + + function initialYear2026() { + try { + if (window.dayjs) return window.dayjs('2026-01-01'); + } catch (e1) {} + return null; + } + + var summaryYearDraftState = useState(initialYear2026); + var summaryYearDraft = summaryYearDraftState[0]; + var setSummaryYearDraft = summaryYearDraftState[1]; + + var summaryYearAppliedState = useState(initialYear2026); + var summaryYearApplied = summaryYearAppliedState[0]; + var setSummaryYearApplied = summaryYearAppliedState[1]; + + var draftState = useState(function () { + return { + month: getLastMonthPickerValue(), + salesperson: undefined, + customerName: undefined, + dept: '业务二部' + }; + }); + var draft = draftState[0]; + var setDraft = draftState[1]; + + var appliedState = useState(function () { + return { + month: getLastMonthPickerValue(), + salesperson: undefined, + customerName: undefined, + dept: '业务二部' + }; + }); + var applied = appliedState[0]; + var setApplied = appliedState[1]; + + var filteredRows = useMemo(function () { + return (rawRowsTemplate || []).filter(function (r) { + if (applied.month && applied.month.format) { + var mk = applied.month.format('YYYY-MM'); + if (r.statMonth !== mk) return false; + } + if (applied.salesperson && r.salesperson !== applied.salesperson) return false; + if (applied.customerName && r.customerName !== applied.customerName) return false; + return true; + }); + }, [rawRowsTemplate, applied.month, applied.salesperson, applied.customerName]); + + var columnSums = useMemo(function () { + return sumRows(filteredRows, numericSumKeys); + }, [filteredRows]); + + var reportTitle = useMemo(function () { + var dept = applied.dept || '业务二部'; + if (applied.month && applied.month.format) { + var y = applied.month.format('YYYY'); + return y + '年浙江羚牛氢能租赁车辆收入明细表(' + dept + ')'; + } + return '浙江羚牛氢能租赁车辆收入明细表(' + dept + ')'; + }, [applied.month, applied.dept]); + + var plSumTableTitle = useMemo(function () { + if (summaryYearApplied && summaryYearApplied.format) { + return '租赁业务' + summaryYearApplied.format('YYYY') + '年盈亏月度汇总'; + } + return '租赁业务盈亏月度汇总'; + }, [summaryYearApplied]); + + var monthlyDataSource = useCallback(function (rows) { + var y = summaryYearApplied && summaryYearApplied.format ? summaryYearApplied.format('YYYY') : null; + if (!y) return []; + if (y !== '2026') return []; + return rows; + }, [summaryYearApplied]); + + var plMonthlyRows = useMemo(function () { return monthlyDataSource(monthlyLeasePL2026); }, [monthlyDataSource, monthlyLeasePL2026]); + var plMonthlySums = useMemo(function () { return sumMonthlyRows(plMonthlyRows, monthlyPLKeys); }, [plMonthlyRows]); + + var handleQuery = useCallback(function () { + setApplied(Object.assign({}, draft)); + }, [draft]); + + var handleReset = useCallback(function () { + var def = { + month: getLastMonthPickerValue(), + salesperson: undefined, + customerName: undefined, + dept: '业务二部' + }; + setDraft(def); + setApplied(def); + }, []); + + var handleSummaryQuery = useCallback(function () { + setSummaryYearApplied(summaryYearDraft); + }, [summaryYearDraft]); + + var handleSummaryReset = useCallback(function () { + var y0 = initialYear2026(); + setSummaryYearDraft(y0); + setSummaryYearApplied(y0); + }, []); + + var handleExport = useCallback(function () { + var rows = filteredRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出'); + return; + } + var headers = [ + '车牌号码', + '车型', + '业务员', + '性质', + '系统车型', + '客户名称', + '合同日期', + '提车日期', + '押金', + '应收', + '实收', + '未收', + '自然月收入', + '付款日期', + '氢费预充值', + '付款方式', + '申请开票日期', + '成本', + '居间费', + '氢费', + '总成本' + ]; + var line = function (r) { + return [ + r.plateNo, + r.vehicleType, + r.salesperson, + r.nature, + r.systemModel, + r.customerName, + fmtTextSlash(r.contractDate), + fmtTextSlash(r.pickupDate), + fmtCell(r.deposit), + fmtCell(r.receivable), + fmtCell(r.received), + fmtCell(r.outstanding), + fmtCell(r.naturalMonthIncome), + fmtTextSlash(r.paymentDate), + fmtCell(r.hydrogenPrepay), + fmtTextSlash(r.paymentMethod), + fmtTextSlash(r.invoiceApplyDate), + fmtCell(r.baseCost), + fmtCell(r.brokerage), + fmtCell(r.hydrogenFee), + fmtCell(r.totalCost) + ]; + }; + var body = [headers].concat(rows.map(line)); + body.push([ + '汇总', '', '', '', '', '', '', '', + fmtSum(columnSums.deposit), + fmtSum(columnSums.receivable), + fmtSum(columnSums.received), + fmtSum(columnSums.outstanding), + fmtSum(columnSums.naturalMonthIncome), + '', + fmtSum(columnSums.hydrogenPrepay), + '', '', + fmtSum(columnSums.baseCost), + fmtSum(columnSums.brokerage), + fmtSum(columnSums.hydrogenFee), + fmtSum(columnSums.totalCost) + ]); + downloadCsv('租赁车辆收入明细_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 条记录'); + }, [filteredRows, columnSums]); + + var handleExportPLMonthly = useCallback(function () { + var rows = plMonthlyRows; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出,请先选择年份并查询'); + return; + } + var headers = [ + '月份', + '押金', + '应收', + '实收', + '未收', + '自然月收入', + '氢费预充值', + '成本', + '居间费', + '氢费', + '总成本', + '盈亏' + ]; + var body = [headers].concat( + rows.map(function (r) { + return [ + String(r.month), + fmtCell(r.deposit), + fmtCell(r.receivable), + fmtCell(r.received), + fmtCell(r.outstanding), + fmtCell(r.naturalMonthIncome), + fmtCell(r.hydrogenPrepay), + fmtCell(r.baseCost), + fmtCell(r.brokerage), + fmtCell(r.hydrogenFee), + fmtCell(r.totalCost), + fmtCell(r.profit) + ]; + }) + ); + body.push(['总计'].concat(monthlyPLKeys.map(function (k) { return fmtSum(plMonthlySums[k] || 0); }))); + downloadCsv('租赁业务盈亏月度汇总_' + new Date().getTime() + '.csv', body); + message.success('已导出 ' + rows.length + ' 个月度数据'); + }, [plMonthlyRows, plMonthlySums]); + + function renderOutstanding(v) { + var s = fmtCell(v); + if (v !== null && v !== undefined && v !== '' && !isNaN(Number(v)) && Number(v) > 0) { + return React.createElement('span', { style: outstandingPositiveStyle }, s); + } + return s; + } + + var columns = useMemo(function () { + return [ + { title: '车牌号码', dataIndex: 'plateNo', key: 'plateNo', width: 110, fixed: 'left', align: 'center' }, + { title: '车型', dataIndex: 'vehicleType', key: 'vehicleType', width: 72, align: 'center' }, + { title: '业务员', dataIndex: 'salesperson', key: 'salesperson', width: 88, align: 'center' }, + { title: '性质', dataIndex: 'nature', key: 'nature', width: 88, align: 'center' }, + { title: '系统车型', dataIndex: 'systemModel', key: 'systemModel', width: 100, align: 'center' }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 200, ellipsis: true }, + { title: '合同日期', dataIndex: 'contractDate', key: 'contractDate', width: 168, render: function (v) { return fmtTextSlash(v); } }, + { title: '提车日期', dataIndex: 'pickupDate', key: 'pickupDate', width: 110, render: function (v) { return fmtTextSlash(v); } }, + { title: '押金', dataIndex: 'deposit', key: 'deposit', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '应收', dataIndex: 'receivable', key: 'receivable', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '实收', dataIndex: 'received', key: 'received', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '未收', dataIndex: 'outstanding', key: 'outstanding', width: 100, align: 'right', render: function (v) { return renderOutstanding(v); } }, + { title: '自然月收入', dataIndex: 'naturalMonthIncome', key: 'naturalMonthIncome', width: 118, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '付款日期', dataIndex: 'paymentDate', key: 'paymentDate', width: 110, render: function (v) { return fmtTextSlash(v); } }, + { title: '氢费预充值', dataIndex: 'hydrogenPrepay', key: 'hydrogenPrepay', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '付款方式', dataIndex: 'paymentMethod', key: 'paymentMethod', width: 100, render: function (v) { return fmtTextSlash(v); } }, + { title: '申请开票日期', dataIndex: 'invoiceApplyDate', key: 'invoiceApplyDate', width: 120, render: function (v) { return fmtTextSlash(v); } }, + { title: '成本', dataIndex: 'baseCost', key: 'baseCost', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '居间费', dataIndex: 'brokerage', key: 'brokerage', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '氢费', dataIndex: 'hydrogenFee', key: 'hydrogenFee', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '总成本', dataIndex: 'totalCost', key: 'totalCost', width: 110, align: 'right', render: function (v) { return fmtCell(v); } } + ]; + }, []); + + var columnsPLMonthly = useMemo(function () { + return [ + { title: '月份', dataIndex: 'month', key: 'month', width: 72, align: 'center' }, + { title: '押金', dataIndex: 'deposit', key: 'deposit', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '应收', dataIndex: 'receivable', key: 'receivable', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '实收', dataIndex: 'received', key: 'received', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '未收', dataIndex: 'outstanding', key: 'outstanding', width: 100, align: 'right', render: function (v) { return renderOutstanding(v); } }, + { title: '自然月收入', dataIndex: 'naturalMonthIncome', key: 'naturalMonthIncome', width: 118, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '氢费预充值', dataIndex: 'hydrogenPrepay', key: 'hydrogenPrepay', width: 110, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '成本', dataIndex: 'baseCost', key: 'baseCost', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '居间费', dataIndex: 'brokerage', key: 'brokerage', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '氢费', dataIndex: 'hydrogenFee', key: 'hydrogenFee', width: 90, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '总成本', dataIndex: 'totalCost', key: 'totalCost', width: 100, align: 'right', render: function (v) { return fmtCell(v); } }, + { title: '盈亏', dataIndex: 'profit', key: 'profit', width: 100, align: 'right', render: function (v) { return fmtCell(v); } } + ]; + }, []); + + function renderPLMonthlySummary(sums) { + return function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center' }, '总计'), + monthlyPLKeys.map(function (k, idx) { + var cell = fmtSum(sums[k] || 0); + if (k === 'outstanding' && Number(sums.outstanding) > 0) { + cell = React.createElement('span', { style: outstandingPositiveStyle }, fmtSum(sums.outstanding || 0)); + } + return React.createElement( + SummaryCell, + { key: k, index: idx + 1, align: 'right' }, + cell + ); + }) + ) + ); + }; + } + + var renderYearFilterCard = useCallback(function () { + return React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '选择年份'), + React.createElement(DatePicker, { + picker: 'year', + style: filterControlStyle, + placeholder: '请选择统计年份', + format: 'YYYY', + value: summaryYearDraft, + onChange: function (v) { setSummaryYearDraft(v); } + }) + ) + ) + ), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleSummaryReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleSummaryQuery }, '查询') + ) + ); + }, [summaryYearDraft, handleSummaryReset, handleSummaryQuery]); + + var tableSummary = useCallback(function () { + return React.createElement( + TableSummary, + null, + React.createElement( + SummaryRow, + null, + React.createElement(SummaryCell, { index: 0, align: 'center', colSpan: 8 }, '汇总'), + React.createElement(SummaryCell, { index: 8, align: 'right' }, fmtSum(columnSums.deposit)), + React.createElement(SummaryCell, { index: 9, align: 'right' }, fmtSum(columnSums.receivable)), + React.createElement(SummaryCell, { index: 10, align: 'right' }, fmtSum(columnSums.received)), + React.createElement(SummaryCell, { index: 11, align: 'right' }, + Number(columnSums.outstanding) > 0 + ? React.createElement('span', { style: outstandingPositiveStyle }, fmtSum(columnSums.outstanding)) + : fmtSum(columnSums.outstanding) + ), + React.createElement(SummaryCell, { index: 12, align: 'right' }, fmtSum(columnSums.naturalMonthIncome)), + React.createElement(SummaryCell, { index: 13, align: 'center' }, '—'), + React.createElement(SummaryCell, { index: 14, align: 'right' }, fmtSum(columnSums.hydrogenPrepay)), + React.createElement(SummaryCell, { index: 15, align: 'center' }, '—'), + React.createElement(SummaryCell, { index: 16, align: 'center' }, '—'), + React.createElement(SummaryCell, { index: 17, align: 'right' }, fmtSum(columnSums.baseCost)), + React.createElement(SummaryCell, { index: 18, align: 'right' }, fmtSum(columnSums.brokerage)), + React.createElement(SummaryCell, { index: 19, align: 'right' }, fmtSum(columnSums.hydrogenFee)), + React.createElement(SummaryCell, { index: 20, align: 'right' }, fmtSum(columnSums.totalCost)) + ) + ); + }, [columnSums]); + + var tabItems = [ + { + key: 'detail', + label: '租赁业务明细', + children: React.createElement(React.Fragment, null, + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement(Row, { gutter: [16, 16], align: 'bottom' }, + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '统计月份'), + React.createElement(DatePicker, { + picker: 'month', + style: filterControlStyle, + placeholder: '请选择年-月', + format: 'YYYY-MM', + value: draft.month, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { month: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务部门'), + React.createElement(Select, { + placeholder: '请选择业务部门', + style: filterControlStyle, + options: deptOptions, + value: draft.dept, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { dept: v }); }); } + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务员'), + React.createElement(Select, { + placeholder: '请选择业务员', + style: filterControlStyle, + allowClear: true, + options: salespersonOptions, + value: draft.salesperson, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { salesperson: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ), + React.createElement(Col, { xs: 24, sm: 12, md: 8, lg: 6 }, + React.createElement('div', { style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请选择客户名称', + style: filterControlStyle, + allowClear: true, + options: customerOptions, + value: draft.customerName, + onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { customerName: v }); }); }, + showSearch: true, + filterOption: filterOption + }) + ) + ) + ), + 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(Card, { + extra: React.createElement(Button, { onClick: handleExport }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, reportTitle), + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'lease-income-detail-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columns, + dataSource: filteredRows, + pagination: false, + size: 'small', + summary: tableSummary, + scroll: { x: 2680 } + }) + ) + ) + ) + }, + { + key: 'monthlyPL', + label: '租赁业务盈亏月度汇总', + children: React.createElement(React.Fragment, null, + renderYearFilterCard(), + React.createElement(Card, { + extra: React.createElement(Button, { onClick: handleExportPLMonthly }, '导出') + }, + React.createElement('div', { style: bizReportTableTitleStyle }, plSumTableTitle), + React.createElement('style', null, tableMonthlyLineStyle), + React.createElement('div', { className: 'lease-pl-monthly-table' }, + React.createElement(Table, { + rowKey: 'key', + columns: columnsPLMonthly, + dataSource: plMonthlyRows, + pagination: false, + size: 'small', + summary: renderPLMonthlySummary(plMonthlySums), + scroll: { x: 1320 } + }) + ) + ) + ) + } + ]; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement(Breadcrumb, { + style: { marginBottom: 16 }, + items: [{ title: '数据分析' }, { title: '租赁车辆收入明细' }] + }), + React.createElement(Card, null, + React.createElement('style', null, tabsBarStyle), + React.createElement(Tabs, { + className: 'lease-income-tabs', + activeKey: mainTab, + onChange: function (k) { setMainTab(k); }, + items: tabItems + }) + ) + ) + ); +}; diff --git a/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx b/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx index bbfe74c..f00b180 100644 --- a/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-变更为三方合同.jsx @@ -52,6 +52,7 @@ const Component = function() { deliveryProvince: '浙江省', deliveryCity: '嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', + contractApprovalType: '标准合同审批', remarks: '', authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], rentalOrders: [ @@ -309,6 +310,7 @@ const Component = function() { 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: '合同审批类型' }, React.createElement(Input, { value: prevContractSample.contractApprovalType || '—', 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)'), @@ -407,7 +409,7 @@ const Component = function() { 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 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.合同审批类型:从原合同自动反写,必填项,选择器,分为「标准合同审批」「非标准合同审批」(原型只读展示);\n4.12.合同原件:显示原合同附件,同时支持上传新附件,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n4.13.备注:从原合同自动反写,如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码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(); } }, diff --git a/web端/车辆租赁合同/车辆租赁合同-新增.jsx b/web端/车辆租赁合同/车辆租赁合同-新增.jsx index 7278aa8..48b56eb 100644 --- a/web端/车辆租赁合同/车辆租赁合同-新增.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-新增.jsx @@ -69,6 +69,9 @@ const Component = function() { var bs11 = React.useState(''); var deliveryLocation = bs11[0]; var setDeliveryLocation = bs11[1]; + var bs11a = React.useState(''); + var contractApprovalType = bs11a[0]; + var setContractApprovalType = bs11a[1]; var bs12 = React.useState(''); var remarks = bs12[0]; var setRemarks = bs12[1]; @@ -245,6 +248,7 @@ const Component = function() { if (!signingCompany) errs.signingCompany = '请选择签约公司'; if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; if (!deliveryLocation || !String(deliveryLocation).trim()) errs.deliveryLocation = '请输入交车地点'; + if (!contractApprovalType) errs.contractApprovalType = '请选择合同审批类型'; 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()); }); @@ -257,7 +261,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.deliveryLocation || 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.contractApprovalType || 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'; 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; @@ -497,7 +501,8 @@ const Component = function() { 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.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } })) + React.createElement(FormItem, { label: '交车地点', required: true, error: formErrors.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同审批类型', required: true, error: formErrors.contractApprovalType }, React.createElement(Select, { placeholder: '请选择合同审批类型', style: { width: '100%' }, value: contractApprovalType || undefined, onChange: function(v) { setContractApprovalType(v || ''); }, status: formErrors.contractApprovalType ? 'error' : undefined }, React.createElement(Option, { value: '标准合同审批' }, '标准合同审批'), React.createElement(Option, { value: '非标准合同审批' }, '非标准合同审批'))) ); var contractFormRow1Part2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal }, @@ -637,7 +642,7 @@ 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 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.备注:文本域,支持自定义输入备注信息;\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 requirementContent = '车辆租赁合同-新增(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「新增租赁合同」模块,点击车辆租赁合同右上角「新增」进行创建;\n1.面包屑:\n#业务管理-车辆租赁合同-新增合同\n\n2.客户基本信息卡片:\n#用于从客户列表中选择客户,并将该合同绑定到业务部门及业务负责人(绑定业务部门/业务负责人主要为了后期从部门/业务负责人维度进行数据统计);\n2.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户(只显示已通过审核的客户);\n2.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;\n2.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;\n2.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;\n2.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;\n2.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;\n2.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;\n2.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;\n2.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;\n2.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;\n2.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;\n2.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;\n2.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;\n2.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人字段不可选;\n\n3.合同基本信息卡片:\n#用于定义租赁合同基本情况和付款方式;\n3.1.项目名称:必填项,输入框,用于定义该合同项目名称,默认提示信息"请输入项目名称";\n3.2.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;\n3.3.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期;\n3.4.付款方式:必选项,付款方式分为「预付」「后付」两种;\n 3.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户;\n 3.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户;\n3.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称;\n3.6.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;\n 3.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 3.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 3.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n3.7.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单;\n3.8.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据;\n3.9.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;\n3.10.交车地点:必填项,输入框,支持自定义输入交车地点;\n3.11.合同审批类型:必填项,选择器,分为「标准合同审批」「非标准合同审批」;\n3.12.合同原件:必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.13.备注:文本域,支持自定义输入备注信息;\n\n4.被授权人信息卡片:\n#用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认;\n4.1.被授权人:必填项,输入框,用于输入被授权人信息;\n4.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;\n4.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;\n4.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可从多个授权人中选择接收授权人;\n\n5.租赁订单信息卡片:\n#用于定义租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n5.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 5.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 5.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 5.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 5.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n5.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注,默认且至少显示一行空数据;\n 5.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;\n 5.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;\n 5.2.3.型号:必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号;\n 5.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写;\n 5.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写;\n 5.2.6.车辆月租金:输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算;\n 5.2.7.服务费项目:点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;\n 5.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;\n 5.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;\n 5.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD;\n 5.2.7.4.操作:删除,点击删除直接删除该行数据;\n 5.2.7.5.新增一行数据:点击添加一行服务项目;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 5.2.8.服务费:自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元;\n 5.2.9.保证金:必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中;\n 5.2.10.备注:选填项,输入框,用于备注车辆复杂情况;\n 5.2.11.操作:删除,点击删除删除该行数据;\n 5.2.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息;\n5.3.氢费承担方:必选项,选择器,选项为:「我方」、「客户」,默认选择「客户」;\n 5.3.1.选择「我方」:不显示付款方式、氢气预付款;\n 5.3.2.选择「客户」:付款方式字段默认为预付,可手动修改;\n5.4.付款方式:必选项,选择器,选项为:「预付」、「月付款」、「自行结算」,默认为:「预付」;\n 5.4.1.预付:选择「预付」,需要填写:「氢气预付款」,氢气预付款指合同签署时客户就需预先付出的氢费款项,该部分款项会自动计入提车应收款中氢气预付款金额;\n 5.4.2.月付款:选择「月付款」,提车应收款中不进行收费,而是由业务人员按照实际情况,通过氢费账单功能生成对应氢费账单,单独与客户进行结算;\n 5.4.3.自行结算:选择「自行结算」,指合同签署后,所有氢气费用由客户自行承担;\n5.5.氢气预付款:选择「客户」「预付」时显示,必填项,输入框,支持2位小数,氢气预付款金额会计算入该合同交车应收款中,并计入5.1.4.氢气预付款金额中;\n5.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,不管氢费承担方和付款方式选择任何选项都需要进行维护,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n6.其他费用信息卡片:\n#用于选择对应租赁费用模板,选择后展示证照补办费用、违约金费用、易损件费用、其他费用等信息,租赁费用模板管理功能位于「车辆租赁合同」列表左上角;\n6.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 6.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;\n 6.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n\n7.账单计算方式卡片:\n#必选项,填充按钮组,默认为按自然月结算,可手动修改,用于定义租赁合同的账单计算方式,分为「按自然月结算」、「按付款周期天数结算」两种方式;\n7.1.按付款周期天数结算:账单按照合同基本信息卡片中每隔付款周期*30天形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款从交车任务配置的开始计费时间开始,根据60天收取车辆租金,此后每隔60天生成一期租赁账单;\n7.2.按自然月结算:账单按照第一个月计费开始日期到当月最后一天为第一期,之后按照付款周期所选月份间隔,从开始月份第一天到间隔月份最后一天的自然月方式形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款车辆租金需要收取≥2个月租金作为标准流程,<2个月租金作为非标流程;\n 租赁账单首期从交车任务配置的开始计费时间开始,如付款周期为2个月,则首期账单结束时间为第二个月最后一天,付款周期为3个月,则首期账单结束时间为第三个月最后一天,此后每一期按照实际自然月开始-结束形成账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;\n\n8.最下方为提交并审核、保存、取消三个按钮;\n8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;\n8.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作;\n8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 720 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px', overflow: 'auto' }) }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementContent)), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; return React.createElement('div', { style: styles.page }, diff --git a/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx b/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx index 70ee8c6..8778868 100644 --- a/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-新增车辆.jsx @@ -275,7 +275,7 @@ const Component = function() { var setContractBasicOriginals = _contractBasicOriginals[1]; var contractBasicInputRef = React.useRef(null); 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 mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', contractApprovalType: '标准合同审批', 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: '' }]; var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' }; @@ -375,7 +375,8 @@ const Component = function() { ) ) ), - React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }) + React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }), + React.createElement(FormItemReadOnly, { label: '合同审批类型', value: mockContract.contractApprovalType || '—' }) ); var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' })); @@ -559,7 +560,7 @@ const Component = function() { React.createElement('tbody', null, historyTableRows) ); - 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.备注:文本域,支持自定义输入备注信息;\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 requirementContent = '车辆租赁合同-新增(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「新增租赁合同」模块,点击车辆租赁合同右上角「新增」进行创建;\n1.面包屑:\n#业务管理-车辆租赁合同-新增合同\n\n2.客户基本信息卡片:\n#用于从客户列表中选择客户,并将该合同绑定到业务部门及业务负责人(绑定业务部门/业务负责人主要为了后期从部门/业务负责人维度进行数据统计);\n2.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户(只显示已通过审核的客户);\n2.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;\n2.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;\n2.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;\n2.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;\n2.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;\n2.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;\n2.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;\n2.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;\n2.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;\n2.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;\n2.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;\n2.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;\n2.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人字段不可选;\n\n3.合同基本信息卡片:\n#用于定义租赁合同基本情况和付款方式;\n3.1.项目名称:必填项,输入框,用于定义该合同项目名称,默认提示信息"请输入项目名称";\n3.2.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;\n3.3.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期;\n3.4.付款方式:必选项,付款方式分为「预付」「后付」两种;\n 3.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户;\n 3.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户;\n3.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称;\n3.6.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;\n 3.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 3.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 3.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n3.7.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单;\n3.8.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据;\n3.9.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;\n3.10.交车地点:必填项,输入框,支持自定义输入交车地点;\n3.11.合同审批类型:必填项,选择器,分为「标准合同审批」「非标准合同审批」;\n3.12.合同原件:必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.13.备注:文本域,支持自定义输入备注信息;\n\n4.被授权人信息卡片:\n#用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认;\n4.1.被授权人:必填项,输入框,用于输入被授权人信息;\n4.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;\n4.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;\n4.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可从多个授权人中选择接收授权人;\n\n5.租赁订单信息卡片:\n#用于定义租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n5.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 5.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 5.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 5.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 5.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n5.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注,默认且至少显示一行空数据;\n 5.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;\n 5.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;\n 5.2.3.型号:必选项,选择器,与品牌存在级联关系,未选择品牌则无法选择型号;\n 5.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配),可通过选择车牌号对品牌、型号进行反写;\n 5.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,根据所选车牌号从车辆表直接拉取进行反写;\n 5.2.6.车辆月租金:输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元,如还车时账单周期不满1个月,则按照:(车辆月租金/30)* 实际天数进行计算;\n 5.2.7.服务费项目:点击管理按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;\n 5.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;\n 5.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;\n 5.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD;\n 5.2.7.4.操作:删除,点击删除直接删除该行数据;\n 5.2.7.5.新增一行数据:点击添加一行服务项目;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 5.2.8.服务费:自动根据添加的所有服务费项目计算总额,支持2位小数,格式为:xx.xx元;\n 5.2.9.保证金:必填项,输入框,支持2位小数,后缀为元,用于填写车辆需要支付的保证金金额,保证金为提车应收款一次性支付,还车时需要计入退还费用中;\n 5.2.10.备注:选填项,输入框,用于备注车辆复杂情况;\n 5.2.11.操作:删除,点击删除删除该行数据;\n 5.2.12.添加一行:点击后列表新增一行,用于填写一条新的车辆租金费用信息;\n5.3.氢费承担方:必选项,选择器,选项为:「我方」、「客户」,默认选择「客户」;\n 5.3.1.选择「我方」:不显示付款方式、氢气预付款;\n 5.3.2.选择「客户」:付款方式字段默认为预付,可手动修改;\n5.4.付款方式:必选项,选择器,选项为:「预付」、「月付款」、「自行结算」,默认为:「预付」;\n 5.4.1.预付:选择「预付」,需要填写:「氢气预付款」,氢气预付款指合同签署时客户就需预先付出的氢费款项,该部分款项会自动计入提车应收款中氢气预付款金额;\n 5.4.2.月付款:选择「月付款」,提车应收款中不进行收费,而是由业务人员按照实际情况,通过氢费账单功能生成对应氢费账单,单独与客户进行结算;\n 5.4.3.自行结算:选择「自行结算」,指合同签署后,所有氢气费用由客户自行承担;\n5.5.氢气预付款:选择「客户」「预付」时显示,必填项,输入框,支持2位小数,氢气预付款金额会计算入该合同交车应收款中,并计入5.1.4.氢气预付款金额中;\n5.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,不管氢费承担方和付款方式选择任何选项都需要进行维护,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n6.其他费用信息卡片:\n#用于选择对应租赁费用模板,选择后展示证照补办费用、违约金费用、易损件费用、其他费用等信息,租赁费用模板管理功能位于「车辆租赁合同」列表左上角;\n6.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 6.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n 6.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;\n 6.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;\n\n7.账单计算方式卡片:\n#必选项,填充按钮组,默认为按自然月结算,可手动修改,用于定义租赁合同的账单计算方式,分为「按自然月结算」、「按付款周期天数结算」两种方式;\n7.1.按付款周期天数结算:账单按照合同基本信息卡片中每隔付款周期*30天形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款从交车任务配置的开始计费时间开始,根据60天收取车辆租金,此后每隔60天生成一期租赁账单;\n7.2.按自然月结算:账单按照第一个月计费开始日期到当月最后一天为第一期,之后按照付款周期所选月份间隔,从开始月份第一天到间隔月份最后一天的自然月方式形成一期账单;\n 例如付款周期为2个月,则交车任务交车成功时,提车应收款车辆租金需要收取≥2个月租金作为标准流程,<2个月租金作为非标流程;\n 租赁账单首期从交车任务配置的开始计费时间开始,如付款周期为2个月,则首期账单结束时间为第二个月最后一天,付款周期为3个月,则首期账单结束时间为第三个月最后一天,此后每一期按照实际自然月开始-结束形成账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;\n\n8.最下方为提交并审核、保存、取消三个按钮;\n8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;\n8.2.点击保存,会存储租赁订单已填写内容,不做必填项校验,同时显示在租赁合同列表中,该条数据只能保存人自己查看并编辑,其他人无法操作;\n8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; return React.createElement('div', { style: styles.page }, React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, diff --git a/web端/车辆租赁合同/车辆租赁合同-查看.jsx b/web端/车辆租赁合同/车辆租赁合同-查看.jsx index c6ba059..bd228bc 100644 --- a/web端/车辆租赁合同/车辆租赁合同-查看.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-查看.jsx @@ -190,7 +190,7 @@ const Component = function() { 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 mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', contractApprovalType: '标准合同审批', remarks: '' }; var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }]; var mockRentalOrders = [ { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vehicleStatus: '已交车', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }, { project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] }, @@ -306,7 +306,8 @@ const Component = function() { ) ) ), - React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }) + React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }), + React.createElement(FormItemReadOnly, { label: '合同审批类型', value: mockContract.contractApprovalType || '—' }) ); var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' })); @@ -511,7 +512,7 @@ const Component = function() { ) ); - var requirementContent = '车辆租赁合同-查看(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「查看租赁合同」模块,在车辆租赁合同操作列点击「查看」进行查看;\n1.面包屑:\n#业务管理-车辆租赁合同-查看合同\n\n3.客户基本信息卡片:\n#显示已审批合同实际信息,不可修改,当客户信息产生变更时,对该条历史合同数据不作更新,合同变更为三方合同时不在原有客户基本信息做修改,而是增加三方客户基本信息卡片;\n3.1.客户名称:从该条合同自动反查;\n3.2.客户统一信用代码:从该条合同自动反查;\n3.3.客户地址:从该条合同自动反查;\n3.4.客户联系人:从该条合同自动反查;\n3.5.客户电话:从该条合同自动反查;\n3.6.客户电子邮箱:从该条合同自动反查;\n3.7.企业名称:从该条合同自动反查;\n3.8.企业电话:从该条合同自动反查;\n3.9.邮寄地址:从该条合同自动反查;\n3.10.开户银行:从该条合同自动反查;\n3.11.银行账号:从该条合同自动反查;\n3.12.纳税人识别号:从该条合同自动反查;\n3.13.业务部门:从该条合同自动反查;\n3.14.业务负责人:从该条合同自动反查;\n\n4.丙方客户基本信息卡片(当合同转三方合同时显示):\n#显示已审批合同丙方实际信息,不可修改;;\n4.1.客户名称:从该条合同自动反查;\n4.2.客户统一信用代码:从该条合同自动反查;\n4.3.客户地址:从该条合同自动反查;\n4.4.客户联系人:从该条合同自动反查;\n4.5.客户电话:从该条合同自动反查;\n4.6.客户电子邮箱:从该条合同自动反查;\n4.7.企业名称:从该条合同自动反查;\n4.8.企业电话:从该条合同自动反查;\n4.9.邮寄地址:从该条合同自动反查;\n4.10.开户银行:从该条合同自动反查;\n4.11.银行账号:从该条合同自动反查;\n4.12.纳税人识别号:从该条合同自动反查;\n4.13.业务部门:从该条合同自动反查;\n4.14.业务负责人:从该条合同自动反查;\n\n5.合同基本信息卡片:\n#显示合同基本信息,不可修改;\n5.1.项目名称:从该条合同自动反查;\n5.2.合同编码:从该条合同自动反查,在新增租赁合同点击提交审核时生成;\n 合同编码由:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]组成;\n 5.2.1.地区简写:如上海为SH,嘉兴为JX;\n 5.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;\n 5.2.3.签约时间:显示合同签约时间,如20260216\n 5.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;\n 5.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团编号为1235的合同;\n 5.2.6.签署状态:A为正式合同,B为试用合同;\n 如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中编号为1235,也可以理解为第1235份合同;\n 5.2.7.如果该合同为续签合同,则自动在新合同编码后额外添加:(续签自:旧合同合同编码xxx);\n 5.2.8.如果该合同为转正式合同,则自动在新合同编码后额外添加:(转正式合同自:旧合同合同编码xxx);\n5.3.合同类型:从该条合同自动反查;\n5.4.生效日期:从该条合同自动反查;\n5.5.付款方式:从该条合同自动反查;\n5.6.主要车型:从该条合同自动反查;\n5.7.结束日期:从该条合同自动反查;\n 5.7.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 5.7.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 5.7.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n5.8.付款周期:从该条合同自动反查;\n5.9.签约公司:从该条合同自动反查;\n5.10.交车区域:从该条合同自动反查;\n5.11.交车地点:从该条合同自动反查;\n5.12.合同原件:从该条合同自动反查,显示「合同原件名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列,如果转为三方合同,则会显示新附件,显示「三方合同原件名称.格式」「文件大小」「上传时间」;\n5.13.备注:从该条合同自动反查;\n 5.13.1.如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx;\n 5.13.2.如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx;\n\n6.被授权人信息卡片:\n#显示所有被授权人信息,不可修改,如要新增被授权人,需要在列表中点击添加被授权人进行处理;\n6.1.被授权人:从该条合同自动反查;\n6.2.被授权人联系电话:从该条合同自动反查;\n6.3.被授权人身份证:从该条合同自动反查;\n\n7.租赁订单信息卡片:\n#显示租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n7.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 7.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 7.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 7.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 7.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n7.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 7.2.1.序号:从该条合同自动反查;\n 7.2.2.品牌:从该条合同自动反查;\n 7.2.3.型号:从该条合同自动反查;\n 7.2.4.车牌号:从该条合同自动反查;\n 7.2.5.车辆状态:分为已交车、已交车-临时替换、已交车-永久替换、待交车;\n 7.2.6.车辆识别代码:从该条合同自动反查;\n 7.2.7.车辆月租金:从该条合同自动反查;\n 7.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 7.2.7.1.服务项目:从该条合同自动反查;\n 7.2.7.2.费用:从该条合同自动反查;\n 7.2.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 7.2.8.服务费:从该条合同自动反查;\n 7.2.9.保证金:从该条合同自动反查;\n 7.2.10.备注:从该条合同自动反查;\n7.3.氢费承担方:从该条合同自动反查;\n7.4.付款方式:从该条合同自动反查;\n7.5.氢气预付款:从该条合同自动反查;\n7.6.退还车氢气单价:从该条合同自动反查,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n8.新增车辆信息卡片(用户每次为合同新增车辆时,都会生成一个新增车辆信息卡片):\n#显示租赁合同对应新增车辆的明细费用、氢费明细费用等相关信息;\n8.1.卡片标题显示为:新增车辆信息(YYYY-MM-DD)\n8.2.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额等相关信息;\n 8.2.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 8.2.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 8.2.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n8.3.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 8.3.1.序号:从该条合同自动反查;\n 8.3.2.品牌:从该条合同自动反查;\n 8.3.3.型号:从该条合同自动反查;\n 8.3.4.车牌号:从该条合同自动反查;\n 8.3.5.车辆识别代码:从该条合同自动反查;\n 8.3.6.车辆月租金:从该条合同自动反查;\n 8.3.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 8.3.7.1.服务项目:从该条合同自动反查;\n 8.3.7.2.费用:从该条合同自动反查;\n 8.3.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 8.3.8.服务费:从该条合同自动反查;\n 8.3.9.保证金:从该条合同自动反查;\n 8.3.10.备注:从该条合同自动反查;\n\n9.其他费用信息卡片:\n#显示对应租赁费用模板证照补办费用、违约金费用、易损件费用、其他费用等信息;\n9.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 9.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,从该条合同自动反查;\n 9.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n 9.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,从该条合同自动反查;\n 9.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n\n10.账单计算方式卡片:\n#显示实际结算方式;\n\n11.盖章合同附件:\n#显示财务审核完成后,由法务上传的盖章版合同文件,显示「盖章合同名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列;如果转为三方合同,则会重新审批,法务上传显示新附件,显示「三方盖章合同名称.格式」「文件大小」「上传时间」\n\n12.合同变更历史记录:\n#显示合同变更历史,包括序号、变更时间、操作类型、操作人、原始记录、备注;\n12.1.序号:显示变更历史序号,按照变更时间倒序(从近到远)进行排序;\n12.2.变更时间:显示变更时间,格式为:YYYY-MM-DD HH:MM;\n12.3.操作类型:包括变更为三方合同、添加车辆、添加授权人、续签合同、撤回合同、终止合同、转正式合同、保存;\n12.4.操作人:显示对应操作类型操作人员;\n12.5.原始记录:显示查看变更前记录,点击打开新页面至查看原始合同快照;\n12.6.备注:备注改动内容,包括:\n 12.6.1.变更为三方合同时:增加丙方客户“客户名称xxxxxx”;\n 12.6.2.添加车辆时:添加车辆“车牌号xxx”、“车牌号xxx”、“车牌号xxx”,每辆车单独一行显示;\n 12.6.3.添加授权人时:添加授权人“授权人xxxx”、“授权人xxx”,每个授权人单独一行显示;\n 12.6.4.续签合同时:续签自”原合同编码xxxxxx“;\n 12.6.5.撤回合同时:主动撤回;\n 12.6.6.终止合同时:主动终止;\n 12.6.7.转正式合同时:转正式合同自“原合同编码xxxx”;\n 12.6.8.保存:无;\n\n2.审批状态:\n按照流程引擎设置节点显示;\n\n13.最下方为返回按钮,点击返回车辆租赁合同列表;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; + var requirementContent = '车辆租赁合同-查看(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「查看租赁合同」模块,在车辆租赁合同操作列点击「查看」进行查看;\n1.面包屑:\n#业务管理-车辆租赁合同-查看合同\n\n3.客户基本信息卡片:\n#显示已审批合同实际信息,不可修改,当客户信息产生变更时,对该条历史合同数据不作更新,合同变更为三方合同时不在原有客户基本信息做修改,而是增加三方客户基本信息卡片;\n3.1.客户名称:从该条合同自动反查;\n3.2.客户统一信用代码:从该条合同自动反查;\n3.3.客户地址:从该条合同自动反查;\n3.4.客户联系人:从该条合同自动反查;\n3.5.客户电话:从该条合同自动反查;\n3.6.客户电子邮箱:从该条合同自动反查;\n3.7.企业名称:从该条合同自动反查;\n3.8.企业电话:从该条合同自动反查;\n3.9.邮寄地址:从该条合同自动反查;\n3.10.开户银行:从该条合同自动反查;\n3.11.银行账号:从该条合同自动反查;\n3.12.纳税人识别号:从该条合同自动反查;\n3.13.业务部门:从该条合同自动反查;\n3.14.业务负责人:从该条合同自动反查;\n\n4.丙方客户基本信息卡片(当合同转三方合同时显示):\n#显示已审批合同丙方实际信息,不可修改;;\n4.1.客户名称:从该条合同自动反查;\n4.2.客户统一信用代码:从该条合同自动反查;\n4.3.客户地址:从该条合同自动反查;\n4.4.客户联系人:从该条合同自动反查;\n4.5.客户电话:从该条合同自动反查;\n4.6.客户电子邮箱:从该条合同自动反查;\n4.7.企业名称:从该条合同自动反查;\n4.8.企业电话:从该条合同自动反查;\n4.9.邮寄地址:从该条合同自动反查;\n4.10.开户银行:从该条合同自动反查;\n4.11.银行账号:从该条合同自动反查;\n4.12.纳税人识别号:从该条合同自动反查;\n4.13.业务部门:从该条合同自动反查;\n4.14.业务负责人:从该条合同自动反查;\n\n5.合同基本信息卡片:\n#显示合同基本信息,不可修改;\n5.1.项目名称:从该条合同自动反查;\n5.2.合同编码:从该条合同自动反查,在新增租赁合同点击提交审核时生成;\n 合同编码由:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]组成;\n 5.2.1.地区简写:如上海为SH,嘉兴为JX;\n 5.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;\n 5.2.3.签约时间:显示合同签约时间,如20260216\n 5.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;\n 5.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团编号为1235的合同;\n 5.2.6.签署状态:A为正式合同,B为试用合同;\n 如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中编号为1235,也可以理解为第1235份合同;\n 5.2.7.如果该合同为续签合同,则自动在新合同编码后额外添加:(续签自:旧合同合同编码xxx);\n 5.2.8.如果该合同为转正式合同,则自动在新合同编码后额外添加:(转正式合同自:旧合同合同编码xxx);\n5.3.合同类型:从该条合同自动反查;\n5.4.生效日期:从该条合同自动反查;\n5.5.付款方式:从该条合同自动反查;\n5.6.主要车型:从该条合同自动反查;\n5.7.结束日期:从该条合同自动反查;\n 5.7.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 5.7.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 5.7.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n5.8.付款周期:从该条合同自动反查;\n5.9.签约公司:从该条合同自动反查;\n5.10.交车区域:从该条合同自动反查;\n5.11.交车地点:从该条合同自动反查;\n5.12.合同审批类型:从该条合同自动反查,可选值「标准合同审批」「非标准合同审批」;\n5.13.合同原件:从该条合同自动反查,显示「合同原件名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列,如果转为三方合同,则会显示新附件,显示「三方合同原件名称.格式」「文件大小」「上传时间」;\n5.14.备注:从该条合同自动反查;\n 5.14.1.如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码xxx;\n 5.14.2.如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx;\n\n6.被授权人信息卡片:\n#显示所有被授权人信息,不可修改,如要新增被授权人,需要在列表中点击添加被授权人进行处理;\n6.1.被授权人:从该条合同自动反查;\n6.2.被授权人联系电话:从该条合同自动反查;\n6.3.被授权人身份证:从该条合同自动反查;\n\n7.租赁订单信息卡片:\n#显示租赁合同对应车辆明细费用、氢费明细费用等相关信息;\n7.1.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;\n 7.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 7.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 7.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n 7.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;\n7.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 7.2.1.序号:从该条合同自动反查;\n 7.2.2.品牌:从该条合同自动反查;\n 7.2.3.型号:从该条合同自动反查;\n 7.2.4.车牌号:从该条合同自动反查;\n 7.2.5.车辆状态:分为已交车、已交车-临时替换、已交车-永久替换、待交车;\n 7.2.6.车辆识别代码:从该条合同自动反查;\n 7.2.7.车辆月租金:从该条合同自动反查;\n 7.2.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 7.2.7.1.服务项目:从该条合同自动反查;\n 7.2.7.2.费用:从该条合同自动反查;\n 7.2.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 7.2.8.服务费:从该条合同自动反查;\n 7.2.9.保证金:从该条合同自动反查;\n 7.2.10.备注:从该条合同自动反查;\n7.3.氢费承担方:从该条合同自动反查;\n7.4.付款方式:从该条合同自动反查;\n7.5.氢气预付款:从该条合同自动反查;\n7.6.退还车氢气单价:从该条合同自动反查,该金额主要用于与客户约定还车时,与交车时氢气差值以此费用进行自动计算和结算;\n\n8.新增车辆信息卡片(用户每次为合同新增车辆时,都会生成一个新增车辆信息卡片):\n#显示租赁合同对应新增车辆的明细费用、氢费明细费用等相关信息;\n8.1.卡片标题显示为:新增车辆信息(YYYY-MM-DD)\n8.2.上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额等相关信息;\n 8.2.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;\n 8.2.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;\n 8.2.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;\n8.3.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;\n 8.3.1.序号:从该条合同自动反查;\n 8.3.2.品牌:从该条合同自动反查;\n 8.3.3.型号:从该条合同自动反查;\n 8.3.4.车牌号:从该条合同自动反查;\n 8.3.5.车辆识别代码:从该条合同自动反查;\n 8.3.6.车辆月租金:从该条合同自动反查;\n 8.3.7.服务费项目:点击查看按钮弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间;\n 8.3.7.1.服务项目:从该条合同自动反查;\n 8.3.7.2.费用:从该条合同自动反查;\n 8.3.7.3.生效时间:从该条合同自动反查;\n #在提车应收款中,车辆的交车日期和服务费生效日期可能会存在不同的情况,所以服务费需要单独以生效时间进行计算,例如:\n 交车后车辆从1月1日开始计费,付款周期为2个月,则提车应收款租金会计算到3月1日,但是服务费生效时间为1月30日,此情况下需要以3月1日-1月30日,计算出服务费具体收费天数为30天,然后根据:(服务费费用/30)* 服务费收费天数30)进行计算;\n 8.3.8.服务费:从该条合同自动反查;\n 8.3.9.保证金:从该条合同自动反查;\n 8.3.10.备注:从该条合同自动反查;\n\n9.其他费用信息卡片:\n#显示对应租赁费用模板证照补办费用、违约金费用、易损件费用、其他费用等信息;\n9.1.选择费用模板:必选项,从「租赁费用模板」中拉取,选择后自动将该费用模板所有环节费用显示在合同中;\n 9.1.1.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,从该条合同自动反查;\n 9.1.2.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n 9.1.3.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,从该条合同自动反查;\n 9.1.4.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,从该条合同自动反查;\n\n10.账单计算方式卡片:\n#显示实际结算方式;\n\n11.盖章合同附件:\n#显示财务审核完成后,由法务上传的盖章版合同文件,显示「盖章合同名称.格式」「文件大小」「上传时间」,可能会存在多个附件,显示为一列;如果转为三方合同,则会重新审批,法务上传显示新附件,显示「三方盖章合同名称.格式」「文件大小」「上传时间」\n\n12.合同变更历史记录:\n#显示合同变更历史,包括序号、变更时间、操作类型、操作人、原始记录、备注;\n12.1.序号:显示变更历史序号,按照变更时间倒序(从近到远)进行排序;\n12.2.变更时间:显示变更时间,格式为:YYYY-MM-DD HH:MM;\n12.3.操作类型:包括变更为三方合同、添加车辆、添加授权人、续签合同、撤回合同、终止合同、转正式合同、保存;\n12.4.操作人:显示对应操作类型操作人员;\n12.5.原始记录:显示查看变更前记录,点击打开新页面至查看原始合同快照;\n12.6.备注:备注改动内容,包括:\n 12.6.1.变更为三方合同时:增加丙方客户“客户名称xxxxxx”;\n 12.6.2.添加车辆时:添加车辆“车牌号xxx”、“车牌号xxx”、“车牌号xxx”,每辆车单独一行显示;\n 12.6.3.添加授权人时:添加授权人“授权人xxxx”、“授权人xxx”,每个授权人单独一行显示;\n 12.6.4.续签合同时:续签自”原合同编码xxxxxx“;\n 12.6.5.撤回合同时:主动撤回;\n 12.6.6.终止合同时:主动终止;\n 12.6.7.转正式合同时:转正式合同自“原合同编码xxxx”;\n 12.6.8.保存:无;\n\n2.审批状态:\n按照流程引擎设置节点显示;\n\n13.最下方为返回按钮,点击返回车辆租赁合同列表;\n\n所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;'; var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 720 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px', overflow: 'auto' }) }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementContent)), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; var servicePopoverRentalOpen = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow]; diff --git a/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx b/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx index fe67f74..39e0b5c 100644 --- a/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-续签合同.jsx @@ -53,6 +53,7 @@ const Component = function() { deliveryProvince: '浙江省', deliveryCity: '嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', + contractApprovalType: '标准合同审批', remarks: '续签自原合同 JXZL20260216YW101235A', authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], rentalOrders: [ @@ -126,6 +127,9 @@ const Component = function() { var bs11 = React.useState(prevContractSample.deliveryLocation); var deliveryLocation = bs11[0]; var setDeliveryLocation = bs11[1]; + var bs11a = React.useState(prevContractSample.contractApprovalType); + var contractApprovalType = bs11a[0]; + var setContractApprovalType = bs11a[1]; var bs12 = React.useState('续签自:旧合同编码JXZL20260216YW101235A'); var remarks = bs12[0]; var setRemarks = bs12[1]; @@ -325,6 +329,7 @@ const Component = function() { if (!signingCompany) errs.signingCompany = '请选择签约公司'; if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; if (!deliveryLocation || !deliveryLocation.trim()) errs.deliveryLocation = '请输入交车地点'; + if (!contractApprovalType) errs.contractApprovalType = '请选择合同审批类型'; 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()); }); @@ -352,7 +357,7 @@ const Component = function() { 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.deliveryLocation || errs.contractOriginal) ? 'card-contract' + : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.deliveryLocation || errs.contractApprovalType || 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' @@ -600,7 +605,8 @@ const Component = function() { React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null)) ); var contractFormRow4 = React.createElement('div', { style: styles.formRow }, - React.createElement(FormItem, { label: '交车地点', required: true, error: formErrors.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setEdited(true); setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } })) + 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%' } })), + React.createElement(FormItem, { label: '合同审批类型', required: true, error: formErrors.contractApprovalType }, React.createElement(Select, { placeholder: '请选择合同审批类型', style: { width: '100%' }, value: contractApprovalType || undefined, onChange: function(v) { setEdited(true); setContractApprovalType(v || ''); }, status: formErrors.contractApprovalType ? 'error' : undefined }, React.createElement(Option, { value: '标准合同审批' }, '标准合同审批'), React.createElement(Option, { value: '非标准合同审批' }, '非标准合同审批'))) ); var contractFormRow5 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal }, @@ -747,7 +753,7 @@ 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 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 requirementContent = '车辆租赁合同-续签合同(2026年3月3日版本)\n「数字化资产ONE-OS运管平台」中的「车辆租赁合同」-「续签合同」模块,在车辆租赁合同操作列点击「续签合同」进行查看;\n1.面包屑:\n#业务管理-车辆租赁合同-续签合同\n\n2.客户基本信息卡片:\n#用于从客户列表中选择客户,并将该合同绑定到业务部门及业务负责人(绑定业务部门/业务负责人主要为了后期从部门/业务负责人维度进行数据统计);\n2.1.客户名称:从原合同自动反写,不可修改,如原客户信息发生改变则按照新信息进行反写;必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户(只显示已通过审核的客户);\n2.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;\n2.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;\n2.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;\n2.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;\n2.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;\n2.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;\n2.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;\n2.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;\n2.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;\n2.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;\n2.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;\n2.13.业务部门:从该条合同自动反写,必选项,选择器,从部门表中选择该租赁合同对应部门;\n2.14.业务负责人:从该条合同自动反写,必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人字段不可选;\n\n3.合同基本信息卡片:\n#用于定义租赁合同基本情况和付款方式;\n3.1.项目名称:从原合同自动反写,必填项,输入框,用于定义该合同项目名称,默认提示信息”请输入项目名称“;\n3.2.合同类型:从原合同自动反写,必选项,选择器,合同类型分为「正式合同」「试用合同」;\n3.3.生效日期:从原合同自动反写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天,默认为点击新增日期;\n3.4.付款方式:从原合同自动反写,必选项,付款方式分为「预付」「后付」两种;\n 3.4.1.如果选择预付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成就列入业务待办(工作台功能),同时以消息通知对应用户;\n 3.4.2.如果选择后付,以对应「付款方式」和「付款周期」规则,在每一期新账单生成时,将前一期账单列入业务待办(工作台功能),同时以消息通知对应用户;\n3.5.主要车型:输入框(禁用状态),根据租赁订单信息中所有所选车型,自动反写入输入框并以标签形式显示,支持多车型显示,标签显示:型号名称;\n3.6.结束日期:需要重新填写,必选项,日期选择器,格式为YYYY-MM-DD,精确至天;\n 3.6.1.合同结束日期前30天将以消息提醒方式提醒(消息中心、工作台);\n 3.6.2.到达合同结束日期时,租赁账单将会立刻停止计算,作为最后一期账单;\n 3.6.3.续签合同/转正式合同将重新生成账单,不会对旧合同账单做任何继承处理;\n3.7.付款周期:从原合同自动反写,必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期和账单计算方式规则,从交车任务形成的交车单进行完整交车后,定时自动生成账单;\n3.8.签约公司:从原合同自动反写,必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据;\n3.9.交车区域:从原合同自动反写,必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;\n3.10.交车地点:从原合同自动反写,必填项,输入框,支持自定义输入交车地点;\n3.11.合同审批类型:从原合同自动反写,必填项,选择器,分为「标准合同审批」「非标准合同审批」;\n3.12.合同原件:从原合同自动反写,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式);\n3.13.备注:从原合同自动反写,如果该合同为续签合同,则自动在已填备注信息上方额外添加:续签自:旧合同合同编码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(); } }, diff --git a/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx b/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx index 967913f..d903314 100644 --- a/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-转正式合同.jsx @@ -53,6 +53,7 @@ const Component = function() { deliveryProvince: '浙江省', deliveryCity: '嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', + contractApprovalType: '标准合同审批', remarks: '', authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], rentalOrders: [ @@ -128,6 +129,9 @@ const Component = function() { var bs11 = React.useState(prevContractSample.deliveryLocation); var deliveryLocation = bs11[0]; var setDeliveryLocation = bs11[1]; + var bs11a = React.useState(prevContractSample.contractApprovalType); + var contractApprovalType = bs11a[0]; + var setContractApprovalType = bs11a[1]; var bs12 = React.useState('转正式合同自:旧合同编码JXZL20260216YW101235A'); var remarks = bs12[0]; var setRemarks = bs12[1]; @@ -321,6 +325,7 @@ const Component = function() { if (!signingCompany) errs.signingCompany = '请选择签约公司'; if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; if (!deliveryLocation || !deliveryLocation.trim()) errs.deliveryLocation = '请输入交车地点'; + if (!contractApprovalType) errs.contractApprovalType = '请选择合同审批类型'; 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()); }); @@ -347,7 +352,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.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'; + 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.contractApprovalType || 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; @@ -579,7 +584,8 @@ const Component = function() { React.createElement(FormItem, { label: '交车区域', required: true, error: formErrors.deliveryRegion }, React.createElement('div', { id: 'delivery-region-wrap', style: { position: 'relative' } }, React.createElement(Input, { style: Object.assign({}, formErrors.deliveryRegion ? { borderColor: '#ff4d4f' } : {}, { cursor: 'pointer', caretColor: 'transparent', width: '100%' }), placeholder: '请选择省-市', value: deliveryRegionDisplay, readOnly: true, onClick: function() { setDeliveryRegionOpen(!deliveryRegionOpen); } }), deliveryRegionOpen ? React.createElement('div', { style: styles.regionCascader, onMouseDown: function() { deliveryRegionClickInsideRef.current = true; } }, React.createElement('div', { style: styles.regionCascaderCol }, regionList.map(function(r, i) { var isActive = r.province === deliveryProvince; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); setEdited(true); setDeliveryProvince(r.province); setDeliveryCity(''); } }, r.province); })), React.createElement('div', { style: styles.regionCascaderColLast }, deliveryProvince ? (regionList.find(function(x) { return x.province === deliveryProvince; }) || { cities: [] }).cities.map(function(c, i) { var isActive = c === deliveryCity; return React.createElement('div', { key: i, style: Object.assign({}, styles.regionCascaderItem, isActive ? { backgroundColor: '#e6f7ff', color: '#1890ff' } : {}), onMouseEnter: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = '#f5f5f5'; }, onMouseLeave: function(e) { if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'; }, onMouseDown: function(e) { e.preventDefault(); selectDeliveryRegion(deliveryProvince, c); } }, c); }) : React.createElement('div', { style: { padding: 16, color: '#999', fontSize: 13 } }, '请先选择省'))) : null)) ); var contractFormRow4 = React.createElement('div', { style: styles.formRow }, - React.createElement(FormItem, { label: '交车地点', required: true, error: formErrors.deliveryLocation }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setEdited(true); setDeliveryLocation(e.target.value); }, status: formErrors.deliveryLocation ? 'error' : undefined, style: { width: '100%' } })) + 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%' } })), + React.createElement(FormItem, { label: '合同审批类型', required: true, error: formErrors.contractApprovalType }, React.createElement(Select, { placeholder: '请选择合同审批类型', style: { width: '100%' }, value: contractApprovalType || undefined, onChange: function(v) { setEdited(true); setContractApprovalType(v || ''); }, status: formErrors.contractApprovalType ? 'error' : undefined }, React.createElement(Option, { value: '标准合同审批' }, '标准合同审批'), React.createElement(Option, { value: '非标准合同审批' }, '非标准合同审批'))) ); var contractFormRow5 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '合同原件', required: true, error: formErrors.contractOriginal }, @@ -759,8 +765,9 @@ const Component = function() { 3.8.签约公司:从原合同自动反写,必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示新增用户当前机构,可手动修改,后期需要考虑从签约公司维度统计合同相关数据; 3.9.交车区域:从原合同自动反写,必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员; 3.10.交车地点:从原合同自动反写,必填项,输入框,支持自定义输入交车地点; -3.11.合同原件:从原合同自动反写,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式); -3.12.备注:从原合同自动反写,如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx,文本域,支持自定义输入备注信息; +3.11.合同审批类型:从原合同自动反写,必填项,选择器,分为「标准合同审批」「非标准合同审批」; +3.12.合同原件:从原合同自动反写,必填项,按钮,按钮文字为:上传附件,支持多个附件上传(doc/docx/pdf格式); +3.13.备注:从原合同自动反写,如果该合同为转正式合同,则自动在已填备注信息上方额外添加:转正式合同自:旧合同合同编码xxx,文本域,支持自定义输入备注信息; 4.被授权人信息卡片: #用于定义租赁合同相关被授权人相关信息,被授权人在交车单完成时,需要选择被授权人,并通过被授权人手机短信,在E签宝进行签字确认; diff --git a/web端/车辆租赁合同/车辆租赁合同-附加费用.jsx b/web端/车辆租赁合同/车辆租赁合同-附加费用.jsx index 28c568f..dbcca48 100644 --- a/web端/车辆租赁合同/车辆租赁合同-附加费用.jsx +++ b/web端/车辆租赁合同/车辆租赁合同-附加费用.jsx @@ -37,6 +37,7 @@ const Component = function() { signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', + contractApprovalType: '标准合同审批', contractOriginalName: '租赁合同-嘉兴某某物流.pdf', remarks: '首年优惠', authorizedList: [ @@ -221,6 +222,7 @@ const Component = function() { React.createElement(FormItem, { label: '签约公司' }, React.createElement(Input, { value: detail.signingCompany, disabled: true, style: styles.inputDisabled })), React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: detail.deliveryRegion, disabled: true, style: styles.inputDisabled })), React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: detail.deliveryLocation, disabled: true, style: styles.inputDisabled })), + React.createElement(FormItem, { label: '合同审批类型' }, React.createElement(Input, { value: detail.contractApprovalType || '—', disabled: true, style: styles.inputDisabled })), React.createElement(FormItem, { label: '合同原件' }, React.createElement(Input, { value: detail.contractOriginalName, disabled: true, style: styles.inputDisabled })), React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { value: detail.remarks, disabled: true, style: { width: '100%', minHeight: 80, backgroundColor: '#f5f5f5' } })) ); diff --git a/web端/车辆租赁合同/车辆租赁合同.jsx b/web端/车辆租赁合同/车辆租赁合同.jsx index f13b042..d1eaa32 100644 --- a/web端/车辆租赁合同/车辆租赁合同.jsx +++ b/web端/车辆租赁合同/车辆租赁合同.jsx @@ -17,6 +17,7 @@ const Component = function() { var Popover = antd.Popover; var Dropdown = antd.Dropdown; var Modal = antd.Modal; + var Upload = antd.Upload; var message = antd.message; var App = antd.App; @@ -33,6 +34,7 @@ const Component = function() { var _businessDept = useState([]); var _businessOwner = useState([]); var _contractType = useState(['全部']); + var _contractApprovalType = useState(['全部']); var _creator = useState([]); var _endDateRange = useState(null); @@ -46,6 +48,7 @@ const Component = function() { businessDept: [], businessOwner: [], contractType: ['全部'], + contractApprovalType: ['全部'], creator: [], endDateRange: null }); @@ -65,6 +68,14 @@ const Component = function() { var _terminateModalVisible = useState(false); var _terminateModalRecord = useState(null); var _requirementModalVisible = useState(false); + var _stampModalVisible = useState(false); + var _stampModalRecord = useState(null); + var _stampFileList = useState([]); + // 上传盖章合同完成后,按合同 id 记录已上传(原型:与列表 legalStampedContractUploaded 合并判断) + var _stampedUploadedOverride = useState({}); + + // 联调时:当前登录用户所属部门为法务部时为 true(原型默认 true 便于演示「上传盖章合同」) + var isLegalDeptUser = true; // 模拟选项(与新增租赁合同保持一致) var contractCodeOptions = [ @@ -118,6 +129,11 @@ const Component = function() { { value: '正式合同', label: '正式合同' }, { value: '试用合同', label: '试用合同' } ]; + var contractApprovalTypeOptions = [ + { value: '全部', label: '全部' }, + { value: '标准合同审批', label: '标准合同审批' }, + { value: '非标准合同审批', label: '非标准合同审批' } + ]; var deptOptions = [ { value: '业务1部', label: '业务1部' }, { value: '业务2部', label: '业务2部' }, @@ -151,6 +167,7 @@ const Component = function() { businessDept: '业务1部', businessOwner: '张经理', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2026-02-16', contactName: '张三', contactPhone: '13800138001', @@ -158,7 +175,8 @@ const Component = function() { createTime: '2025-01-05 10:00', updater: '-', updateTime: '-', - remark: '草稿待完善' + remark: '草稿待完善', + legalStampedContractUploaded: undefined }, // 2. 未提交 + 草稿(试用合同) { @@ -176,6 +194,7 @@ const Component = function() { businessDept: '业务2部', businessOwner: '李专员', contractType: '试用合同', + contractApprovalType: '非标准合同审批', contractEndDate: '2025-08-01', contactName: '李四', contactPhone: '13800138002', @@ -183,7 +202,8 @@ const Component = function() { createTime: '2025-02-10 09:00', updater: '-', updateTime: '-', - remark: '试用期 3 个月' + remark: '试用期 3 个月', + legalStampedContractUploaded: undefined }, // 3. 待审批 + 已提交审批(初次提交,尚未有任何节点审批) { @@ -201,6 +221,7 @@ const Component = function() { businessDept: '业务3部', businessOwner: '王专员', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2026-06-30', contactName: '王五', contactPhone: '13800138003', @@ -208,7 +229,8 @@ const Component = function() { createTime: '2025-02-12 11:00', updater: '-', updateTime: '-', - remark: '-' + remark: '-', + legalStampedContractUploaded: undefined }, // 4. 审批中 + 已提交审批(已有节点审批,未走完) { @@ -227,6 +249,7 @@ const Component = function() { businessDept: '业务1部', businessOwner: '张经理', contractType: '正式合同', + contractApprovalType: '非标准合同审批', contractEndDate: '2026-03-01', contactName: '赵六', contactPhone: '13900139001', @@ -234,7 +257,8 @@ const Component = function() { createTime: '2025-02-14 09:00', updater: '李专员', updateTime: '2025-02-15 16:00', - remark: '-' + remark: '-', + legalStampedContractUploaded: undefined }, // 5. 审批中 + 变更(已通过审批后做了变更并重新提交) { @@ -252,6 +276,7 @@ const Component = function() { businessDept: '业务2部', businessOwner: '李专员', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2026-05-31', contactName: '孙七', contactPhone: '13900139002', @@ -259,7 +284,8 @@ const Component = function() { createTime: '2025-02-18 10:00', updater: '李专员', updateTime: '2025-02-22 14:00', - remark: '变更车辆数量' + remark: '变更车辆数量', + legalStampedContractUploaded: undefined }, // 6. 审批通过 + 合同进行中(正式合同) { @@ -279,6 +305,7 @@ const Component = function() { businessDept: '业务3部', businessOwner: '王专员', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2026-12-31', contactName: '周八', contactPhone: '13900139003', @@ -286,7 +313,8 @@ const Component = function() { createTime: '2025-01-15 09:00', updater: '王专员', updateTime: '2025-01-25 11:00', - remark: '-' + remark: '-', + legalStampedContractUploaded: false }, // 7. 审批通过 + 合同进行中(试用合同,可转正式) { @@ -304,6 +332,7 @@ const Component = function() { businessDept: '业务1部', businessOwner: '张经理', contractType: '试用合同', + contractApprovalType: '非标准合同审批', contractEndDate: '2025-05-31', contactName: '吴九', contactPhone: '13900139004', @@ -311,7 +340,8 @@ const Component = function() { createTime: '2025-01-28 10:00', updater: '-', updateTime: '-', - remark: '试用 3 个月' + remark: '试用 3 个月', + legalStampedContractUploaded: false }, // 8. 审批驳回 + 已提交审批(可编辑和重新提交) { @@ -330,6 +360,7 @@ const Component = function() { businessDept: '业务2部', businessOwner: '李专员', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2026-08-15', contactName: '郑十', contactPhone: '13900139005', @@ -337,7 +368,8 @@ const Component = function() { createTime: '2025-02-20 14:00', updater: '李专员', updateTime: '2025-02-23 09:00', - remark: '驳回原因:费用条款需调整' + remark: '驳回原因:费用条款需调整', + legalStampedContractUploaded: undefined }, // 9. 审批通过 + 到期合同(审批已通过但合同结束日期已过) { @@ -356,6 +388,7 @@ const Component = function() { businessDept: '业务3部', businessOwner: '王专员', contractType: '正式合同', + contractApprovalType: '标准合同审批', contractEndDate: '2024-12-31', contactName: '王五', contactPhone: '13800138003', @@ -363,7 +396,8 @@ const Component = function() { createTime: '2024-02-20 10:00', updater: '王专员', updateTime: '2024-12-20 16:00', - remark: '已到期可续签' + remark: '已到期可续签', + legalStampedContractUploaded: false }, // 10. 审批通过 + 已结束(操作列终止合同并完成审核) { @@ -381,6 +415,7 @@ const Component = function() { businessDept: '业务1部', businessOwner: '张经理', contractType: '正式合同', + contractApprovalType: '非标准合同审批', contractEndDate: '2025-01-15', contactName: '张三', contactPhone: '13800138001', @@ -388,7 +423,8 @@ const Component = function() { createTime: '2024-05-20 09:00', updater: '张经理', updateTime: '2025-01-10 14:00', - remark: '-' + remark: '-', + legalStampedContractUploaded: true } ]; @@ -428,6 +464,10 @@ const Component = function() { if (type && type.length > 0 && type.indexOf('全部') === -1) { list = list.filter(function(r) { return type.indexOf(r.contractType) !== -1; }); } + var capType = f.contractApprovalType; + if (capType && capType.length > 0 && capType.indexOf('全部') === -1) { + list = list.filter(function(r) { return capType.indexOf(r.contractApprovalType) !== -1; }); + } if (f.creator && f.creator.length > 0) { list = list.filter(function(r) { return f.creator.indexOf(r.creator) !== -1; }); } @@ -463,6 +503,7 @@ const Component = function() { businessDept: _businessDept[0] ? _businessDept[0].slice() : [], businessOwner: _businessOwner[0] ? _businessOwner[0].slice() : [], contractType: _contractType[0] ? _contractType[0].slice() : ['全部'], + contractApprovalType: _contractApprovalType[0] ? _contractApprovalType[0].slice() : ['全部'], creator: _creator[0] ? _creator[0].slice() : [], endDateRange: _endDateRange[0] }); @@ -478,6 +519,7 @@ const Component = function() { _businessDept[1]([]); _businessOwner[1]([]); _contractType[1](['全部']); + _contractApprovalType[1](['全部']); _creator[1]([]); _endDateRange[1](null); _appliedFilter[1]({ @@ -490,6 +532,7 @@ const Component = function() { businessDept: [], businessOwner: [], contractType: ['全部'], + contractApprovalType: ['全部'], creator: [], endDateRange: null }); @@ -544,6 +587,22 @@ const Component = function() { } _contractType[1](v); }, []); + var handleContractApprovalTypeChange = useCallback(function(v) { + if (!v || v.length === 0) { _contractApprovalType[1](['全部']); return; } + if (v.indexOf('全部') !== -1 && v.length > 1) { + var prevA = _contractApprovalType[0] || []; + var hadAllOnlyA = prevA.length === 1 && prevA.indexOf('全部') !== -1; + if (hadAllOnlyA) { + var nextA = []; + for (var ia = 0; ia < v.length; ia++) { if (v[ia] !== '全部') nextA.push(v[ia]); } + _contractApprovalType[1](nextA); + } else { + _contractApprovalType[1](['全部']); + } + return; + } + _contractApprovalType[1](v); + }, []); var addAuthorizedRow = useCallback(function() { _authorizedList[1](function(prev) { return prev.concat([{ name: '', phone: '', idCard: '' }]); }); @@ -649,6 +708,21 @@ const Component = function() { if (type === '试用合同') { items.push({ key: 'toFormal', label: '转正式合同', onClick: function() { message.info('转正式合同(原型)'); } }); } + // 审批通过且法务审核环节尚未上传盖章合同附件时,法务部员工可在「更多」中上传(上传完成后入口关闭) + if (approval === '审批通过' && isLegalDeptUser) { + var needStampUpload = (record.legalStampedContractUploaded === false) && !(_stampedUploadedOverride[0][record.id] === true); + if (needStampUpload) { + items.push({ + key: 'uploadStamped', + label: '上传盖章合同', + onClick: function() { + _stampModalRecord[1](record); + _stampFileList[1]([]); + _stampModalVisible[1](true); + } + }); + } + } return items; } @@ -751,6 +825,7 @@ const Component = function() { { title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 100 }, { title: '业务负责人', dataIndex: 'businessOwner', key: 'businessOwner', width: 100 }, { title: '合同类型', dataIndex: 'contractType', key: 'contractType', width: 100 }, + { title: '合同审批类型', dataIndex: 'contractApprovalType', key: 'contractApprovalType', width: 130 }, { title: '合同结束日期', dataIndex: 'contractEndDate', key: 'contractEndDate', width: 120 }, { title: '客户联系人', dataIndex: 'contactName', key: 'contactName', width: 100 }, { title: '联系电话', dataIndex: 'contactPhone', key: 'contactPhone', width: 120 }, @@ -865,6 +940,16 @@ const Component = function() { onChange: handleContractTypeChange, options: contractTypeOptions })), + React.createElement('div', { key: 'contractApprovalType', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同审批类型'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择', + style: filterControlStyle, + value: _contractApprovalType[0], + onChange: handleContractApprovalTypeChange, + options: contractApprovalTypeOptions + })), React.createElement('div', { key: 'creator', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '创建人'), React.createElement(Select, { @@ -885,7 +970,7 @@ const Component = function() { })) ]; - var filterCount = _filterExpanded[0] ? 11 : 3; + var filterCount = _filterExpanded[0] ? 12 : 3; var filterNodes = []; for (var i = 0; i < filterCount && i < filterItems.length; i++) { filterNodes.push(filterItems[i]); @@ -1034,6 +1119,61 @@ const Component = function() { scroll: { x: 920 } }) )), + React.createElement(Modal, { + title: '上传盖章合同', + open: _stampModalVisible[0], + onCancel: function() { + _stampModalVisible[1](false); + _stampModalRecord[1](null); + _stampFileList[1]([]); + }, + width: 560, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: function() { + _stampModalVisible[1](false); + _stampModalRecord[1](null); + _stampFileList[1]([]); + } }, '取消'), + React.createElement(Button, { key: 'ok', type: 'primary', onClick: function() { + var rec = _stampModalRecord[0]; + var list = _stampFileList[0] || []; + if (!list.length) { + message.warning('请先选择要上传的文件'); + return; + } + var pending = list.some(function(f) { return f.status === 'uploading'; }); + if (pending) { + message.warning('请等待文件上传完成'); + return; + } + _stampedUploadedOverride[1](function(prev) { + var n = Object.assign({}, prev); + if (rec) n[rec.id] = true; + return n; + }); + message.success('盖章合同已上传(原型)'); + _stampModalVisible[1](false); + _stampModalRecord[1](null); + _stampFileList[1]([]); + } }, '确认上传') + ] + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { marginBottom: 12, fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, + '合同编码:', + React.createElement('span', { style: { color: 'rgba(0,0,0,0.85)', fontWeight: 500 } }, _stampModalRecord[0] ? _stampModalRecord[0].contractCode : '-') + ), + React.createElement(Upload.Dragger, { + multiple: true, + fileList: _stampFileList[0], + onChange: function(info) { _stampFileList[1](info.fileList); }, + customRequest: function(opts) { + setTimeout(function() { + if (opts.onSuccess) opts.onSuccess('ok'); + }, 200); + }, + accept: '.pdf,.doc,.docx,image/*' + }, React.createElement('p', { style: { margin: 0, padding: '20px 0' } }, '点击或拖拽文件到此区域上传,支持多文件')) + )), React.createElement(Modal, { title: '需求说明', open: _requirementModalVisible[0], @@ -1047,7 +1187,7 @@ const Component = function() { React.createElement('div', { style: reqSectionStyle }, '1.面包屑:'), React.createElement('div', { style: reqSubStyle }, '1.1.业务管理-车辆租赁合同'), React.createElement('div', { style: reqSectionStyle }, '2.筛选:'), - React.createElement('div', { style: reqSubStyle }, '2.1.支持通过合同编码、项目名称、客户名称、签约公司、审批状态、合同状态、业务部门、业务负责人、合同类型、创建人、合同结束日期等条件进行筛选,右侧为重置、查询、展开/收起(筛选条件以3列显示,默认显示一行,点击展开/收起对筛选栏卡片进行展开/收起所有筛选条件),点击查询后,筛选条件与列表内容联动。点击重置会回到默认筛选条件并在列表展示结果:'), + React.createElement('div', { style: reqSubStyle }, '2.1.支持通过合同编码、项目名称、客户名称、签约公司、审批状态、合同状态、业务部门、业务负责人、合同类型、合同审批类型、创建人、合同结束日期等条件进行筛选,右侧为重置、查询、展开/收起(筛选条件以3列显示,默认显示一行,点击展开/收起对筛选栏卡片进行展开/收起所有筛选条件),点击查询后,筛选条件与列表内容联动。点击重置会回到默认筛选条件并在列表展示结果:'), React.createElement('div', { style: reqItemStyle }, '2.1.1.合同编码:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), React.createElement('div', { style: reqItemStyle }, '2.1.2.项目名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), React.createElement('div', { style: reqItemStyle }, '2.1.3.客户名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), @@ -1057,10 +1197,11 @@ const Component = function() { 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: reqItemStyle }, '2.1.10.合同审批类型:选择器,支持全选或多选,选项为:全部、标准合同审批、非标准合同审批;'), + React.createElement('div', { style: reqItemStyle }, '2.1.11.创建人:选择器,支持全选或多选,拉取所有业务相关部门下所有用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '2.1.12.合同结束日期:日期选择器,支持单输入框内双日历选择开始-结束时间;'), 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.租赁车辆数:显示租赁车辆数,点击显示气泡卡片,卡片中列表显示:车辆类型、品牌、型号、车牌号、实际交车日期;'), @@ -1089,29 +1230,31 @@ const Component = function() { React.createElement('div', { style: reqItemStyle }, '3.1.8.业务部门:显示租赁合同创建时所选业务部门,业务部门来自部门表(业务组);'), React.createElement('div', { style: reqItemStyle }, '3.1.9.业务负责人:显示租赁合同创建时所选业务负责人,与业务部门联动,查询该业务组下人员;'), React.createElement('div', { style: reqItemStyle }, '3.1.10.合同类型:显示租赁合同类型,类型包括:正式合同、试用合同,于创建租赁合同时选取;'), - React.createElement('div', { style: reqItemStyle }, '3.1.11.合同结束日期:显示租赁合同结束日期,精确至日,格式为YYYY-MM-DD;'), - React.createElement('div', { style: reqItemStyle }, '3.1.12.客户联系人:显示租赁合同客户联系人姓名,客户联系人姓名来自「客户管理」-「联系人」;'), - React.createElement('div', { style: reqItemStyle }, '3.1.13.联系电话:显示租赁合同客户联系电话,客户联系电话来自「客户管理」-「联系人手机」;'), - React.createElement('div', { style: reqItemStyle }, '3.1.14.创建人:显示租赁合同创建人姓名,取自操作用户姓名;'), - React.createElement('div', { style: reqItemStyle }, '3.1.15.创建时间:显示租赁合同创建时间,精确至分钟,格式为YYYY-MM-DD HH:MM;'), - 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: reqSubItemStyle }, '3.1.19.3.新增车辆:当「合同状态」为「合同进行中」时显示,仅能在租赁订单信息卡片下新订单中进行车辆新增;新增车辆提交后触发重新审核流程,审核通过后生效(不影响原有合同正常业务);'), - 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: 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.终止合同:当「合同状态」为「合同进行中」时显示,点击后进行二次确认,提示语:是否确认终止合同,确认按钮为提交审核,会重新发起合同审核流程,审核通过后,合同状态变更为:已结束;') + React.createElement('div', { style: reqItemStyle }, '3.1.11.合同审批类型:显示租赁合同创建时所选合同审批类型,包括:标准合同审批、非标准合同审批;'), + React.createElement('div', { style: reqItemStyle }, '3.1.12.合同结束日期:显示租赁合同结束日期,精确至日,格式为YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '3.1.13.客户联系人:显示租赁合同客户联系人姓名,客户联系人姓名来自「客户管理」-「联系人」;'), + React.createElement('div', { style: reqItemStyle }, '3.1.14.联系电话:显示租赁合同客户联系电话,客户联系电话来自「客户管理」-「联系人手机」;'), + React.createElement('div', { style: reqItemStyle }, '3.1.15.创建人:显示租赁合同创建人姓名,取自操作用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '3.1.16.创建时间:显示租赁合同创建时间,精确至分钟,格式为YYYY-MM-DD HH:MM;'), + React.createElement('div', { style: reqItemStyle }, '3.1.17.更新人:显示租赁合同最后一次更新人姓名,取自操作用户姓名,如无则显示:-;'), + React.createElement('div', { style: reqItemStyle }, '3.1.18.最后更新时间:显示租赁合同最后一次更新时间,精确至分钟,格式为YYYY-MM-DD HH:MM,如无则显示:-;'), + React.createElement('div', { style: reqItemStyle }, '3.1.19.备注:显示租赁合同创建时输入的备注信息,如无则显示:-;'), + React.createElement('div', { style: reqItemStyle }, '3.1.20.操作:操作分为:查看、编辑、新增车辆、续签合同、删除合同、撤回合同、添加被授权人、附加费用、变更为三方合同、转正式合同、终止合同、上传盖章合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.1.查看:跳转车辆租赁合同-查看;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.2.编辑:当「合同状态」为「草稿」时显示,点击跳转编辑租赁合同页面,编辑租赁合同页面可输入项参考「新增租赁合同」页面,并支持对保存时已填内容进行修改;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.3.新增车辆:当「合同状态」为「合同进行中」时显示,仅能在租赁订单信息卡片下新订单中进行车辆新增;新增车辆提交后触发重新审核流程,审核通过后生效(不影响原有合同正常业务);'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.4.续签合同:当「合同状态」为「合同进行中」、「合同到期」时显示,点击跳转车辆租赁合同-续签合同页,提交时重新触发租赁合同审核流程;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.5.删除合同:当「合同状态」为「草稿」时显示,点击删除合同时进行二次确认,提示语:是否确认删除该合同草稿;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.6.撤回合同:当「合同状态」为「已提交审核」时显示,点击撤回合同时进行二次确认,提示语:是否确认撤回该合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.7.添加被授权人:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中可编辑被授权人、被授权人联系电话、被授权人身份证,同时支持添加一行、删除已有行等操作;添加被授权人触发重新审核流程,审核通过后生效;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.8.附加费用:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中显示该租赁合同内:车辆类型、品牌、型号、车牌号,同时可对服务项目、费用、生效时间进行编辑,触发租赁合同审核流程,审核通过后生效;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.8.1.服务项目:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.8.2.费用:输入框,后缀为元,支持2位小数输入;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.8.3.生效时间:日期选择器,精确至日;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.9.变更为三方合同:当「合同状态」为「合同进行中」时显示,点击跳转车辆租赁合同-变更为三方合同页面,提交时重新触发租赁合同审核流程;;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.10.转正式合同:当「合同类型」为「试用合同」时显示,点击后跳转车辆租赁合同-转正式合同页面,提交时重新触发租赁合同审核流程;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.11.终止合同:当「合同状态」为「合同进行中」时显示,点击后进行二次确认,提示语:是否确认终止合同,确认按钮为提交审核,会重新发起合同审核流程,审核通过后,合同状态变更为:已结束;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.20.12.上传盖章合同:当「审批状态」为「审批通过」且法务审核环节尚未上传盖章合同附件时,在「更多」中显示,仅法务部员工可见并可操作;点击后弹出上传窗口,可浏览本地文件上传,支持多文件;上传完成后关闭该入口(列表不再展示「上传盖章合同」);') )) ) );