From 09cc45db36bdf93c720585841d887dd131fe2059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Fri, 27 Feb 2026 18:11:40 +0800 Subject: [PATCH] Initial commit: ONE-OS project Made-with: Cursor --- .DS_Store | Bin 0 -> 8196 bytes .vscode/settings.json | 2 + blueway/数据集管理.jsx | 506 ++++ docs/ARCO-DESIGN-SPEC.md | 125 + package.json | 25 + src/App.tsx | 15 + src/main.tsx | 10 + src/pages/VehicleManage/VehiclePlateModal.tsx | 116 + src/pages/VehicleManage/index.tsx | 384 +++ src/services/vehicle.ts | 145 ++ src/types/vehicle.ts | 129 + tsconfig.json | 23 + tsconfig.node.json | 11 + vite.config.ts | 10 + web端.zip | Bin 0 -> 191082 bytes web端/.DS_Store | Bin 0 -> 6148 bytes web端/业务管理/交车任务.jsx | 577 +++++ web端/业务管理/新增交车任务.jsx | 300 +++ web端/业务管理/查看交车任务.jsx | 123 + web端/业务管理/租赁账单.jsx | 933 +++++++ web端/业务管理/编辑交车任务.jsx | 283 +++ web端/登录.jsx | 446 ++++ web端/财务管理/提车首付款.jsx | 1048 ++++++++ web端/车辆租赁合同/.DS_Store | Bin 0 -> 6148 bytes .../车辆租赁合同/2026-02-27租赁合同修改点.zip | Bin 0 -> 58166 bytes web端/车辆租赁合同/合同续签.jsx | 690 +++++ web端/车辆租赁合同/新增租赁合同.jsx | 655 +++++ web端/车辆租赁合同/新增车辆.jsx | 448 ++++ web端/车辆租赁合同/查看租赁合同.jsx | 448 ++++ web端/车辆租赁合同/车辆租赁合同.jsx | 1064 ++++++++ web端/车辆租赁合同/转正式合同.jsx | 690 +++++ web端/车辆管理.jsx | 591 +++++ web端/运维管理/.DS_Store | Bin 0 -> 6148 bytes web端/运维管理/基本数据维护/停车场管理.jsx | 1319 ++++++++++ web端/运维管理/车辆业务/上牌管理.jsx | 938 +++++++ web端/运维管理/车辆业务/交车管理.jsx | 538 ++++ web端/运维管理/车辆业务/后装设备.jsx | 481 ++++ web端/运维管理/车辆业务/备车管理.jsx | 652 +++++ web端/运维管理/车辆业务/新增后装设备.jsx | 143 ++ web端/运维管理/车辆业务/新增备车.jsx | 472 ++++ web端/运维管理/车辆业务/查看备车.jsx | 275 ++ web端/运维管理/车辆业务/调拨管理.jsx | 2240 +++++++++++++++++ 小程序/App.jsx | 176 ++ 小程序/LoginPage.jsx | 286 +++ 小程序/WorkbenchPage.jsx | 232 ++ 小程序/login-preview.html | 20 + 小程序/workbench-preview.html | 20 + 47 files changed, 17589 insertions(+) create mode 100644 .DS_Store create mode 100644 .vscode/settings.json create mode 100644 blueway/数据集管理.jsx create mode 100644 docs/ARCO-DESIGN-SPEC.md create mode 100644 package.json create mode 100644 src/App.tsx create mode 100644 src/main.tsx create mode 100644 src/pages/VehicleManage/VehiclePlateModal.tsx create mode 100644 src/pages/VehicleManage/index.tsx create mode 100644 src/services/vehicle.ts create mode 100644 src/types/vehicle.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts create mode 100644 web端.zip create mode 100644 web端/.DS_Store create mode 100644 web端/业务管理/交车任务.jsx create mode 100644 web端/业务管理/新增交车任务.jsx create mode 100644 web端/业务管理/查看交车任务.jsx create mode 100644 web端/业务管理/租赁账单.jsx create mode 100644 web端/业务管理/编辑交车任务.jsx create mode 100644 web端/登录.jsx create mode 100644 web端/财务管理/提车首付款.jsx create mode 100644 web端/车辆租赁合同/.DS_Store create mode 100644 web端/车辆租赁合同/2026-02-27租赁合同修改点.zip create mode 100644 web端/车辆租赁合同/合同续签.jsx create mode 100644 web端/车辆租赁合同/新增租赁合同.jsx create mode 100644 web端/车辆租赁合同/新增车辆.jsx create mode 100644 web端/车辆租赁合同/查看租赁合同.jsx create mode 100644 web端/车辆租赁合同/车辆租赁合同.jsx create mode 100644 web端/车辆租赁合同/转正式合同.jsx create mode 100644 web端/车辆管理.jsx create mode 100644 web端/运维管理/.DS_Store create mode 100644 web端/运维管理/基本数据维护/停车场管理.jsx create mode 100755 web端/运维管理/车辆业务/上牌管理.jsx create mode 100644 web端/运维管理/车辆业务/交车管理.jsx create mode 100644 web端/运维管理/车辆业务/后装设备.jsx create mode 100755 web端/运维管理/车辆业务/备车管理.jsx create mode 100644 web端/运维管理/车辆业务/新增后装设备.jsx create mode 100644 web端/运维管理/车辆业务/新增备车.jsx create mode 100644 web端/运维管理/车辆业务/查看备车.jsx create mode 100644 web端/运维管理/车辆业务/调拨管理.jsx create mode 100644 小程序/App.jsx create mode 100644 小程序/LoginPage.jsx create mode 100644 小程序/WorkbenchPage.jsx create mode 100644 小程序/login-preview.html create mode 100644 小程序/workbench-preview.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ca937d0cedb2b36f7f8e7507ba6e6648b07d91b7 GIT binary patch literal 8196 zcmeHMK~EDw6n@iI*^0n|pr|ov>_HQaA`+r8hFUdBjDb*splIFQ4rOJTX?C|%iX^@2 z#pvB1VB*P(2QMDIc=cvX`~!ORq;F<+p)KWVjLsx8@6DU{y_x-XXQyuq03=^6odXyK z00XO#W;Zq+iWnDlM@dAMUX%p!05z~+ebVEO?d|Z^2F-wGKr^5j&{sso{%ofGi z<=j`Jy44J52L4M1#QPzGRmfZ<8%oMo2R1SVK$$|bEU051ATgOp<|5fpQbIAN${t7r zB~gk&6dcDh!X0HUk_{yl9EgGgiDo8IhC<}*h|5TKAUP#|{XX?pEc03+ zw8CnTxBPpeH_N$a$GTXgZ zJhZVfK6*McHhy9AbY|n?=m_fP$2T|A#!&X`rJ3T2x61faqz4O$-p$aX`djTY)V40$ zb=IQGA)n&Q`D&W+1*P|O@9*jDGy4Y)^bhtA4GkSUG<^8T(IZ9kfNeP|WnV1xD)+db z_p0<>*>_8YisiVhJRj1>VZp7_qB+p!IzvkjwoS9^74#Y0Hpp-{d`g*&H?_1B&~Uy< zUnrV=f#Y+}o92NVx|}U621xT{NLMG>62}?Wkm9L|7z*a7AE4ZUnP0Gc-(qE&U%&5J zHua`ho`)e{op&A1)XMv0SnN*wRD{e<{7^JzbGKsUq*6@CDg}SS55`&~N%q5&X{paL z&S*NDQ$0xIIeQAOz-^eromqz$@CshTd-w=n;5+;zUBo2E$tf~ME|UpzlT4Dk#39SX zC1M|8NyIn#nCizZ2~gX*$!i6ld7PEeCj5=~na5pP$DLDYefH( 0 ? React.createElement('div', { + style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', padding: '16px 0', gap: 16, fontSize: 13, color: colors.textSecondary } + }, + React.createElement('span', null, '共 ' + totalCount + ' 项'), + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement('button', { + type: 'button', + disabled: page <= 1, + onClick: function () { setPage(page - 1); }, + style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page <= 1 ? 'not-allowed' : 'pointer', opacity: page <= 1 ? 0.6 : 1 } + }, '上一页'), + React.createElement('span', null, page + ' / ' + Math.max(1, Math.ceil(totalCount / pageSize))), + React.createElement('button', { + type: 'button', + disabled: page >= Math.ceil(totalCount / pageSize), + onClick: function () { setPage(page + 1); }, + style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page >= Math.ceil(totalCount / pageSize) ? 'not-allowed' : 'pointer', opacity: page >= Math.ceil(totalCount / pageSize) ? 0.6 : 1 } + }, '下一页') + ) + ) : null, + + // 弹窗(查看/编辑/新增) + modalOpen && React.createElement('div', { + style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + onClick: function (e) { if (e.target === e.currentTarget) closeModal(); } + }, + React.createElement('div', { + style: { background: colors.cardBg, borderRadius: 8, width: 520, maxWidth: '90vw', maxHeight: '90vh', overflow: 'auto', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' }, + onClick: function (e) { e.stopPropagation(); } + }, + React.createElement('div', { style: { padding: '16px 24px', borderBottom: '1px solid ' + colors.border, fontSize: 16, fontWeight: 600 } }, + modalMode === 'view' ? '查看数据集' : (modalMode === 'edit' ? '编辑数据集' : '新建数据集') + ), + (modalRecord || modalMode === 'add') && React.createElement('div', { style: { padding: 24 } }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '数据集名称'), + modalMode === 'view' ? (modalRecord && modalRecord.name) : React.createElement('input', { + type: 'text', + placeholder: '请输入名称', + defaultValue: modalRecord && modalRecord.name, + style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' } + }) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '类型'), + modalMode === 'view' ? (modalRecord && modalRecord.type) : React.createElement('select', { + defaultValue: modalRecord && modalRecord.type, + style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' } + }, typeOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); })) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '备注'), + modalMode === 'view' ? (modalRecord && modalRecord.remark) || '-' : React.createElement('textarea', { + placeholder: '选填', + defaultValue: modalRecord && modalRecord.remark, + rows: 3, + style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box', resize: 'vertical' } + }) + ) + ), + React.createElement('div', { style: { padding: '12px 24px', borderTop: '1px solid ' + colors.border, display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + modalMode === 'view' + ? React.createElement('button', { type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '关闭') + : [ + React.createElement('button', { key: 'cancel', type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'), + React.createElement('button', { + key: 'save', + type: 'button', + onClick: function () { showToast(modalMode === 'add' ? '创建成功' : '已保存'); closeModal(); }, + style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.primary, color: '#fff', cursor: 'pointer', fontSize: 14 } + }, modalMode === 'add' ? '创建' : '保存') + ] + ) + ) + ), + + // 删除确认弹窗 + confirmOpen && confirmRecord && React.createElement('div', { + style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1001, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + onClick: function (e) { if (e.target === e.currentTarget) closeConfirm(); } + }, + React.createElement('div', { + style: { background: colors.cardBg, borderRadius: 8, padding: 24, width: 400, maxWidth: '90vw', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' }, + onClick: function (e) { e.stopPropagation(); } + }, + React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 8 } }, '删除数据集'), + React.createElement('div', { style: { color: colors.textSecondary, marginBottom: 24 } }, '确定要删除「' + (confirmRecord.name || '') + '」吗?此操作不可恢复。'), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + React.createElement('button', { type: 'button', onClick: closeConfirm, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'), + React.createElement('button', { + type: 'button', + onClick: function () { handleDelete(confirmRecord); }, + style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.danger, color: '#fff', cursor: 'pointer', fontSize: 14 } + }, '删除') + ) + ) + ), + + // 轻提示 + toastShow && React.createElement('div', { + style: { + position: 'fixed', + left: '50%', + bottom: 48, + transform: 'translateX(-50%)', + padding: '8px 16px', + background: 'rgba(0,0,0,0.75)', + color: '#fff', + borderRadius: 6, + fontSize: 14, + zIndex: 1002 + } + }, toastText) + ); +}; diff --git a/docs/ARCO-DESIGN-SPEC.md b/docs/ARCO-DESIGN-SPEC.md new file mode 100644 index 0000000..acd9445 --- /dev/null +++ b/docs/ARCO-DESIGN-SPEC.md @@ -0,0 +1,125 @@ +# Arco Design 设计规范学习笔记 + +> 参考:[Arco Design 设计原则](https://arco.design/docs/spec/philosophy) + +## 一、设计语言定位:务实的浪漫主义 + +Arco Design 用「**务实的浪漫主义**」形容自己的设计语言,也代表其工作模式。 + +- **务实**:清晰与一致 → 清晰的效率提升是基础,品牌一致性是目的 +- **浪漫**:韵律与开放 → 推敲设计的韵律,用包容开放的体系解决问题 + +设计原则相当于语言体系中的「修辞手法」,在实际设计中提供**模式化的思考结构**,让整个设计系统更完整、可复用。 + +--- + +## 二、八大设计原则 + +### 1. 及时反馈 + +**系统应让用户清楚当前状态,并及时给出对应反馈。** + +- 操作后要有明确、即时的状态变化(如加载、成功、失败) +- 避免用户不确定「有没有生效」「进行到哪一步」 + +--- + +### 2. 贴近现实 + +**用用户的语言、用词和熟悉的概念,而不是系统术语。** + +- 遵循现实世界的惯例 +- 让信息符合自然思考逻辑 +- 避免技术黑话、内部用语 + +--- + +### 3. 系统一致性 + +**同一用语、功能、操作在系统内保持一致。** + +- 相同功能用相同名称、相同交互方式 +- 相同类型的反馈用相同形式(如成功/错误提示) +- 有利于降低学习成本、建立心智模型 + +--- + +### 4. 防止错误发生 + +**比「出错后再提示」更好的是:用设计提前避免错误。** + +- 在用户做出选择/动作之前,就减少容易混淆或错误的选择 +- 通过禁用、约束、确认、默认值等方式预防问题 +- 错误提示是兜底,不是主要手段 + +--- + +### 5. 遵从习惯 + +**减少用户对操作目标的记忆负荷,动作和选项应尽量可见。** + +- 例如:填完表单后,下一步应是「生成/提交表单」,而不是笼统的「完成」 +- 步骤、选项要符合用户对流程的预期 +- 减少「需要记住才能操作」的情况 + +--- + +### 6. 突出重点 + +**用户的行为多是「扫」而不是「读」或「看」。** + +- 设计中应**突出重点**,弱化或剔除无关信息 +- 重要对话/流程中不堆砌无关内容 +- 通过层级、对比、留白引导注意力 + +--- + +### 7. 错误帮助 + +**错误信息要用自然语言表达,而不是仅显示错误码(如 404)。** + +- 准确说明问题所在 +- 给出建设性的解决建议 +- 帮助用户从错误中恢复,将损失降到最低 +- 提供清晰的说明和下一步指引 + +--- + +### 8. 人性化帮助 + +**理想情况是系统本身无需帮助文档;若需要帮助,应分层、适度。** + +帮助的优先级建议: + +1. **无需提示**:设计自解释,用户自然能懂 +2. **一次性提示**:如首次使用的引导、Tooltip +3. **常驻提示**:如表单旁的说明、占位符 +4. **帮助文档**:作为最后兜底,便于深度查阅 + +--- + +## 三、设计规范结构速览(Arco 官网) + +设计相关文档大致包括: + +| 类型 | 说明 | +|------------|------| +| 设计价值观 | 务实与浪漫的价值观定义 | +| 设计原则 | 上述八大原则(本文档重点) | +| 样式指南 | 视觉与交互的样式规范 | +| 组件用法 | 各组件(Button、Form、Table 等)的设计与使用规范 | + +开发侧还有:快速上手、暗黑模式、颜色、设计变量(Token)、国际化、定制主题等,与设计规范配合使用。 + +--- + +## 四、在项目中的使用建议 + +1. **做界面时**:先对照「及时反馈、贴近现实、一致性、防错、遵从习惯、突出重点」检查一版交互与文案。 +2. **写错误与帮助**:遵循「错误帮助」和「人性化帮助」,避免只扔错误码,并控制提示的侵入性。 +3. **统一用语与交互**:建立一份产品用词表与组件使用规范,对齐「系统一致性」。 +4. **用 Arco 组件时**:优先按官方组件用法与样式指南使用,便于保持「务实的浪漫主义」下的整体体验。 + +--- + +*文档根据 [Arco Design 设计原则](https://arco.design/docs/spec/philosophy) 整理,便于团队统一理解与落地。* diff --git a/package.json b/package.json new file mode 100644 index 0000000..a73bfbf --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "one-os", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@arco-design/web-react": "^2.67.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0" + }, + "devDependencies": { + "@types/node": "^22.9.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "typescript": "~5.6.2", + "vite": "^5.4.10" + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..3548eba --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,15 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import VehicleManage from '@/pages/VehicleManage'; + +function App() { + return ( + + + } /> + } /> + + + ); +} + +export default App; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..2d40348 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import '@arco-design/web-react/dist/css/arco.css'; +import App from './App'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +); diff --git a/src/pages/VehicleManage/VehiclePlateModal.tsx b/src/pages/VehicleManage/VehiclePlateModal.tsx new file mode 100644 index 0000000..6ce3ff5 --- /dev/null +++ b/src/pages/VehicleManage/VehiclePlateModal.tsx @@ -0,0 +1,116 @@ +import React, { useState } from 'react'; +import { Modal, Form, Input, Upload, Image, Message } from '@arco-design/web-react'; +import type { VehicleRecord } from '@/types/vehicle'; + +type Props = { + visible: boolean; + vehicle: VehicleRecord | null; + onCancel: () => void; + onOk: (vin: string, plateNo: string) => void; +}; + +export default function VehiclePlateModal({ visible, vehicle, onCancel, onOk }: Props) { + const [ocrLoading, setOcrLoading] = useState(false); + const [licenseImageUrl, setLicenseImageUrl] = useState(''); + const [form] = Form.useForm(); + + const handleUploadChange = (fileList: { file?: File; url?: string }[]) => { + const file = fileList[0]?.file; + if (!file) return; + const url = URL.createObjectURL(file); + setLicenseImageUrl(url); + setOcrLoading(true); + // 模拟 OCR 识别,实际对接 OCR 接口 + setTimeout(() => { + form.setFieldsValue({ vin: vehicle?.vin ?? '', plateNo: vehicle?.plateNo ?? '' }); + setOcrLoading(false); + }, 1500); + }; + + const handleOk = () => { + form.validate().then((values) => { + const v = (values.vin ?? '').trim(); + const p = (values.plateNo ?? '').trim(); + if (!v || !p) { + Message.warning('请填写车辆识别代号与车牌号'); + return; + } + if (vehicle && v !== vehicle.vin) { + Message.error('车辆识别代号与该车辆不匹配'); + return; + } + onOk(v, p); + reset(); + }).catch(() => {}); + }; + + const reset = () => { + setLicenseImageUrl(''); + setOcrLoading(false); + form.resetFields(); + }; + + const handleCancel = () => { + reset(); + onCancel(); + }; + + return ( + <> + +
+
+
行驶证照片
+ {licenseImageUrl ? ( + + ) : ( + { + const { file } = option; + if (file) handleUploadChange([{ file: file as File }]); + }} + > +
上传行驶证照片
+
+ )} +
+
+ {ocrLoading ? ( +
+ 识别中,请勿关闭页面 +
+ ) : ( +
+ + + + + + +
+ )} +
+
+
+ + ); +} diff --git a/src/pages/VehicleManage/index.tsx b/src/pages/VehicleManage/index.tsx new file mode 100644 index 0000000..7ba00e6 --- /dev/null +++ b/src/pages/VehicleManage/index.tsx @@ -0,0 +1,384 @@ +import React, { useState, useCallback } from 'react'; +import { + Breadcrumb, + Form, + Grid, + Select, + Cascader, + Input, + Button, + Space, + Table, + Card, + Message, + Dropdown, + Menu, + Checkbox, + Modal, + Upload, +} from '@arco-design/web-react'; +import type { TableColumnProps } from '@arco-design/web-react/es/Table'; +import { + IconExport, + IconImport, + IconMore, + IconSearch, + IconRefresh, +} from '@arco-design/web-react/icon'; +import { + regionOptions, + vehicleTypeOptions, + brandOptions, + modelOptions, + customerOptions, + businessDeptOptions, + contractNoOptions, + registeredOwnerOptions, + mockVehicleList, +} from '@/services/vehicle'; +import type { VehicleRecord, VehicleFilterForm } from '@/types/vehicle'; +import VehiclePlateModal from './VehiclePlateModal'; + +const { Row, Col } = Grid; + +const defaultFilter: VehicleFilterForm = {}; + +export default function VehicleManage() { + const [filter, setFilter] = useState(defaultFilter); + const [plateNoQuick, setPlateNoQuick] = useState(''); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(20); + const [plateModalVisible, setPlateModalVisible] = useState(false); + const [plateModalVehicle, setPlateModalVehicle] = useState(null); + const [form] = Form.useForm(); + + const loadList = useCallback( + (p = page, ps = pageSize) => { + setLoading(true); + setTimeout(() => { + const res = mockVehicleList({ + ...filter, + plateNo: plateNoQuick || undefined, + page: p, + pageSize: ps, + }); + setData(res.list); + setTotal(res.total); + setLoading(false); + }, 300); + }, + [filter, plateNoQuick, page, pageSize] + ); + + React.useEffect(() => { + loadList(); + }, [loadList]); + + const onFilterSubmit = (values: VehicleFilterForm) => { + setFilter(values); + setPage(1); + loadList(1, pageSize); + }; + + const onFilterReset = () => { + form.resetFields(); + setFilter(defaultFilter); + setPlateNoQuick(''); + setPage(1); + loadList(1, pageSize); + }; + + const onExport = () => { + if (selectedRowKeys.length === 0) { + Message.warning('请先勾选要导出的车辆'); + return; + } + Message.info(`导出 ${selectedRowKeys.length} 条记录(联调时对接导出接口)`); + }; + + const onBatchImport = () => { + Message.info('批量导入(联调时对接导入接口)'); + }; + + const onView = (record: VehicleRecord) => { + Message.info(`查看车辆详情:${record.plateNo || record.vin}`); + // 实际可: navigate(`/vehicle/detail/${record.id}`); + }; + + const onPlate = (record: VehicleRecord) => { + setPlateModalVehicle(record); + setPlateModalVisible(true); + }; + + const onPlateModalOk = (vin: string, plateNo: string) => { + if (plateModalVehicle && (vin !== plateModalVehicle.vin || !plateNo)) { + Message.error('车辆识别代号与该车辆不匹配'); + return; + } + Message.success('上牌信息已更新'); + setPlateModalVisible(false); + setPlateModalVehicle(null); + loadList(); + }; + + const moreMenu = (record: VehicleRecord) => ( + + onPlate(record)}>车辆上牌 + 车辆过户 + 车辆异动 + 车辆调拨 + 车辆报废 + 车辆年审 + 车辆出售 + 故障提报 + + ); + + const columns: TableColumnProps[] = [ + { title: '运营城市', dataIndex: 'operationCity', width: 120, fixed: 'left' }, + { title: '车辆识别代号', dataIndex: 'vin', width: 180, fixed: 'left' }, + { title: '车牌号', dataIndex: 'plateNo', width: 100, fixed: 'left' }, + { title: '车辆编号', dataIndex: 'vehicleNo', width: 100, render: (v) => v || '-' }, + { title: '车辆类型', dataIndex: 'vehicleType', width: 100 }, + { title: '品牌', dataIndex: 'brand', width: 90 }, + { title: '型号', dataIndex: 'model', width: 90 }, + { title: '车身颜色', dataIndex: 'bodyColor', width: 90 }, + { title: '归属停车场', dataIndex: 'parkingLot', width: 110 }, + { title: '客户名称', dataIndex: 'customerName', width: 100 }, + { title: '业务部门', dataIndex: 'businessDept', width: 100 }, + { title: '业务负责人', dataIndex: 'businessOwner', width: 100 }, + { title: '运营状态', dataIndex: 'operationStatus', width: 90 }, + { title: '库位状态', dataIndex: 'libStatus', width: 180 }, + { title: '出库状态', dataIndex: 'outboundStatus', width: 100 }, + { title: '整备状态', dataIndex: 'prepStatus', width: 90 }, + { title: '过户状态', dataIndex: 'transferStatus', width: 120 }, + { title: '维修状态', dataIndex: 'repairStatus', width: 110 }, + { title: '证照状态', dataIndex: 'licenseStatus', width: 90 }, + { title: '报废状态', dataIndex: 'scrapStatus', width: 90 }, + { title: '登记所有权', dataIndex: 'registeredOwner', width: 160 }, + { title: '在线状态', dataIndex: 'onlineStatus', width: 90 }, + { title: '出厂年份', dataIndex: 'manufactureYear', width: 90 }, + { title: '行驶公里数(KM)', dataIndex: 'mileage', width: 120, render: (v) => v?.toFixed(2) ?? '-' }, + { title: '采购入库时间', dataIndex: 'purchaseTime', width: 120 }, + { title: '行驶证注册日期', dataIndex: 'licenseRegisterDate', width: 130 }, + { title: '行驶证检验有效期', dataIndex: 'licenseExpiry', width: 130 }, + { title: '上次交车时间', dataIndex: 'lastDeliveryTime', width: 120 }, + { title: '上次交车里程(KM)', dataIndex: 'lastDeliveryMileage', width: 130, render: (v) => (v ? Number(v).toFixed(2) : '-') }, + { title: '上次还车时间', dataIndex: 'lastReturnTime', width: 120 }, + { title: '上次还车里程(KM)', dataIndex: 'lastReturnMileage', width: 130, render: (v) => (v ? Number(v).toFixed(2) : '-') }, + { title: '强制报废日期', dataIndex: 'forceScrapDate', width: 120 }, + { title: '合同编号', dataIndex: 'contractNo', width: 110 }, + { title: '当前位置', dataIndex: 'currentLocation', width: 300 }, + { title: 'GPS最后上传时间', dataIndex: 'gpsLastTime', width: 160 }, + { + title: '操作', + dataIndex: 'op', + width: 140, + fixed: 'right', + render: (_, record) => ( + + + + + + + ), + }, + ]; + + const watchedBrand = Form.useWatch('brand', form); + const modelOptionsByBrand = watchedBrand ? (modelOptions as Record)[watchedBrand] ?? [] : []; + + return ( +
+ + 运维管理 + 车辆管理 + + + +
+ + + + + + + + + + + + + + + + + + + + (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) + } + /> + + + + + loadList(1, pageSize)} + /> + + + + + + +
+ + 0} + indeterminate={selectedRowKeys.length > 0 && selectedRowKeys.length < data.length} + onChange={(checked) => setSelectedRowKeys(checked ? data.map((r) => r.id) : [])} + /> + ), + width: 48, + fixed: 'left', + render: (_, record) => ( + + setSelectedRowKeys((prev) => + checked ? [...prev, record.id] : prev.filter((k) => k !== record.id) + ) + } + /> + ), + }, + ...columns, + ]} + scroll={{ x: 3800 }} + pagination={{ + current: page, + pageSize, + total, + showTotal: true, + showJumper: true, + sizeCanChange: true, + pageSizeChange: (ps) => { + setPageSize(ps); + setPage(1); + loadList(1, ps); + }, + onChange: (p) => { + setPage(p); + loadList(p, pageSize); + }, + }} + /> + + + { setPlateModalVisible(false); setPlateModalVehicle(null); }} + onOk={onPlateModalOk} + /> + + ); +} diff --git a/src/services/vehicle.ts b/src/services/vehicle.ts new file mode 100644 index 0000000..9004ded --- /dev/null +++ b/src/services/vehicle.ts @@ -0,0 +1,145 @@ +import type { VehicleRecord, VehicleFilterForm } from '@/types/vehicle'; + +/** 模拟省-市二级地区 */ +export const regionOptions = [ + { + value: 'guangdong', + label: '广东省', + children: [ + { value: 'guangzhou', label: '广州市' }, + { value: 'shenzhen', label: '深圳市' }, + { value: 'dongguan', label: '东莞市' }, + ], + }, + { + value: 'zhejiang', + label: '浙江省', + children: [ + { value: 'hangzhou', label: '杭州市' }, + { value: 'ningbo', label: '宁波市' }, + ], + }, + { + value: 'jiangsu', + label: '江苏省', + children: [ + { value: 'nanjing', label: '南京市' }, + { value: 'suzhou', label: '苏州市' }, + ], + }, +]; + +/** 车辆类型(从车辆类型表拉取) */ +export const vehicleTypeOptions = [ + { value: 'light_truck', label: '轻卡' }, + { value: 'van', label: '厢式车' }, + { value: 'suv', label: 'SUV' }, + { value: 'sedan', label: '轿车' }, +]; + +/** 品牌(从型号参数表拉取) */ +export const brandOptions = [ + { value: 'brand_a', label: '品牌A' }, + { value: 'brand_b', label: '品牌B' }, + { value: 'brand_c', label: '品牌C' }, +]; + +/** 型号(从型号参数表拉取,可按品牌联动) */ +export const modelOptions: Record = { + brand_a: [ + { value: 'model_a1', label: '型号A1' }, + { value: 'model_a2', label: '型号A2' }, + ], + brand_b: [ + { value: 'model_b1', label: '型号B1' }, + { value: 'model_b2', label: '型号B2' }, + ], + brand_c: [ + { value: 'model_c1', label: '型号C1' }, + ], +}; + +/** 客户名称(租赁/自营合同客户,含「无」) */ +export const customerOptions = [ + { value: '__none__', label: '无' }, + { value: 'customer_1', label: '客户甲' }, + { value: 'customer_2', label: '客户乙' }, + { value: 'customer_3', label: '客户丙' }, +]; + +/** 归属业务部门(含「无」) */ +export const businessDeptOptions = [ + { value: '__none__', label: '无' }, + { value: 'dept_1', label: '业务一部' }, + { value: 'dept_2', label: '业务二部' }, + { value: 'dept_3', label: '业务三部' }, +]; + +/** 合同编号列表(支持模糊匹配,此处为示例) */ +export const contractNoOptions = [ + { value: 'HT-2024-001', label: 'HT-2024-001' }, + { value: 'HT-2024-002', label: 'HT-2024-002' }, + { value: 'HT-2024-003', label: 'HT-2024-003' }, + { value: 'ZY-2024-001', label: 'ZY-2024-001' }, +]; + +/** 登记所有权列表(支持模糊匹配) */ +export const registeredOwnerOptions = [ + { value: 'owner_1', label: '某某物流有限公司' }, + { value: 'owner_2', label: '某某租赁有限公司' }, +]; + +function randomItem(arr: T[]): T { + return arr[Math.floor(Math.random() * arr.length)]; +} + +/** 生成模拟列表数据 */ +export function mockVehicleList(params: VehicleFilterForm & { plateNo?: string; page?: number; pageSize?: number }): { list: VehicleRecord[]; total: number } { + const total = 48; + const list: VehicleRecord[] = []; + const cities = ['广东省广州市', '广东省深圳市', '浙江省杭州市', '江苏省南京市']; + for (let i = 1; i <= (params.pageSize ?? 20); i++) { + const idx = ((params.page ?? 1) - 1) * (params.pageSize ?? 20) + i; + if (idx > total) break; + list.push({ + id: `v-${idx}`, + operationCity: randomItem(cities), + vin: `L${String(idx).padStart(6, '0')}${Math.random().toString(36).slice(2, 11).toUpperCase()}`, + plateNo: `粤A${String(10000 + idx).slice(-5)}`, + vehicleNo: idx <= 10 ? `VN-${idx}` : '', + vehicleType: randomItem(vehicleTypeOptions).label, + brand: randomItem(brandOptions).label, + model: randomItem(modelOptions.brand_a).label, + bodyColor: ['白', '黑', '银', '蓝'][idx % 4], + parkingLot: idx % 3 === 0 ? '-' : `停车场${(idx % 3) + 1}`, + customerName: idx % 4 === 0 ? '-' : `客户${(idx % 4)}`, + businessDept: idx % 4 === 0 ? '-' : `业务${(idx % 4)}部`, + businessOwner: idx % 4 === 0 ? '-' : `负责人${idx % 5}`, + operationStatus: randomItem(['待运营', '库存', '租赁', '自营', '退出运营']), + libStatus: randomItem(['库存车-可交付车', '已交付车-租赁交车', '新车入库-待验车']), + outboundStatus: randomItem(['无', '租赁交车', '异动出库']), + preemptStatus: '-', + prepStatus: randomItem(['待整备', '整备中', '正常', '无']), + transferStatus: randomItem(['无', '过户中', '内部过户完成']), + repairStatus: randomItem(['待服务站接单', '维修中', '正常']), + licenseStatus: randomItem(['正常', '异常']), + scrapStatus: randomItem(['无', '报废中', '已报废']), + registeredOwner: '某某物流有限公司', + onlineStatus: randomItem(['在线', '离线']), + manufactureYear: `${2020 + (idx % 5)}`, + mileage: Number((10000 + idx * 500 + Math.random() * 200).toFixed(2)), + purchaseTime: `2022-0${(idx % 9) + 1}-15`, + licenseRegisterDate: `2022-0${(idx % 9) + 1}-01`, + licenseExpiry: `2025-0${(idx % 9) + 1}-01`, + lastDeliveryTime: idx % 2 === 0 ? `2024-01-${String(10 + (idx % 20)).padStart(2, '0')}` : '-', + lastDeliveryMileage: idx % 2 === 0 ? Number((12000 + idx * 100).toFixed(2)) : 0, + lastReturnTime: idx % 2 === 1 ? `2024-02-${String(5 + (idx % 20)).padStart(2, '0')}` : '-', + lastReturnMileage: idx % 2 === 1 ? Number((12500 + idx * 100).toFixed(2)) : 0, + forceScrapDate: `2030-12-31`, + contractNo: idx % 3 === 0 ? '-' : `HT-2024-00${(idx % 3) + 1}`, + currentLocation: `广东省广州市天河区某某路${idx}号`, + gpsLastTime: `2024-02-13 ${String(10 + (idx % 12)).padStart(2, '0')}:${String(idx % 60).padStart(2, '0')}`, + }); + } + return { list, total }; +} diff --git a/src/types/vehicle.ts b/src/types/vehicle.ts new file mode 100644 index 0000000..34b9cd7 --- /dev/null +++ b/src/types/vehicle.ts @@ -0,0 +1,129 @@ +/** 车辆管理相关类型 */ + +export type OperationCity = { province: string; city: string }; +export type VehicleTypeItem = { value: string; label: string }; +export type ModelParamItem = { brand: string; model: string }; +export type CustomerItem = { value: string; label: string }; +export type DeptItem = { value: string; label: string }; + +export const OPERATION_STATUS_OPTIONS = [ + { value: 'pending', label: '待运营' }, + { value: 'in_stock', label: '库存' }, + { value: 'lease', label: '租赁' }, + { value: 'self', label: '自营' }, + { value: 'exit', label: '退出运营' }, +] as const; + +export const LIB_STATUS_OPTIONS = [ + { value: 'new_pending', label: '新车入库-待验车' }, + { value: 'new_license', label: '新车入库-证照办理' }, + { value: 'stock_ok', label: '库存车-可交付车' }, + { value: 'stock_no', label: '库存车-不可交付车' }, + { value: 'stock_slow', label: '库存车-呆滞车' }, + { value: 'out_lease', label: '已交付车-租赁交车' }, + { value: 'out_self', label: '已交付车-自营交车' }, + { value: 'out_replace', label: '已交付车-替换交车' }, + { value: 'exit_scrap', label: '退出运营-报废车' }, + { value: 'exit_third', label: '退出运营-三方退租车' }, + { value: 'exit_sale', label: '退出运营-过户售车' }, +] as const; + +export const OUTBOUND_STATUS_OPTIONS = [ + { value: 'move', label: '异动出库' }, + { value: 'transfer', label: '调拨出库' }, + { value: 'show', label: '展示出库' }, + { value: 'lease_deliver', label: '租赁交车' }, + { value: 'self_deliver', label: '自营交车' }, + { value: 'replace_deliver', label: '替换交车' }, + { value: 'sale', label: '过户售车' }, + { value: 'lease_return', label: '外租退车' }, + { value: 'scrap', label: '报废出库' }, + { value: 'none', label: '无' }, +] as const; + +export const PREP_STATUS_OPTIONS = [ + { value: 'pending', label: '待整备' }, + { value: 'doing', label: '整备中' }, + { value: 'normal', label: '正常' }, + { value: 'none', label: '无' }, +] as const; + +export const TRANSFER_STATUS_OPTIONS = [ + { value: 'doing', label: '过户中' }, + { value: 'internal_done', label: '内部过户完成' }, + { value: 'sale_done', label: '销售过户完成' }, + { value: 'none', label: '无' }, +] as const; + +export const REPAIR_STATUS_OPTIONS = [ + { value: 'pending', label: '待服务站接单' }, + { value: 'doing', label: '维修中' }, + { value: 'normal', label: '正常' }, +] as const; + +export const LICENSE_STATUS_OPTIONS = [ + { value: 'normal', label: '正常' }, + { value: 'abnormal', label: '异常' }, +] as const; + +export const SCRAP_STATUS_OPTIONS = [ + { value: 'doing', label: '报废中' }, + { value: 'done', label: '已报废' }, + { value: 'none', label: '无' }, +] as const; + +export const ONLINE_STATUS_OPTIONS = [ + { value: 'online', label: '在线' }, + { value: 'offline', label: '离线' }, +] as const; + +export interface VehicleRecord { + id: string; + operationCity: string; + vin: string; + plateNo: string; + vehicleNo: string; + vehicleType: string; + brand: string; + model: string; + bodyColor: string; + parkingLot: string; + customerName: string; + businessDept: string; + businessOwner: string; + operationStatus: string; + libStatus: string; + outboundStatus: string; + preemptStatus: string; + prepStatus: string; + transferStatus: string; + repairStatus: string; + licenseStatus: string; + scrapStatus: string; + registeredOwner: string; + onlineStatus: string; + manufactureYear: string; + mileage: number; + purchaseTime: string; + licenseRegisterDate: string; + licenseExpiry: string; + lastDeliveryTime: string; + lastDeliveryMileage: number; + lastReturnTime: string; + lastReturnMileage: number; + forceScrapDate: string; + contractNo: string; + currentLocation: string; + gpsLastTime: string; +} + +export interface VehicleFilterForm { + operationCity?: string[]; + vehicleType?: string; + brand?: string; + model?: string; + customerName?: string; + businessDept?: string; + contractNo?: string; + registeredOwner?: string; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4ffafe1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { "@/*": ["src/*"] } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..b70c402 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { '@': path.resolve(__dirname, 'src') }, + }, +}); diff --git a/web端.zip b/web端.zip new file mode 100644 index 0000000000000000000000000000000000000000..2ee407a05506bc40f1a6ccad5eb1dd1cef8276a3 GIT binary patch literal 191082 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�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 Qzh1ysiL*1n=Rv^#0psrmumAu6 literal 0 HcmV?d00001 diff --git a/web端/.DS_Store b/web端/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..20ff8d656e81013887f1e6a2439ec9f5e1dce93b GIT binary patch literal 6148 zcmeHK-A)rh6rKYWw*1HfML?q2*c%f_q)1GRF)Rh6#2An^SOQpg+iBgf?9|;Y$Y0Vc z6HN^90ek?Tzzc7@^a+eNhDXp_?>sZJ3zq7wF=kFO^PM?!&Y7KWvvYO`A&{+?Glb}b z5Dgp0L>G3y5kAjbi?l_Sc0}Mg@>;&f-U({9%j_0!Wl#(#2L3e$__y0}R5^OFd;EI# z%=i1=uDMPSPELM?w)Vuij!vyp>((}MPuWf`w8DCjwfr?c$}{HLv2NKb4&5p0sX69_ zmg7-h1UO}iF6)mRugrF`tnQTq5u1|}T2f0E_2K>f%=CnjnaUha82dM;CMS&P>o*P$ zliE=F+O6f{rn}{_my$!kBS%PYgfYBArD;n}X*)WS;7h!vV_lComF(*7={?`4_YVyA zr}~G6E)0*1UK|^{RMZD;tF&45`NPgJm-$(@PS>iwQ_j__lH*kiA$=a^oIP692hO-I z)AHj}Q+~IGKB-fKNO#w#)D!s@E0utTgY2Z^@@TQ^7%5m6XVwCroN0Z64DTlm@jFVaNfUJ;BvPa&K zx8xo9Kt7SLv65Hq5|1cnHf-0vDcv4}nbI7Qge$C(8*14iK8&`F76t zT;^5L9>+9?s8LLe)~&cu3@8Q^17{fE2d9CJqZwgCqBv8r6ITGl1e%4QE(!yp!3~IJ zgbj&kK>-= 0 + ? r.planDeliveryDisplay.split('至')[1] + : r.planDeliveryDisplay; + var d = planEnd ? window.dayjs(planEnd) : null; + return d && !d.isBefore(start) && !d.isAfter(end); + }); + } + return list; + }, [applied.contractCode, applied.projectName, applied.customerName, applied.creator, applied.planDateRange]); + + var handleQuery = function() { + setApplied({ + contractCode: _contractCode[0], + projectName: _projectName[0], + customerName: _customerName[0], + planDateRange: _planDateRange[0] && _planDateRange[0].length === 2 ? _planDateRange[0] : null, + creator: _creator[0] + }); + message.info('已按筛选条件查询'); + }; + + var handleReset = function() { + _contractCode[1](undefined); + _projectName[1](undefined); + _customerName[1](undefined); + _planDateRange[1](null); + _creator[1](undefined); + setApplied({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + planDateRange: null, + creator: undefined + }); + }; + + var handleDisableEnable = function(record) { + if (record.enabled) { + Modal.confirm({ + title: '是否确认停用该交车任务?', + okText: '确定', + cancelText: '取消', + onOk: function() { + setOngoingList(function(prev) { + return prev.map(function(r) { return r.id === record.id ? Object.assign({}, r, { enabled: false }) : r; }); + }); + message.success('已停用'); + } + }); + return; + } + // 启用:判断预计交车结束日期是否已过期 + var dayjs = window.dayjs; + var endDate = record.planDeliveryEnd ? (dayjs ? dayjs(record.planDeliveryEnd) : null) : null; + var now = dayjs ? dayjs() : null; + if (endDate && now && endDate.isBefore(now, 'day')) { + _rescheduleTask[1](record); + _rescheduleDateRange[1](null); + _rescheduleModalVisible[1](true); + return; + } + Modal.confirm({ + title: '是否确认启用该交车任务?', + okText: '确定', + cancelText: '取消', + onOk: function() { + setOngoingList(function(prev) { + return prev.map(function(r) { return r.id === record.id ? Object.assign({}, r, { enabled: true }) : r; }); + }); + message.success('已启用'); + } + }); + }; + + var handleRescheduleConfirm = function() { + var task = _rescheduleTask[0]; + var range = _rescheduleDateRange[0]; + if (!task || !range || range.length !== 2) { + message.warning('请选择预计交车日期(单日请选择同一天为开始和结束)'); + return; + } + var dayjs = window.dayjs; + var startStr = dayjs ? dayjs(range[0]).format('YYYY-MM-DD') : String(range[0]); + var endStr = dayjs ? dayjs(range[1]).format('YYYY-MM-DD') : String(range[1]); + var planDisplay = startStr === endStr ? startStr : startStr + '至' + endStr; + setOngoingList(function(prev) { + return prev.map(function(r) { + if (r.id !== task.id) return r; + return Object.assign({}, r, { planDeliveryDisplay: planDisplay, planDeliveryEnd: endStr, enabled: true }); + }); + }); + message.success('已重新选择预计交车日期并启用'); + _rescheduleModalVisible[1](false); + _rescheduleTask[1](null); + _rescheduleDateRange[1](null); + }; + + // 进行中表格列 + var ongoingColumns = [ + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 180, ellipsis: true, fixed: 'left' }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true, fixed: 'left' }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true, fixed: 'left' }, + { title: '交车数量', dataIndex: 'deliveryCount', key: 'deliveryCount', width: 100 }, + { title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 140 }, + { title: '交车地点', dataIndex: 'deliveryLocation', key: 'deliveryLocation', width: 200, ellipsis: true }, + { title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 180 }, + { title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120 }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 100 }, + { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 120 }, + { title: '最后更新人', dataIndex: 'lastUpdater', key: 'lastUpdater', width: 100 }, + { title: '最后更新时间', dataIndex: 'lastUpdatedAt', key: 'lastUpdatedAt', width: 120 }, + { + title: '操作', + key: 'action', + width: 180, + fixed: 'right', + render: function(_, record) { + return React.createElement('span', null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务页面'); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务-编辑交车任务页面'); } }, '编辑'), + React.createElement(Button, { + type: 'link', + size: 'small', + onClick: function() { handleDisableEnable(record); } + }, record.enabled ? '停用' : '启用') + ); + } + } + ]; + + // 已完成表格列(交车数量重点色+气泡) + var vehiclePopoverContent = function(vehicles) { + if (!vehicles || !vehicles.length) return React.createElement('span', null, '-'); + var headers = ['车辆类型', '品牌', '型号', '车牌号', '实际交车日期', '交车人']; + return React.createElement('div', { style: { padding: 4, minWidth: 420 } }, + React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } }, + React.createElement('thead', null, + React.createElement('tr', null, + headers.map(function(h) { return React.createElement('th', { key: h, style: { padding: '6px 8px', textAlign: 'left', borderBottom: '1px solid #eee', fontWeight: 600 } }, h); }) + ) + ), + React.createElement('tbody', null, + vehicles.map(function(v, i) { + return React.createElement('tr', { key: i }, + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.vehicleType || '-'), + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.brand || '-'), + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.model || '-'), + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.plateNo || '-'), + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.actualDeliveryDate || '-'), + React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.deliverer || '-') + ); + }) + ) + ) + ); + }; + + var completedColumns = [ + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 180, ellipsis: true, fixed: 'left' }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true, fixed: 'left' }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true, fixed: 'left' }, + { + title: '交车数量', + dataIndex: 'deliveryCount', + key: 'deliveryCount', + width: 100, + render: function(count, record) { + var content = vehiclePopoverContent(record.vehicles); + return React.createElement(Popover, { content: content, title: '交车车辆明细', trigger: 'click' }, + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 500 } }, count) + ); + } + }, + { title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 140 }, + { title: '交车地点', dataIndex: 'deliveryLocation', key: 'deliveryLocation', width: 200, ellipsis: true }, + { title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 180 }, + { title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120 }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 100 }, + { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 120 }, + { title: '最后更新人', dataIndex: 'lastUpdater', key: 'lastUpdater', width: 100 }, + { title: '最后更新时间', dataIndex: 'lastUpdatedAt', key: 'lastUpdatedAt', width: 120 }, + { + title: '操作', + key: 'action', + width: 80, + fixed: 'right', + render: function(_, record) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务页面'); } }, '查看'); + } + } + ]; + + var filterOption = function(input, option) { + var label = (option && option.label) ? String(option.label) : ''; + return label.toLowerCase().indexOf((input || '').toLowerCase()) >= 0; + }; + + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + + var filterItems = [ + React.createElement('div', { key: 'contractCode', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同编码'), + React.createElement(Select, { + placeholder: '请输入或选择合同编码', + style: filterControlStyle, + value: _contractCode[0], + onChange: function(v) { _contractCode[1](v); }, + options: contractOptions, + showSearch: true, + allowClear: true, + filterOption: filterOption + })), + React.createElement('div', { key: 'projectName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '项目名称'), + React.createElement(Select, { + placeholder: '请输入或选择项目名称', + style: filterControlStyle, + value: _projectName[0], + onChange: function(v) { _projectName[1](v); }, + options: projectOptions, + showSearch: true, + allowClear: true, + filterOption: filterOption + })), + React.createElement('div', { key: 'customerName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请输入或选择客户名称', + style: filterControlStyle, + value: _customerName[0], + onChange: function(v) { _customerName[1](v); }, + options: customerOptions, + showSearch: true, + allowClear: true, + filterOption: filterOption + })), + React.createElement('div', { key: 'planDate', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '预计交车日期'), + React.createElement(RangePicker, { + style: filterControlStyle, + placeholder: ['请选择预计交车开始时间', '请选择预计交车结束时间'], + value: _planDateRange[0] && _planDateRange[0].length === 2 && window.dayjs ? [_planDateRange[0][0], _planDateRange[0][1]] : null, + onChange: function(dates) { _planDateRange[1](dates && dates.length === 2 ? dates : null); } + })), + React.createElement('div', { key: 'creator', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '创建人'), + React.createElement(Select, { + placeholder: '请输入或选择创建人', + style: filterControlStyle, + value: _creator[0], + onChange: function(v) { _creator[1](v); }, + options: creatorOptions, + showSearch: true, + allowClear: true, + filterOption: filterOption + })) + ]; + + var filterCount = _filterExpanded[0] ? 5 : 3; + var filterNodes = []; + for (var i = 0; i < filterCount && i < filterItems.length; i++) { + filterNodes.push(filterItems[i]); + } + + // 表格单元格一行显示、根据内容适应宽度 + var cellNoWrap = function() { return { style: { whiteSpace: 'nowrap' } }; }; + var ongoingColumnsWithNowrap = ongoingColumns.map(function(col) { return Object.assign({}, col, { onCell: cellNoWrap }); }); + var completedColumnsWithNowrap = completedColumns.map(function(col) { return Object.assign({}, col, { onCell: cellNoWrap }); }); + + var layoutStyle = { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh' }; + + var reqTitleStyle = { fontSize: 18, fontWeight: 600, marginBottom: 16, color: 'rgba(0,0,0,0.85)' }; + var reqSectionStyle = { fontSize: 15, fontWeight: 600, marginTop: 16, marginBottom: 8, color: 'rgba(0,0,0,0.85)' }; + var reqSubStyle = { fontSize: 14, fontWeight: 500, marginLeft: 16, marginTop: 8, marginBottom: 4, color: 'rgba(0,0,0,0.85)' }; + var reqItemStyle = { fontSize: 13, marginLeft: 32, marginTop: 4, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.75)' }; + var reqSubItemStyle = { fontSize: 13, marginLeft: 48, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.7)' }; + var reqBlockStyle = { marginBottom: 8 }; + + var reqSpecContent = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqTitleStyle }, '交车任务'), + React.createElement('div', { style: reqBlockStyle }, + React.createElement('div', { style: reqSectionStyle }, '1.面包屑:'), + React.createElement('div', { style: reqSubStyle }, '1.1.业务管理-交车任务') + ), + React.createElement('div', { style: reqBlockStyle }, + React.createElement('div', { style: reqSectionStyle }, '2.筛选:'), + React.createElement('div', { style: reqItemStyle }, '支持通过合同编码、项目名称、客户名称、预计交车日期、创建人进行筛选,右侧为查询、重置按钮;'), + React.createElement('div', { style: reqItemStyle }, '2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;'), + React.createElement('div', { style: reqItemStyle }, '2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;'), + React.createElement('div', { style: reqItemStyle }, '2.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;'), + React.createElement('div', { style: reqItemStyle }, '2.4.预计交车日期:日期选择器,默认提示信息为:请选择预计交车开始时间 请选择预计交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '2.5.创建人:选择器,默认为所有创建人;提示信息为:请输入或选择创建人,支持从输入框输入内容进行模糊搜索,下拉显示结果;'), + React.createElement('div', { style: reqItemStyle }, '2.6.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;'), + React.createElement('div', { style: reqItemStyle }, '2.7.重置:点击清空查询条件至默认') + ), + React.createElement('div', { style: reqBlockStyle }, + React.createElement('div', { style: reqSectionStyle }, '3.列表:分为2个tab:进行中、已完成,右侧为新增;'), + React.createElement('div', { style: reqSubStyle }, '3.1进行中:显示所有已新增成功,但未完成交车单提交的任务;列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作;'), + React.createElement('div', { style: reqItemStyle }, '3.1.1合同编码:显示对应合同编码;'), + React.createElement('div', { style: reqItemStyle }, '3.1.2.项目名称:显示对应项目名称;'), + React.createElement('div', { style: reqItemStyle }, '3.1.3.客户名称:显示对应客户名称;'), + React.createElement('div', { style: reqItemStyle }, '3.1.4.交车数量:显示交车数;'), + React.createElement('div', { style: reqItemStyle }, '3.1.5.交车区域:显示交车省-市;'), + React.createElement('div', { style: reqItemStyle }, '3.1.6.交车地点:显示交车详细地址;'), + React.createElement('div', { style: reqItemStyle }, '3.1.7.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '3.1.8.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效;'), + React.createElement('div', { style: reqItemStyle }, '3.1.9.创建人:显示交车任务的创建人用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '3.1.10.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD;'), + 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: reqSubItemStyle }, '3.1.13.1.查看:跳转查看交车任务;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.13.2.编辑:跳转编辑交车任务;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.13.3.停用/启用:任务创建后为启用状态,此处为停用,点击停用后,变为启用。停用状态的交车任务不会推送给对应区域运维人员,重新启用时判断预计交车结束日期是否已经过期,如已过期则启用时需要在弹出卡片中重新选择预计交车日期;'), + React.createElement('div', { style: reqSubStyle }, '3.2.已完成:显示所有已新增成功并完成交车单提交的任务,列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作;'), + React.createElement('div', { style: reqItemStyle }, '3.2.1.合同编码:显示对应合同编码;'), + React.createElement('div', { style: reqItemStyle }, '3.2.2.项目名称:显示对应项目名称;'), + React.createElement('div', { style: reqItemStyle }, '3.2.3.客户名称:显示对应客户名称;'), + React.createElement('div', { style: reqItemStyle }, '3.2.4.交车数量:显示交车数,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、实际交车日期、交车人;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.1.车辆类型:显示车辆类型;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.2.品牌:显示车辆品牌;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.3.型号:显示车辆型号;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.5.实际交车日期:显示实际交车日期,与列表中显示相同,格式为YYYY-MM-DD,如该车还未交车则显示为-;'), + React.createElement('div', { style: reqSubItemStyle }, '3.2.4.6.交车人:显示实际交车人用户姓名,如该车还未还车则显示为-;'), + React.createElement('div', { style: reqItemStyle }, '3.2.5.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '3.2.6.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效;'), + React.createElement('div', { style: reqItemStyle }, '3.2.7.创建人:显示交车任务的创建人用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '3.2.8.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '3.2.9.最后更新人:显示交车任务的最后一次更新人用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '3.2.10.最后更新时间:显示交车任务的最后一次更新时间,格式为YYYY-MM-DD;'), + React.createElement('div', { style: reqItemStyle }, '3.2.11.操作:支持查看;') + ), + React.createElement('div', { style: reqBlockStyle }, + React.createElement('div', { style: reqSectionStyle }, '4.列表右下方为分页功能,支持单页显示条目选择;') + ) + ); + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '业务管理' }, + { title: '交车任务' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { _reqSpecOpen[1](true); } }, '查看需求说明') + ), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + flex: 1, + minWidth: 0 + } + }, filterNodes), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开') + ) + ), + React.createElement(Card, null, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, + React.createElement(Tabs, { + activeKey: _activeTab[0], + onChange: function(k) { _activeTab[1](k); }, + items: [ + { key: 'ongoing', label: '进行中' }, + { key: 'completed', label: '已完成' } + ] + }), + React.createElement(Button, { type: 'primary', onClick: function() { message.info('请参照原型业务管理-交车任务-新增交车任务页面'); } }, '新增') + ), + _activeTab[0] === 'ongoing' + ? React.createElement(Table, { + rowKey: 'id', + columns: ongoingColumnsWithNowrap, + dataSource: filteredOngoing, + scroll: { x: 1860 }, + size: 'small', + pagination: { + showSizeChanger: true, + showTotal: function(t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50', '100'] + } + }) + : React.createElement(Table, { + rowKey: 'id', + columns: completedColumnsWithNowrap, + dataSource: filteredCompleted, + scroll: { x: 1860 }, + size: 'small', + pagination: { + showSizeChanger: true, + showTotal: function(t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50', '100'] + } + }) + ), + React.createElement(Modal, { + title: '需求说明', + open: _reqSpecOpen[0], + onCancel: function() { _reqSpecOpen[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function() { _reqSpecOpen[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, reqSpecContent), + React.createElement(Modal, { + title: '预计交车日期已过期,请重新选择预计交车日期', + open: _rescheduleModalVisible[0], + onCancel: function() { + _rescheduleModalVisible[1](false); + _rescheduleTask[1](null); + _rescheduleDateRange[1](null); + }, + onOk: handleRescheduleConfirm, + okText: '确定', + cancelText: '取消' + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: { marginBottom: 8, color: '#666', fontSize: 13 } }, '支持选择单日或开始-结束时间段,单日时开始与结束选同一天即可。'), + React.createElement('span', { style: { marginRight: 8 } }, '预计交车日期:'), + React.createElement(RangePicker, { + placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'], + value: _rescheduleDateRange[0] && _rescheduleDateRange[0].length === 2 && window.dayjs ? [_rescheduleDateRange[0][0], _rescheduleDateRange[0][1]] : null, + onChange: function(dates) { _rescheduleDateRange[1](dates && dates.length === 2 ? dates : null); } + }) + )) + ) + ); +}; diff --git a/web端/业务管理/新增交车任务.jsx b/web端/业务管理/新增交车任务.jsx new file mode 100644 index 0000000..17cc43f --- /dev/null +++ b/web端/业务管理/新增交车任务.jsx @@ -0,0 +1,300 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 数字化资产ONEOS运管平台 - 新增交车任务模块(参照新增租赁合同布局) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var message = antd.message; + var Option = Select.Option; + var Checkbox = antd.Checkbox; + var Tooltip = antd.Tooltip; + var RangePicker = DatePicker.RangePicker; + + var projectId = React.useState(''); + var selectedProjectId = projectId[0]; + var setSelectedProjectId = projectId[1]; + + var expectedDelivery = React.useState(null); + var expectedDeliveryValue = expectedDelivery[0]; + var setExpectedDelivery = expectedDelivery[1]; + + var billingDate = React.useState(null); + var billingDateValue = billingDate[0]; + var setBillingDate = billingDate[1]; + + var selectedRowKeys = React.useState([]); + var checkedRowKeys = selectedRowKeys[0]; + var setCheckedRowKeys = selectedRowKeys[1]; + + var formErrors = React.useState({}); + var errors = formErrors[0]; + var setErrors = formErrors[1]; + + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + + // Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示 + var projectList = [ + { id: 'p1', name: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [ + { key: 'v1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliveryStatus: 'submitted' }, + { key: 'v2', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10002', vin: 'LFV2BJCH8K3123457', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v3', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌', deliveryStatus: 'none' }, + { key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' }, + { key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' } + ]}, + { id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [ + { key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' }, + { key: 'v8', brand: '福田', model: '欧辉氢能大巴', plateNo: '', vin: 'LZYTBACR2M2345678', monthRent: '19800', serviceFee: '1100', deposit: '45000', remark: '', deliveryStatus: 'none' } + ]}, + { id: 'p3', name: '杭州某某租赁氢能项目', contractCode: 'HZZL20260115YW100089A', customerName: '杭州某某租赁有限公司', deliveryRegion: '浙江省 / 杭州市', deliveryLocation: '浙江省杭州市余杭区未来科技城', vehicles: [ + { key: 'v9', brand: '品牌C', model: '型号C1', plateNo: '浙A40004', vin: 'L4234567890ABCDEF', monthRent: '8200', serviceFee: '450', deposit: '20000', remark: '重点客户', deliveryStatus: 'completed' }, + { key: 'v10', brand: '品牌C', model: '型号C2', plateNo: '', vin: 'L5234567890ABCDEF', monthRent: '7800', serviceFee: '420', deposit: '19000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v11', brand: '东风', model: '氢燃料电池厢货', plateNo: '浙A50001', vin: 'LFV2BJCH8K5678901', monthRent: '9200', serviceFee: '520', deposit: '22000', remark: '城配', deliveryStatus: 'none' }, + { key: 'v12', brand: '开沃', model: '创源氢能轻卡', plateNo: '浙A50002', vin: 'LJXTBACR9N6789012', monthRent: '8800', serviceFee: '480', deposit: '21000', remark: '', deliveryStatus: 'none' } + ]} + ]; + + var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; }); + var vehicleListRaw = selectedProject ? selectedProject.vehicles : []; + var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; }); + var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; }); + + var handleProjectChange = function(id) { + setSelectedProjectId(id || ''); + setCheckedRowKeys([]); + }; + + var todayStr = (function() { + var d = new Date(); + return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); + })(); + + var validateExpectedDelivery = function() { + if (!expectedDeliveryValue || !expectedDeliveryValue.length) return '请选择预计交车日期'; + var start = expectedDeliveryValue[0] && expectedDeliveryValue[0].format ? expectedDeliveryValue[0].format('YYYY-MM-DD') : null; + var end = expectedDeliveryValue[1] && expectedDeliveryValue[1].format ? expectedDeliveryValue[1].format('YYYY-MM-DD') : null; + if (!start) return '请选择预计交车日期'; + if (end && end < start) return '结束日期不能早于开始日期'; // 结束可与开始相同,即单日 + if (end && end < todayStr) return '结束日期不能早于当前日期'; + return null; + }; + + var expectedDeliveryError = validateExpectedDelivery(); + + var billingDateError = !billingDateValue ? '请选择开始计费日期' : null; + + var handleSubmit = function() { + var err = {}; + if (!selectedProjectId) err.projectName = '请选择项目名称'; + if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; + if (billingDateError) err.billingDate = billingDateError; + if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车'; + setErrors(err); + if (Object.keys(err).length) return; + var count = checkedRowKeys.length; + message.success('已为 ' + count + ' 辆车生成交车任务,将按交车区域分配对应运维人员。'); + }; + + var handleCancel = function() { + message.info('取消'); + }; + + var onSelectAll = function(checked) { + if (checked) setCheckedRowKeys(selectableVehicles.map(function(v) { return v.key; })); + else setCheckedRowKeys([]); + }; + + var onSelectRow = function(record, checked) { + if (checked) setCheckedRowKeys(function(prev) { return prev.indexOf(record.key) === -1 ? prev.concat(record.key) : prev; }); + else setCheckedRowKeys(function(prev) { return prev.filter(function(k) { return k !== record.key; }); }); + }; + + 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' }, + cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 }, + 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' }, + inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 } + }; + + var reqSpecBlock = { marginBottom: 16 }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 }; + var reqSpecP = { fontSize: 13, color: '#666', marginBottom: 4 }; + var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 }; + + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-新增交车任务')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'), + React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:必选项,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;'), + React.createElement('div', { style: reqSpecLi }, '2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码;'), + React.createElement('div', { style: reqSpecLi }, '2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称;'), + React.createElement('div', { style: reqSpecLi }, '2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务;'), + React.createElement('div', { style: reqSpecLi }, '2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点;'), + React.createElement('div', { style: reqSpecLi }, '2.6.预计交车日期:必填项,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期;'), + React.createElement('div', { style: reqSpecLi }, '2.7.开始计费日期:必填项,日期选择器,支持单日选择,格式为YYYY-MM-DD;'), + React.createElement('div', { style: reqSpecLi }, '2.8.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注;'), + React.createElement('div', { style: reqSpecLi }, '2.8.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中;'), + React.createElement('div', { style: reqSpecLi }, '2.8.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌;'), + React.createElement('div', { style: reqSpecLi }, '2.8.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号;'), + React.createElement('div', { style: reqSpecLi }, '2.8.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-;'), + React.createElement('div', { style: reqSpecLi }, '2.8.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码;'), + React.createElement('div', { style: reqSpecLi }, '2.8.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元;'), + React.createElement('div', { style: reqSpecLi }, '2.8.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元;'), + React.createElement('div', { style: reqSpecLi }, '2.8.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元;'), + React.createElement('div', { style: reqSpecLi }, '2.8.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-;'), + React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;') + )); + + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + var FormItem = function(props) { + return React.createElement('div', { style: styles.formCol }, + React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null + ); + }; + + var projectOptions = projectList.map(function(p) { return React.createElement(Option, { key: p.id, value: p.id }, p.name); }); + + var formRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName }, + React.createElement(Select, { + placeholder: '请选择或输入项目名称', + style: { width: '100%' }, + value: selectedProjectId || undefined, + onChange: handleProjectChange, + showSearch: true, + allowClear: true, + filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, + status: errors.projectName ? 'error' : undefined + }, projectOptions)), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: selectedProject ? selectedProject.contractCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: selectedProject ? selectedProject.customerName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryRegion : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryLocation : '', disabled: true, style: { width: '100%' } })) + ); + + var formRow2 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, + React.createElement(RangePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'], + value: expectedDeliveryValue, + onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); }, + status: errors.expectedDelivery ? 'error' : undefined + })), + React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate }, + React.createElement(DatePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: '请选择日期', + value: billingDateValue, + onChange: function(d, dateStr) { setBillingDate(d); }, + status: errors.billingDate ? 'error' : undefined + })) + ); + + var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); + var someSelectableChecked = selectableVehicles.some(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); + var tableHeader = React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: Object.assign({}, styles.th, { width: 48 }) }, + React.createElement(Checkbox, { + checked: allSelectableChecked, + indeterminate: someSelectableChecked && !allSelectableChecked, + onChange: function(e) { onSelectAll(e.target.checked); } + })), + React.createElement('th', { style: styles.th }, '品牌'), + React.createElement('th', { style: styles.th }, '型号'), + React.createElement('th', { style: styles.th }, '车牌号'), + React.createElement('th', { style: styles.th }, '车辆识别代码'), + React.createElement('th', { style: styles.th }, '车辆月租金'), + React.createElement('th', { style: styles.th }, '服务费'), + React.createElement('th', { style: styles.th }, '保证金'), + React.createElement('th', { style: styles.th }, '备注') + ) + ); + + var 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) { + var isSubmitted = row.deliveryStatus === 'submitted'; + return React.createElement('tr', { key: row.key }, + React.createElement('td', { style: styles.td }, + isSubmitted + ? React.createElement(Tooltip, { title: '该车辆已有交车任务' }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } }, + React.createElement(Checkbox, { disabled: true, checked: false }), + React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交'))) + : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), + React.createElement('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 })) + ); + }) + ); + + var tableEl = React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, tableHeader, tableBody) + ); + + if (errors.vehicles) { + tableEl = React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.errMsg, { marginBottom: 8 }) }, errors.vehicles), + tableEl + ); + } + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, + React.createElement('span', null, '业务管理'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', null, '交车任务'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { style: { color: '#1890ff' } }, '新增交车任务')), + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), + React.createElement('div', { style: styles.cardBody }, + formRow1, + formRow2, + tableEl)), + React.createElement('div', { style: { height: 60 } }), + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleCancel }, '取消')) + ); +}; diff --git a/web端/业务管理/查看交车任务.jsx b/web端/业务管理/查看交车任务.jsx new file mode 100644 index 0000000..7dd98da --- /dev/null +++ b/web端/业务管理/查看交车任务.jsx @@ -0,0 +1,123 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 数字化资产ONEOS运管平台 - 查看交车任务模块(只读,布局参照新增交车任务) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Button = antd.Button; + + // Mock:当前查看的交车任务详情(从列表跳转带入或根据 id 拉取) + var task = { + projectName: '嘉兴某某物流氢能运输项目', + contractCode: 'JXZL20260216YW101235A', + customerName: '嘉兴某某物流有限公司', + deliveryRegion: '浙江省 / 嘉兴市', + deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', + planDeliveryDisplay: '2026-03-01至2026-03-05', + billingStartDate: '2026-03-06', + vehicles: [ + { brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车' }, + { brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌' }, + { brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '' } + ] + }; + + var vehicleList = task.vehicles || []; + + 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' }, + cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + 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' } + }; + + 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 }) + ); + }; + + var formRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '选择项目名称', value: task.projectName }), + React.createElement(FormItemReadOnly, { label: '合同编码', value: task.contractCode }), + React.createElement(FormItemReadOnly, { label: '客户名称', value: task.customerName }), + React.createElement(FormItemReadOnly, { label: '交车区域', value: task.deliveryRegion }), + React.createElement(FormItemReadOnly, { label: '交车地点', value: task.deliveryLocation }) + ); + + var formRow2 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '预计交车日期', value: task.planDeliveryDisplay }), + React.createElement(FormItemReadOnly, { label: '开始计费日期', value: task.billingStartDate }) + ); + + var tableHeader = React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.th }, '品牌'), + React.createElement('th', { style: styles.th }, '型号'), + React.createElement('th', { style: styles.th }, '车牌号'), + React.createElement('th', { style: styles.th }, '车辆识别代码'), + React.createElement('th', { style: styles.th }, '车辆月租金'), + React.createElement('th', { style: styles.th }, '服务费'), + React.createElement('th', { style: styles.th }, '保证金'), + React.createElement('th', { style: styles.th }, '备注') + ) + ); + + var tableBody = React.createElement('tbody', null, + vehicleList.length === 0 + ? React.createElement('tr', null, React.createElement('td', { colSpan: 8, 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 }, 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 })) + ); + }) + ); + + var tableEl = React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, tableHeader, tableBody) + ); + + var handleBack = function() { + if (window.history && window.history.back) window.history.back(); + else antd.message.info('返回'); + }; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, + React.createElement('span', null, '业务管理'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', null, '交车任务'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { style: { color: '#1890ff' } }, '查看交车任务'))), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), + React.createElement('div', { style: styles.cardBody }, + formRow1, + formRow2, + tableEl)), + React.createElement('div', { style: { height: 60 } }), + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { onClick: handleBack }, '返回')) + ); +}; diff --git a/web端/业务管理/租赁账单.jsx b/web端/业务管理/租赁账单.jsx new file mode 100644 index 0000000..40aeb83 --- /dev/null +++ b/web端/业务管理/租赁账单.jsx @@ -0,0 +1,933 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 账单管理 - 车辆资产管理后台(按 antd 规范) + +const Component = function() { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + var useEffect = React.useEffect; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Select = antd.Select; + var Button = antd.Button; + var Table = antd.Table; + var Tag = antd.Tag; + var Modal = antd.Modal; + var Input = antd.Input; + var message = antd.message; + var Space = antd.Space; + var Row = antd.Row; + var Col = antd.Col; + + // 当前视图:list | detail + var viewState = useState('list'); + var currentView = viewState[0]; + var setCurrentView = viewState[1]; + + // 分页 + var pageState = useState(1); + var currentPage = pageState[0]; + var setCurrentPage = pageState[1]; + var pageSizeState = useState(10); + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + // 选中项(Table rowSelection 用) + var selectedRowKeysState = useState([]); + var selectedRowKeys = selectedRowKeysState[0]; + var setSelectedRowKeys = selectedRowKeysState[1]; + + // 筛选器(表单值) + var contractFilterState = useState(undefined); + var contractFilter = contractFilterState[0]; + var setContractFilter = contractFilterState[1]; + var projectFilterState = useState(undefined); + var projectFilter = projectFilterState[0]; + var setProjectFilter = projectFilterState[1]; + var customerFilterState = useState(undefined); + var customerFilter = customerFilterState[0]; + var setCustomerFilter = customerFilterState[1]; + var statusFilterState = useState([]); + var statusFilter = statusFilterState[0]; + var setStatusFilter = statusFilterState[1]; + + // 已应用的筛选条件(点击查询后生效) + var appliedFilterState = useState({ contract: '', project: '', customer: '', status: [] }); + var appliedFilter = appliedFilterState[0]; + var setAppliedFilter = appliedFilterState[1]; + + // 弹窗:应付金额明细 | 服务费明细 + var popoverState = useState({ type: null, data: null }); + var popover = popoverState[0]; + var setPopover = popoverState[1]; + + // 照片查看器 + var photoViewerState = useState({ visible: false, photos: [], currentIndex: 0 }); + var photoViewer = photoViewerState[0]; + var setPhotoViewer = photoViewerState[1]; + + var viewingBillIdState = useState(null); + var viewingBillId = viewingBillIdState[0]; + var setViewingBillId = viewingBillIdState[1]; + + // 详情模式:view | payment + var detailModeState = useState('view'); + var detailMode = detailModeState[0]; + var setDetailMode = detailModeState[1]; + + // 付款表单数据 + var paymentFormState = useState({ + discountAmount: '', + discountReason: '', + vehicleList: [], + hydrogenList: [], + violationList: [], + returnFeeList: [] + }); + var paymentForm = paymentFormState[0]; + var setPaymentForm = paymentFormState[1]; + + // 列表内成本编辑 + var editingListCellState = useState(null); + var editingListCell = editingListCellState[0]; + var setEditingListCell = editingListCellState[1]; + var editingInputValueState = useState(''); + var editingInputValue = editingInputValueState[0]; + var setEditingInputValue = editingInputValueState[1]; + var listEditableCostsState = useState({}); + var listEditableCosts = listEditableCostsState[0]; + var setListEditableCosts = listEditableCostsState[1]; + + // 模拟选项与数据(与原文一致) + var contractOptions = useMemo(function() { + return [ + { value: 'HT001', label: 'HT001 - 租赁合同A' }, + { value: 'HT002', label: 'HT002 - 租赁合同B' }, + { value: 'HT003', label: 'HT003 - 租赁合同C' } + ]; + }, []); + var projectOptions = useMemo(function() { + return [ + { value: 'XM001', label: 'XM001 - 北京项目' }, + { value: 'XM002', label: 'XM002 - 上海项目' }, + { value: 'XM003', label: 'XM003 - 广州项目' } + ]; + }, []); + var customerOptions = useMemo(function() { + return [ + { value: 'KH001', label: 'KH001 - 科技公司A' }, + { value: 'KH002', label: 'KH002 - 制造企业B' }, + { value: 'KH003', label: 'KH003 - 物流公司C' } + ]; + }, []); + var statusOptions = [ + { value: 'paid', label: '已付款' }, + { value: 'partial', label: '部分付款' }, + { value: 'unpaid', label: '未付款' } + ]; + + var mockBillList = useMemo(function() { + var list = []; + for (var i = 1; i <= 25; i++) { + var statuses = ['paid', 'partial', 'unpaid']; + var status = statuses[i % 3]; + list.push({ + id: 'BILL' + i, + contractCode: 'HT00' + (i % 3 + 1), + projectName: 'XM00' + (i % 3 + 1) + ' - 项目' + i, + customerName: 'KH00' + (i % 3 + 1) + ' - 客户' + i, + period: i, + payableAmount: (15000 + i * 500).toFixed(2), + paidAmount: status === 'paid' ? (15000 + i * 500).toFixed(2) : status === 'partial' ? (8000).toFixed(2) : '0.00', + discountAmount: (i % 5 === 0 ? 200 : 0).toFixed(2), + remark: status !== 'unpaid' ? '已付款备注' + i : '-', + vehicleCost: (5000 + i * 100).toFixed(2), + hydrogenCost: (300 + i * 20).toFixed(2), + otherCost: (200 + i * 10).toFixed(2), + status: status + }); + } + return list; + }, []); + + var mockPayableDetail = useMemo(function() { + return [ + { startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京A12345', rent: '8000.00', serviceFee: '500.00', deposit: '2000.00' }, + { startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京B67890', rent: '7000.00', serviceFee: '400.00', deposit: '1500.00' } + ]; + }, []); + + var mockBillDetail = useMemo(function() { + return { + startDate: '2025-01-01', + endDate: '2025-01-31', + contractCode: 'HT001', + projectName: 'XM001 - 北京项目', + customerName: 'KH001 - 科技公司A', + department: '运营部', + responsible: '张三', + paymentCycle: '先付(付款周期:1个月)', + discountTotal: '200.00', + discountReason: '长期合作客户优惠,首期账单减免部分金额' + }; + }, []); + + var mockVehicleList = useMemo(function() { + return [ + { brand: '奔驰', model: 'E300L', plateNo: '京A12345', planDelivery: '2024-12-25', actualDelivery: '2024-12-28', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '8000.00', paidMonthlyRent: '7600.00', serviceFee: '500.00', paidServiceFee: '480.00', deposit: '2000.00', paidDeposit: '2000.00', serviceItems: [{ name: '保养服务', price: '300.00', effectiveDate: '2025-01-01' }, { name: '保险', price: '200.00', effectiveDate: '2025-01-01' }] }, + { brand: '宝马', model: '530Li', plateNo: '京B67890', planDelivery: '2024-12-20', actualDelivery: '2024-12-22', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '7000.00', paidMonthlyRent: '7000.00', serviceFee: '400.00', paidServiceFee: '380.00', deposit: '1500.00', paidDeposit: '1500.00', serviceItems: [{ name: '保养服务', price: '250.00', effectiveDate: '2025-01-01' }] } + ]; + }, []); + + var mockReturnFeeData = useMemo(function() { + return { + totalAmount: '2850.00', + paidTotalAmount: '2700.00', + list: [ + { feeName: '车辆外观损伤费', amount: '800.00', paidAmount: '760.00', photos: ['https://picsum.photos/80/80?random=1', 'https://picsum.photos/80/80?random=2'], attachments: [{ name: '外观损伤说明.pdf' }] }, + { feeName: '轮胎磨损费', amount: '1200.00', paidAmount: '1150.00', photos: ['https://picsum.photos/80/80?random=3'], attachments: [{ name: '轮胎检测报告.pdf' }, { name: '维修单据.pdf' }] }, + { feeName: '内饰清洁费', amount: '450.00', paidAmount: '430.00', photos: ['https://picsum.photos/80/80?random=4', 'https://picsum.photos/80/80?random=5', 'https://picsum.photos/80/80?random=6'], attachments: [] }, + { feeName: '油量补充费', amount: '400.00', paidAmount: '360.00', photos: [], attachments: [{ name: '加油凭证.jpg' }] } + ] + }; + }, []); + + var mockViolationData = useMemo(function() { + return { + violationCount: 3, + totalAmount: '650.00', + paidTotalAmount: '600.00', + list: [ + { violationTime: '2025-01-10 08:30:00', plateNo: '京A12345', violationType: '违停', location: '北京市朝阳区xxx路', fineAmount: '200.00', paidFineAmount: '200.00' }, + { violationTime: '2025-01-18 14:20:00', plateNo: '京B67890', violationType: '超速', location: '北京市海淀区xxx大道', fineAmount: '200.00', paidFineAmount: '180.00' }, + { violationTime: '2025-01-25 09:15:00', plateNo: '京A12345', violationType: '闯红灯', location: '北京市东城区xxx路口', fineAmount: '250.00', paidFineAmount: '220.00' } + ] + }; + }, []); + + var mockHydrogenData = useMemo(function() { + return { + refuelCount: 12, + balance: '3580.00', + list: [ + { refuelTime: '2025-01-15 09:30:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '15.5', costPrice: '28.00', feePrice: '434.00', paidFeePrice: '420.00' }, + { refuelTime: '2025-01-18 14:20:00', stationName: '北京海淀加氢站', plateNo: '京B67890', amount: '12.0', costPrice: '28.00', feePrice: '336.00', paidFeePrice: '336.00' }, + { refuelTime: '2025-01-22 11:00:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '18.2', costPrice: '28.00', feePrice: '509.60', paidFeePrice: '490.00' } + ] + }; + }, []); + + var vehicleBillTotals = useMemo(function() { + var list = mockVehicleList || []; + var monthlyRentTotal = 0, paidRentTotal = 0, serviceFeeTotal = 0, paidServiceFeeTotal = 0, depositTotal = 0, paidDepositTotal = 0; + list.forEach(function(v) { + monthlyRentTotal += parseFloat(v.monthlyRent || 0); + paidRentTotal += parseFloat(v.paidMonthlyRent || 0); + serviceFeeTotal += parseFloat(v.serviceFee || 0); + paidServiceFeeTotal += parseFloat(v.paidServiceFee || 0); + depositTotal += parseFloat(v.deposit || 0); + paidDepositTotal += parseFloat(v.paidDeposit || 0); + }); + return { + monthlyRentTotal: monthlyRentTotal.toFixed(2), + paidRentTotal: paidRentTotal.toFixed(2), + serviceFeeTotal: serviceFeeTotal.toFixed(2), + paidServiceFeeTotal: paidServiceFeeTotal.toFixed(2), + depositTotal: depositTotal.toFixed(2), + paidDepositTotal: paidDepositTotal.toFixed(2) + }; + }, [mockVehicleList]); + + var billInfoTotals = useMemo(function() { + var monthlyRentTotal = parseFloat(vehicleBillTotals.monthlyRentTotal || 0); + var serviceFeeTotal = parseFloat(vehicleBillTotals.serviceFeeTotal || 0); + var depositTotal = parseFloat(vehicleBillTotals.depositTotal || 0); + var hydrogenTotal = 0; + (mockHydrogenData.list || []).forEach(function(item) { hydrogenTotal += parseFloat(item.feePrice || 0); }); + var violationTotal = parseFloat(mockViolationData.totalAmount || 0); + var returnFeeTotal = parseFloat(mockReturnFeeData.totalAmount || 0); + var payableTotal = monthlyRentTotal + serviceFeeTotal + depositTotal + hydrogenTotal + violationTotal + returnFeeTotal; + var paidTotal = 0; + (mockVehicleList || []).forEach(function(v) { + paidTotal += parseFloat(v.paidMonthlyRent || 0) + parseFloat(v.paidServiceFee || 0) + parseFloat(v.paidDeposit || 0); + }); + var discountTotal = parseFloat(mockBillDetail.discountTotal || 0); + var unpaidTotal = Math.max(0, payableTotal - paidTotal - discountTotal); + return { + payableTotal: payableTotal.toFixed(2), + paidTotal: paidTotal.toFixed(2), + unpaidTotal: unpaidTotal.toFixed(2) + }; + }, [vehicleBillTotals, mockHydrogenData, mockViolationData, mockReturnFeeData, mockVehicleList, mockBillDetail]); + + var vehicleTotals = billInfoTotals; + + var filteredList = useMemo(function() { + var list = (mockBillList || []).slice(); + var c = (appliedFilter.contract || '').trim(); + var p = (appliedFilter.project || '').trim(); + var cust = (appliedFilter.customer || '').trim(); + var st = Array.isArray(appliedFilter.status) ? appliedFilter.status : []; + if (c) list = list.filter(function(item) { return String(item.contractCode || '').toLowerCase().indexOf(c.toLowerCase()) >= 0; }); + if (p) list = list.filter(function(item) { return String(item.projectName || '').toLowerCase().indexOf(p.toLowerCase()) >= 0; }); + if (cust) list = list.filter(function(item) { return String(item.customerName || '').toLowerCase().indexOf(cust.toLowerCase()) >= 0; }); + if (st.length > 0) { + var statusSet = {}; + st.forEach(function(s) { statusSet[s] = true; }); + list = list.filter(function(item) { return statusSet[item.status]; }); + } + return list; + }, [mockBillList, appliedFilter.contract, appliedFilter.project, appliedFilter.customer, appliedFilter.status]); + + var totalCount = filteredList.length; + var paginatedList = useMemo(function() { + var start = (currentPage - 1) * pageSize; + return filteredList.slice(start, start + pageSize); + }, [filteredList, currentPage, pageSize]); + + var handleSearch = useCallback(function() { + setAppliedFilter({ + contract: (contractFilter != null && contractFilter !== '') ? String(contractFilter) : '', + project: (projectFilter != null && projectFilter !== '') ? String(projectFilter) : '', + customer: (customerFilter != null && customerFilter !== '') ? String(customerFilter) : '', + status: Array.isArray(statusFilter) ? statusFilter.slice() : [] + }); + setCurrentPage(1); + }, [contractFilter, projectFilter, customerFilter, statusFilter]); + + var handleReset = useCallback(function() { + setContractFilter(undefined); + setProjectFilter(undefined); + setCustomerFilter(undefined); + setStatusFilter([]); + setAppliedFilter({ contract: '', project: '', customer: '', status: [] }); + setCurrentPage(1); + }, []); + + var handleExport = useCallback(function() { + if (selectedRowKeys.length === 0) { + message.warning('请先选择要导出的数据'); + return; + } + message.success('导出 ' + selectedRowKeys.length + ' 条数据'); + }, [selectedRowKeys.length]); + + var handleView = useCallback(function(id, isPayment) { + setViewingBillId(id); + setDetailMode(isPayment ? 'payment' : 'view'); + if (isPayment) { + var vl = mockVehicleList.map(function(v) { + return { paidMonthlyRent: v.paidMonthlyRent || '', paidServiceFee: v.paidServiceFee || '', paidDeposit: v.paidDeposit || '' }; + }); + var hl = mockHydrogenData.list.map(function(item) { return { paidFeePrice: item.paidFeePrice || '' }; }); + var viol = mockViolationData.list.map(function(item) { return { paidFineAmount: item.paidFineAmount || '' }; }); + var rfl = mockReturnFeeData.list.map(function(item) { return { paidAmount: item.paidAmount || '' }; }); + setPaymentForm({ + discountAmount: mockBillDetail.discountTotal || '', + discountReason: mockBillDetail.discountReason || '', + vehicleList: vl, + hydrogenList: hl, + violationList: viol, + returnFeeList: rfl + }); + } + setCurrentView('detail'); + }, []); + + var handleBackToList = useCallback(function() { + setCurrentView('list'); + setViewingBillId(null); + setDetailMode('view'); + }, []); + + var handlePaymentFormChange = useCallback(function(section, index, field, value) { + setPaymentForm(function(prev) { + var next = { + discountAmount: prev.discountAmount, + discountReason: prev.discountReason, + vehicleList: prev.vehicleList.slice(), + hydrogenList: prev.hydrogenList.slice(), + violationList: prev.violationList.slice(), + returnFeeList: prev.returnFeeList.slice() + }; + if (section === 'bill') { + if (field === 'discountAmount') next.discountAmount = value; + if (field === 'discountReason') next.discountReason = value; + } else if (section === 'vehicle' && index >= 0 && index < next.vehicleList.length) { + next.vehicleList[index] = Object.assign({}, next.vehicleList[index], { [field]: value }); + } else if (section === 'hydrogen' && index >= 0 && index < next.hydrogenList.length) { + next.hydrogenList[index] = Object.assign({}, next.hydrogenList[index], { [field]: value }); + } else if (section === 'violation' && index >= 0 && index < next.violationList.length) { + next.violationList[index] = Object.assign({}, next.violationList[index], { [field]: value }); + } else if (section === 'returnFee' && index >= 0 && index < next.returnFeeList.length) { + next.returnFeeList[index] = Object.assign({}, next.returnFeeList[index], { [field]: value }); + } + return next; + }); + }, []); + + var handleSubmit = useCallback(function() { + var errors = []; + if (!paymentForm.discountAmount || String(paymentForm.discountAmount).trim() === '') errors.push('减免金额'); + if (!paymentForm.discountReason || String(paymentForm.discountReason).trim() === '') errors.push('减免原因'); + paymentForm.vehicleList.forEach(function(v, i) { + if (!v.paidMonthlyRent || String(v.paidMonthlyRent).trim() === '') errors.push('车辆账单-实付月租金(第' + (i + 1) + '行)'); + if (!v.paidServiceFee || String(v.paidServiceFee).trim() === '') errors.push('车辆账单-实付服务费(第' + (i + 1) + '行)'); + if (!v.paidDeposit || String(v.paidDeposit).trim() === '') errors.push('车辆账单-实付保证金(第' + (i + 1) + '行)'); + }); + paymentForm.hydrogenList.forEach(function(h, i) { + if (!h.paidFeePrice || String(h.paidFeePrice).trim() === '') errors.push('氢费账单-实付氢费金额(第' + (i + 1) + '行)'); + }); + paymentForm.violationList.forEach(function(v, i) { + if (!v.paidFineAmount || String(v.paidFineAmount).trim() === '') errors.push('违章费用-实付罚款金额(第' + (i + 1) + '行)'); + }); + paymentForm.returnFeeList.forEach(function(r, i) { + if (!r.paidAmount || String(r.paidAmount).trim() === '') errors.push('还车费用-实付金额(第' + (i + 1) + '行)'); + }); + if (errors.length > 0) { + message.warning('请填写必填项:' + errors.join('、')); + return; + } + message.success('提交成功'); + handleBackToList(); + }, [paymentForm, handleBackToList]); + + var handleSave = useCallback(function() { + message.success('保存成功'); + }, []); + + function getStatusText(status) { + if (status === 'paid') return '已付款'; + if (status === 'partial') return '部分付款'; + return '未付款'; + } + function getStatusTagColor(status) { + if (status === 'paid') return 'success'; + if (status === 'partial') return 'warning'; + return 'error'; + } + + // 列表表格列(含行内编辑氢费/其他成本) + function renderEditableCost(row, field, label) { + var rowId = row.id; + var val = (listEditableCosts[rowId] && listEditableCosts[rowId][field] !== undefined) + ? listEditableCosts[rowId][field] + : (row[field] != null ? row[field] : '0.00'); + var num = (parseFloat(val) || 0).toFixed(2); + var isEditing = editingListCell && editingListCell.rowId === rowId && editingListCell.field === field; + if (isEditing) { + return React.createElement(Space, { key: 'edit', size: 4 }, + React.createElement(Input, { + value: editingInputValue, + onChange: function(e) { setEditingInputValue(e.target.value); }, + onBlur: function() { + var n = parseFloat(editingInputValue); + var formatted = (isNaN(n) ? 0 : n).toFixed(2); + setListEditableCosts(function(prev) { + var next = Object.assign({}, prev); + next[rowId] = Object.assign({}, next[rowId], { [field]: formatted }); + return next; + }); + setEditingListCell(null); + setEditingInputValue(''); + }, + style: { width: 80 }, + autoFocus: true + }), + React.createElement('span', null, '元') + ); + } + return React.createElement('span', { + style: { cursor: 'pointer' }, + onClick: function() { + setEditingInputValue(num); + setEditingListCell({ rowId: rowId, field: field }); + } + }, num); + } + + var listColumns = useMemo(function() { + return [ + { title: '付款状态', dataIndex: 'status', key: 'status', width: 100, render: function(s) { return React.createElement(Tag, { color: getStatusTagColor(s) }, getStatusText(s)); } }, + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 120 }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160 }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160 }, + { title: '当前期数', dataIndex: 'period', key: 'period', width: 90 }, + { title: '应付金额', dataIndex: 'payableAmount', key: 'payableAmount', width: 110, render: function(_, row) { + return React.createElement('a', { + onClick: function() { setPopover({ type: 'payable', data: mockPayableDetail }); }, + style: { color: '#1677ff', fontWeight: 600, textDecoration: 'underline', cursor: 'pointer' } + }, row.payableAmount); + }}, + { title: '实付金额', dataIndex: 'paidAmount', key: 'paidAmount', width: 110 }, + { title: '减免金额', dataIndex: 'discountAmount', key: 'discountAmount', width: 100 }, + { title: '未付金额', key: 'unpaid', width: 100, render: function(_, row) { + return (parseFloat(row.payableAmount || 0) - parseFloat(row.paidAmount || 0) - parseFloat(row.discountAmount || 0)).toFixed(2); + }}, + { title: '备注', dataIndex: 'remark', key: 'remark', width: 120 }, + { title: '车辆成本(元)', dataIndex: 'vehicleCost', key: 'vehicleCost', width: 120, render: function(val) { return (parseFloat(val) || 0).toFixed(2); } }, + { title: '氢费成本(元)', key: 'hydrogenCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'hydrogenCost', '氢费'); } }, + { title: '其他成本(元)', key: 'otherCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'otherCost', '其他'); } }, + { title: '操作', key: 'action', width: 140, fixed: 'right', render: function(_, row) { + return React.createElement(Space, null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { handleView(row.id, false); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', style: { color: '#52c41a' }, onClick: function() { handleView(row.id, true); } }, '收费') + ); + }} + ]; + }, [listEditableCosts, editingListCell, editingInputValue, handleView]); + + var rowSelection = useMemo(function() { + return { + selectedRowKeys: selectedRowKeys, + onChange: function(keys) { setSelectedRowKeys(keys || []); } + }; + }, [selectedRowKeys]); + + var tablePagination = useMemo(function() { + return { + current: currentPage, + pageSize: pageSize, + total: totalCount, + showSizeChanger: true, + showTotal: function(t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50'], + onChange: function(page, size) { + setCurrentPage(page); + if (size !== pageSize) setPageSize(size); + } + }; + }, [currentPage, pageSize, totalCount]); + + // —————— 详情视图 —————— + if (currentView === 'detail') { + var detailBreadcrumbItems = [ + { title: '运维管理' }, + { title: '业务管理' }, + { title: '租赁账单' }, + { title: detailMode === 'payment' ? '收费' : '查看' } + ]; + + var billInfoCols = [ + { label: '账单开始日期', value: mockBillDetail.startDate }, + { label: '账单结束日期', value: mockBillDetail.endDate }, + { label: '合同编码', value: mockBillDetail.contractCode }, + { label: '项目名称', value: mockBillDetail.projectName }, + { label: '客户名称', value: mockBillDetail.customerName }, + { label: '业务部门', value: mockBillDetail.department }, + { label: '业务负责人', value: mockBillDetail.responsible }, + { label: '付款周期', value: mockBillDetail.paymentCycle } + ]; + + return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } }, + React.createElement(Breadcrumb, { items: detailBreadcrumbItems, style: { marginBottom: 16 } }), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' } }, '账单信息'), + React.createElement(Row, { gutter: [24, 24] }, + billInfoCols.map(function(item, i) { + return React.createElement(Col, { key: i, span: 8 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, item.label + ':'), + React.createElement('span', null, item.value) + ) + ); + }), + React.createElement(Col, { span: 8 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '应付款总额:'), + React.createElement('span', { style: { color: '#1890ff', fontWeight: 600 } }, vehicleTotals.payableTotal + ' 元') + ) + ), + React.createElement(Col, { span: 8 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '实付款总额:'), + React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, vehicleTotals.paidTotal + ' 元') + ) + ), + React.createElement(Col, { span: 8 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免金额:'), + detailMode === 'payment' + ? React.createElement(Input, { + value: paymentForm.discountAmount, + onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountAmount', e.target.value); }, + style: { width: 120 }, + placeholder: '0.00', + addonAfter: '元' + }) + : React.createElement('span', { style: { fontWeight: 600 } }, mockBillDetail.discountTotal + ' 元') + ) + ), + React.createElement(Col, { span: 8 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '未付款总额:'), + React.createElement('span', { style: { color: '#ff4d4f', fontWeight: 600 } }, vehicleTotals.unpaidTotal + ' 元') + ) + ), + React.createElement(Col, { span: 24 }, + React.createElement('div', { style: { marginBottom: 8 } }, + React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免原因:'), + detailMode === 'payment' + ? React.createElement(Input.TextArea, { + value: paymentForm.discountReason, + onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountReason', e.target.value); }, + placeholder: '请输入减免原因', + rows: 3, + style: { maxWidth: 400 } + }) + : React.createElement('span', null, mockBillDetail.discountReason || '-') + ) + ) + ) + ), + // 车辆账单 + React.createElement(Card, { title: '车辆账单', style: { marginBottom: 16 } }, + React.createElement(Table, { + dataSource: mockVehicleList, + rowKey: 'plateNo', + pagination: false, + scroll: { x: 1200 }, + columns: [ + { title: '品牌', dataIndex: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', width: 90 }, + { title: '车牌号', dataIndex: 'plateNo', width: 100 }, + { title: '计划交车日期', dataIndex: 'planDelivery', width: 120 }, + { title: '实际交车日期', dataIndex: 'actualDelivery', width: 120 }, + { title: '账单开始日期', dataIndex: 'billStart', width: 120 }, + { title: '计费结束日期', dataIndex: 'billEnd', width: 120 }, + { title: '车辆月租金', dataIndex: 'monthlyRent', width: 110 }, + { title: '实付月租金', dataIndex: 'paidMonthlyRent', width: 110, render: function(_, record, idx) { + if (detailMode !== 'payment') return (parseFloat(record.paidMonthlyRent || 0)).toFixed(2); + var pf = paymentForm.vehicleList[idx] || {}; + return React.createElement(Input, { value: pf.paidMonthlyRent, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidMonthlyRent', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }}, + { title: '服务费', dataIndex: 'serviceFee', width: 90, render: function(val, record) { + if (detailMode === 'payment') return val; + return React.createElement('a', { onClick: function() { setPopover({ type: 'service', data: record.serviceItems }); } }, val); + }}, + { title: '实付服务费', dataIndex: 'paidServiceFee', width: 110, render: function(_, record, idx) { + if (detailMode !== 'payment') return (parseFloat(record.paidServiceFee || 0)).toFixed(2); + var pf = paymentForm.vehicleList[idx] || {}; + return React.createElement(Input, { value: pf.paidServiceFee, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidServiceFee', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }}, + { title: '保证金', dataIndex: 'deposit', width: 90 }, + { title: '实付保证金', dataIndex: 'paidDeposit', width: 110, render: function(_, record, idx) { + if (detailMode !== 'payment') return (parseFloat(record.paidDeposit || 0)).toFixed(2); + var pf = paymentForm.vehicleList[idx] || {}; + return React.createElement(Input, { value: pf.paidDeposit, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidDeposit', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }} + ], + summary: function() { + return React.createElement(Table.Summary, null, + React.createElement(Table.Summary.Row, null, + React.createElement(Table.Summary.Cell, { index: 0, colSpan: 7 }, '总计'), + React.createElement(Table.Summary.Cell, { index: 7 }, vehicleBillTotals.monthlyRentTotal), + React.createElement(Table.Summary.Cell, { index: 8 }, vehicleBillTotals.paidRentTotal), + React.createElement(Table.Summary.Cell, { index: 9 }, vehicleBillTotals.serviceFeeTotal), + React.createElement(Table.Summary.Cell, { index: 10 }, vehicleBillTotals.paidServiceFeeTotal), + React.createElement(Table.Summary.Cell, { index: 11 }, vehicleBillTotals.depositTotal), + React.createElement(Table.Summary.Cell, { index: 12 }, vehicleBillTotals.paidDepositTotal) + ) + ); + } + }) + ), + // 氢费账单 + React.createElement(Card, { title: '氢费账单', style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('span', { style: { marginRight: 24 } }, '加氢次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockHydrogenData.refuelCount + ' 次')), + React.createElement('span', null, '当前氢费余额:', React.createElement('span', { style: { fontWeight: 600, color: '#1890ff' } }, mockHydrogenData.balance + ' 元')) + ), + React.createElement(Table, { + dataSource: mockHydrogenData.list, + rowKey: function(item, i) { return i; }, + pagination: false, + columns: [ + { title: '加氢时间', dataIndex: 'refuelTime', width: 180 }, + { title: '加氢站名称', dataIndex: 'stationName', width: 140 }, + { title: '车牌号', dataIndex: 'plateNo', width: 100 }, + { title: '加氢量', dataIndex: 'amount', width: 90 }, + { title: '成本单价(元/KG)', dataIndex: 'costPrice', width: 120 }, + { title: '氢费价格(元)', dataIndex: 'feePrice', width: 120 }, + { title: '实付氢费金额(元)', dataIndex: 'paidFeePrice', width: 140, render: function(_, record, i) { + if (detailMode !== 'payment') return (parseFloat(record.paidFeePrice || 0)).toFixed(2); + var pf = paymentForm.hydrogenList[i] || {}; + return React.createElement(Input, { value: pf.paidFeePrice, onChange: function(e) { handlePaymentFormChange('hydrogen', i, 'paidFeePrice', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }} + ], + summary: function() { + var totalFee = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.feePrice || 0); }, 0).toFixed(2); + var totalPaid = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.paidFeePrice || 0); }, 0).toFixed(2); + return React.createElement(Table.Summary, null, + React.createElement(Table.Summary.Row, null, + React.createElement(Table.Summary.Cell, { index: 0, colSpan: 5 }, '总计'), + React.createElement(Table.Summary.Cell, { index: 5 }, totalFee), + React.createElement(Table.Summary.Cell, { index: 6 }, totalPaid) + ) + ); + } + }) + ), + // 违章费用 + React.createElement(Card, { title: '违章费用', style: { marginBottom: 16 } }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('span', null, '违章次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockViolationData.violationCount + ' 次')) + ), + React.createElement(Table, { + dataSource: mockViolationData.list, + rowKey: function(item, i) { return i; }, + pagination: false, + columns: [ + { title: '违章时间', dataIndex: 'violationTime', width: 180 }, + { title: '车牌号', dataIndex: 'plateNo', width: 100 }, + { title: '违章类型', dataIndex: 'violationType', width: 100 }, + { title: '违章地点', dataIndex: 'location', width: 200 }, + { title: '罚款金额', dataIndex: 'fineAmount', width: 100 }, + { title: '实付罚款金额(元)', dataIndex: 'paidFineAmount', width: 140, render: function(_, record, i) { + if (detailMode !== 'payment') return (parseFloat(record.paidFineAmount || 0)).toFixed(2); + var pf = paymentForm.violationList[i] || {}; + return React.createElement(Input, { value: pf.paidFineAmount, onChange: function(e) { handlePaymentFormChange('violation', i, 'paidFineAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }} + ], + summary: function() { + var totalPaid = mockViolationData.list.reduce(function(s, item) { return s + parseFloat(item.paidFineAmount || 0); }, 0).toFixed(2); + return React.createElement(Table.Summary, null, + React.createElement(Table.Summary.Row, null, + React.createElement(Table.Summary.Cell, { index: 0, colSpan: 4 }, '总计'), + React.createElement(Table.Summary.Cell, { index: 4 }, mockViolationData.totalAmount), + React.createElement(Table.Summary.Cell, { index: 5 }, totalPaid) + ) + ); + } + }) + ), + // 还车费用 + React.createElement(Card, { title: '还车费用', style: { marginBottom: 16 } }, + React.createElement(Table, { + dataSource: mockReturnFeeData.list, + rowKey: function(item, i) { return i; }, + pagination: false, + columns: [ + { title: '费用名称', dataIndex: 'feeName', width: 140 }, + { title: '金额', dataIndex: 'amount', width: 100 }, + { title: '实付金额(元)', dataIndex: 'paidAmount', width: 120, render: function(_, record, i) { + if (detailMode !== 'payment') return (parseFloat(record.paidAmount || 0)).toFixed(2); + var pf = paymentForm.returnFeeList[i] || {}; + return React.createElement(Input, { value: pf.paidAmount, onChange: function(e) { handlePaymentFormChange('returnFee', i, 'paidAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' }); + }}, + { title: '照片', dataIndex: 'photos', width: 200, render: function(photos) { + if (!photos || photos.length === 0) return '-'; + return React.createElement(Space, null, photos.slice(0, 5).map(function(url, idx) { + return React.createElement('img', { + key: idx, + src: url, + alt: '', + style: { width: 40, height: 40, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function() { setPhotoViewer({ visible: true, photos: photos, currentIndex: idx }); } + }); + })); + }}, + { title: '附件', dataIndex: 'attachments', render: function(attachments) { + if (!attachments || attachments.length === 0) return '-'; + return React.createElement(Space, { wrap: true }, attachments.map(function(att, idx) { + return React.createElement('a', { key: idx, onClick: function() { message.info('下载附件:' + att.name); } }, att.name); + })); + }} + ], + summary: function() { + var totalPaid = mockReturnFeeData.list.reduce(function(s, item) { return s + parseFloat(item.paidAmount || 0); }, 0).toFixed(2); + return React.createElement(Table.Summary, null, + React.createElement(Table.Summary.Row, null, + React.createElement(Table.Summary.Cell, { index: 0 }, '总计'), + React.createElement(Table.Summary.Cell, { index: 1 }, mockReturnFeeData.totalAmount), + React.createElement(Table.Summary.Cell, { index: 2 }, totalPaid), + React.createElement(Table.Summary.Cell, { index: 3 }, ''), + React.createElement(Table.Summary.Cell, { index: 4 }, '') + ) + ); + } + }) + ), + React.createElement('div', { style: { marginTop: 24, textAlign: 'center' } }, + React.createElement(Space, null, + detailMode === 'payment' && React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交审核'), + detailMode === 'payment' && React.createElement(Button, { onClick: handleSave }, '保存'), + detailMode === 'payment' && React.createElement(Button, { onClick: handleBackToList }, '取消'), + detailMode === 'view' && React.createElement(Button, { onClick: handleBackToList }, '返回') + ) + ), + // 照片查看器 Modal + React.createElement(Modal, { + title: '照片查看', + open: photoViewer.visible && photoViewer.photos.length > 0, + onCancel: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); }, + footer: null, + width: '90vw', + centered: true + }, photoViewer.photos.length > 0 ? React.createElement('div', { style: { textAlign: 'center' } }, + React.createElement('img', { + src: photoViewer.photos[photoViewer.currentIndex].replace('80/80', '600/600'), + alt: '', + style: { maxWidth: '100%', maxHeight: '70vh', objectFit: 'contain' } + }), + React.createElement('div', { style: { marginTop: 16 } }, + React.createElement(Button, { + disabled: photoViewer.currentIndex <= 0, + onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex - 1 }); } + }, '上一张'), + React.createElement('span', { style: { margin: '0 16px' } }, (photoViewer.currentIndex + 1) + ' / ' + photoViewer.photos.length), + React.createElement(Button, { + disabled: photoViewer.currentIndex >= photoViewer.photos.length - 1, + onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex + 1 }); } + }, '下一张') + ) + ) : null), + // 服务费明细 Modal + popover.type === 'service' && React.createElement(Modal, { + title: '服务费明细', + open: true, + onCancel: function() { setPopover({ type: null, data: null }); }, + footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭') + }, React.createElement(Table, { + dataSource: popover.data, + rowKey: function(item, i) { return i; }, + pagination: false, + columns: [ + { title: '服务项', dataIndex: 'name' }, + { title: '价格', dataIndex: 'price' }, + { title: '服务生效日期', dataIndex: 'effectiveDate' } + ] + })) + ); + } + + // —————— 列表视图 —————— + var listBreadcrumbItems = [ + { title: '运维管理' }, + { title: '业务管理' }, + { title: '租赁账单' } + ]; + + return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } }, + React.createElement(Breadcrumb, { items: listBreadcrumbItems, style: { marginBottom: 16 } }), + React.createElement(Card, null, + React.createElement(Row, { gutter: [16, 16], style: { marginBottom: 16 }, align: 'middle' }, + React.createElement(Col, null, + React.createElement('span', { style: { marginRight: 8 } }, '合同编码:'), + React.createElement(Select, { + placeholder: '请选择合同编码', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: contractFilter, + onChange: setContractFilter, + style: { width: 200 }, + options: contractOptions + }) + ), + React.createElement(Col, null, + React.createElement('span', { style: { marginRight: 8 } }, '项目名称:'), + React.createElement(Select, { + placeholder: '请选择项目名称', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: projectFilter, + onChange: setProjectFilter, + style: { width: 200 }, + options: projectOptions + }) + ), + React.createElement(Col, null, + React.createElement('span', { style: { marginRight: 8 } }, '客户名称:'), + React.createElement(Select, { + placeholder: '请选择客户名称', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: customerFilter, + onChange: setCustomerFilter, + style: { width: 200 }, + options: customerOptions + }) + ), + React.createElement(Col, null, + React.createElement('span', { style: { marginRight: 8 } }, '付款状态:'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '全部', + allowClear: true, + value: statusFilter, + onChange: setStatusFilter, + style: { width: 200 }, + options: statusOptions, + maxTagCount: 'responsive' + }) + ), + React.createElement(Col, null, + React.createElement(Space, null, + React.createElement(Button, { type: 'primary', onClick: handleSearch }, '查询'), + React.createElement(Button, { onClick: handleReset }, '重置') + ) + ) + ), + React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' } }, + React.createElement(Button, { type: 'primary', onClick: handleExport }, '导出') + ), + React.createElement(Table, { + rowSelection: rowSelection, + columns: listColumns, + dataSource: paginatedList, + rowKey: 'id', + pagination: tablePagination, + scroll: { x: 1600 }, + size: 'middle' + }) + ), + // 应付金额明细 Modal(列表按内容一行显示、宽度随内容调整) + React.createElement(Modal, { + title: '应付金额明细', + open: popover.type === 'payable', + onCancel: function() { setPopover({ type: null, data: null }); }, + footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭'), + width: 'fit-content', + style: { maxWidth: '90vw' }, + styles: { body: { paddingBottom: 24 } } + }, popover.type === 'payable' && popover.data ? React.createElement(Table, { + dataSource: popover.data, + rowKey: function(item, i) { return i; }, + pagination: false, + tableLayout: 'auto', + style: { minWidth: 0 }, + columns: [ + { title: '账单开始日期', dataIndex: 'startDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }, + { title: '账单结束日期', dataIndex: 'endDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }, + { title: '车牌号', dataIndex: 'plateNo', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }, + { title: '车辆租金', dataIndex: 'rent', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }, + { title: '服务费', dataIndex: 'serviceFee', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }, + { title: '保证金', dataIndex: 'deposit', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } } + ] + }) : null) + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/业务管理/编辑交车任务.jsx b/web端/业务管理/编辑交车任务.jsx new file mode 100644 index 0000000..6269bba --- /dev/null +++ b/web端/业务管理/编辑交车任务.jsx @@ -0,0 +1,283 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 数字化资产ONEOS运管平台 - 编辑交车任务模块(布局同新增交车任务,项目名称禁用不可改) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var message = antd.message; + var Option = Select.Option; + var Checkbox = antd.Checkbox; + var Tooltip = antd.Tooltip; + var RangePicker = DatePicker.RangePicker; + + var projectId = React.useState('p1'); + var selectedProjectId = projectId[0]; + var setSelectedProjectId = projectId[1]; + + var expectedDelivery = React.useState(null); + var expectedDeliveryValue = expectedDelivery[0]; + var setExpectedDelivery = expectedDelivery[1]; + + var billingDate = React.useState(null); + var billingDateValue = billingDate[0]; + var setBillingDate = billingDate[1]; + + var selectedRowKeys = React.useState(['v3', 'v4']); + var checkedRowKeys = selectedRowKeys[0]; + var setCheckedRowKeys = selectedRowKeys[1]; + + var formErrors = React.useState({}); + var errors = formErrors[0]; + var setErrors = formErrors[1]; + + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + + // Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示 + var projectList = [ + { id: 'p1', name: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [ + { key: 'v1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliveryStatus: 'submitted' }, + { key: 'v2', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10002', vin: 'LFV2BJCH8K3123457', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v3', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌', deliveryStatus: 'none' }, + { key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' }, + { key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' } + ]}, + { id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [ + { key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' }, + { key: 'v8', brand: '福田', model: '欧辉氢能大巴', plateNo: '', vin: 'LZYTBACR2M2345678', monthRent: '19800', serviceFee: '1100', deposit: '45000', remark: '', deliveryStatus: 'none' } + ]}, + { id: 'p3', name: '杭州某某租赁氢能项目', contractCode: 'HZZL20260115YW100089A', customerName: '杭州某某租赁有限公司', deliveryRegion: '浙江省 / 杭州市', deliveryLocation: '浙江省杭州市余杭区未来科技城', vehicles: [ + { key: 'v9', brand: '品牌C', model: '型号C1', plateNo: '浙A40004', vin: 'L4234567890ABCDEF', monthRent: '8200', serviceFee: '450', deposit: '20000', remark: '重点客户', deliveryStatus: 'completed' }, + { key: 'v10', brand: '品牌C', model: '型号C2', plateNo: '', vin: 'L5234567890ABCDEF', monthRent: '7800', serviceFee: '420', deposit: '19000', remark: '', deliveryStatus: 'submitted' }, + { key: 'v11', brand: '东风', model: '氢燃料电池厢货', plateNo: '浙A50001', vin: 'LFV2BJCH8K5678901', monthRent: '9200', serviceFee: '520', deposit: '22000', remark: '城配', deliveryStatus: 'none' }, + { key: 'v12', brand: '开沃', model: '创源氢能轻卡', plateNo: '浙A50002', vin: 'LJXTBACR9N6789012', monthRent: '8800', serviceFee: '480', deposit: '21000', remark: '', deliveryStatus: 'none' } + ]} + ]; + + var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; }); + var vehicleListRaw = selectedProject ? selectedProject.vehicles : []; + var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; }); + var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; }); + + var handleProjectChange = function(id) { + setSelectedProjectId(id || ''); + setCheckedRowKeys([]); + }; + + var todayStr = (function() { + var d = new Date(); + return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); + })(); + + var validateExpectedDelivery = function() { + if (!expectedDeliveryValue || !expectedDeliveryValue.length) return '请选择预计交车日期'; + var start = expectedDeliveryValue[0] && expectedDeliveryValue[0].format ? expectedDeliveryValue[0].format('YYYY-MM-DD') : null; + var end = expectedDeliveryValue[1] && expectedDeliveryValue[1].format ? expectedDeliveryValue[1].format('YYYY-MM-DD') : null; + if (!start) return '请选择预计交车日期'; + if (end && end < start) return '结束日期不能早于开始日期'; + if (end && end < todayStr) return '结束日期不能早于当前日期'; + return null; + }; + + var expectedDeliveryError = validateExpectedDelivery(); + + var billingDateError = !billingDateValue ? '请选择开始计费日期' : null; + + var handleSubmit = function() { + var err = {}; + if (!selectedProjectId) err.projectName = '请选择项目名称'; + if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError; + if (billingDateError) err.billingDate = billingDateError; + if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车'; + setErrors(err); + if (Object.keys(err).length) return; + message.success('交车任务已保存。'); + }; + + var handleCancel = function() { + message.info('取消'); + }; + + var onSelectAll = function(checked) { + if (checked) setCheckedRowKeys(selectableVehicles.map(function(v) { return v.key; })); + else setCheckedRowKeys([]); + }; + + var onSelectRow = function(record, checked) { + if (checked) setCheckedRowKeys(function(prev) { return prev.indexOf(record.key) === -1 ? prev.concat(record.key) : prev; }); + else setCheckedRowKeys(function(prev) { return prev.filter(function(k) { return k !== record.key; }); }); + }; + + 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' }, + cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 }, + 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' }, + inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 } + }; + + var reqSpecBlock = { marginBottom: 16 }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 }; + var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 }; + + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-编辑交车任务')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'), + React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:已选择禁用,不可修改;'), + React.createElement('div', { style: reqSpecLi }, '2.2.合同编码、客户名称、交车区域、交车地点、预计交车日期、开始计费日期、车辆列表等可修改;'), + React.createElement('div', { style: reqSpecLi }, '预计交车日期:不能早于当前日期'), + React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;') + )); + + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + var FormItem = function(props) { + return React.createElement('div', { style: styles.formCol }, + React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null + ); + }; + + var projectOptions = projectList.map(function(p) { return React.createElement(Option, { key: p.id, value: p.id }, p.name); }); + + var formRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName }, + React.createElement(Select, { + placeholder: '请选择或输入项目名称', + style: { width: '100%' }, + value: selectedProjectId || undefined, + disabled: true, + showSearch: true, + filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, + status: errors.projectName ? 'error' : undefined + }, projectOptions)), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: selectedProject ? selectedProject.contractCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: selectedProject ? selectedProject.customerName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryRegion : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryLocation : '', disabled: true, style: { width: '100%' } })) + ); + + var formRow2 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery }, + React.createElement(RangePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'], + value: expectedDeliveryValue, + onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); }, + status: errors.expectedDelivery ? 'error' : undefined + })), + React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate }, + React.createElement(DatePicker, { + style: { width: '100%' }, + format: 'YYYY-MM-DD', + placeholder: '请选择日期', + value: billingDateValue, + onChange: function(d, dateStr) { setBillingDate(d); }, + status: errors.billingDate ? 'error' : undefined + })) + ); + + var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); + var someSelectableChecked = selectableVehicles.some(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; }); + var tableHeader = React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: Object.assign({}, styles.th, { width: 48 }) }, + React.createElement(Checkbox, { + checked: allSelectableChecked, + indeterminate: someSelectableChecked && !allSelectableChecked, + onChange: function(e) { onSelectAll(e.target.checked); } + })), + React.createElement('th', { style: styles.th }, '品牌'), + React.createElement('th', { style: styles.th }, '型号'), + React.createElement('th', { style: styles.th }, '车牌号'), + React.createElement('th', { style: styles.th }, '车辆识别代码'), + React.createElement('th', { style: styles.th }, '车辆月租金'), + React.createElement('th', { style: styles.th }, '服务费'), + React.createElement('th', { style: styles.th }, '保证金'), + React.createElement('th', { style: styles.th }, '备注') + ) + ); + + var 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) { + var isSubmitted = row.deliveryStatus === 'submitted'; + return React.createElement('tr', { key: row.key }, + React.createElement('td', { style: styles.td }, + isSubmitted + ? React.createElement(Tooltip, { title: '该车辆已有交车任务' }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } }, + React.createElement(Checkbox, { disabled: true, checked: false }), + React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交'))) + : React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })), + React.createElement('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 })) + ); + }) + ); + + var tableEl = React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, tableHeader, tableBody) + ); + + if (errors.vehicles) { + tableEl = React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.errMsg, { marginBottom: 8 }) }, errors.vehicles), + tableEl + ); + } + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, + React.createElement('span', null, '业务管理'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', null, '交车任务'), + React.createElement('span', { style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { style: { color: '#1890ff' } }, '编辑交车任务')), + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')), + React.createElement('div', { style: styles.cardBody }, + formRow1, + formRow2, + tableEl)), + React.createElement('div', { style: { height: 60 } }), + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleCancel }, '取消')) + ); +}; diff --git a/web端/登录.jsx b/web端/登录.jsx new file mode 100644 index 0000000..1da2bc7 --- /dev/null +++ b/web端/登录.jsx @@ -0,0 +1,446 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 参考 Arco Design Pro 登录页布局:左侧品牌区 + 右侧登录表单 +const Component = function () { + var antd = window.antd; + var useState = React.useState; + var useRef = React.useRef; + var useEffect = React.useEffect; + + var _state = useState({ username: '', password: '', remember: true, agreeTerms: false, phone: '', smsCode: '' }); + var formData = _state[0]; + var setFormData = _state[1]; + + var _mode = useState('account'); + var loginMode = _mode[0]; + var setLoginMode = _mode[1]; + + var _countdown = useState(0); + var countdown = _countdown[0]; + var setCountdown = _countdown[1]; + + var _hasRequestedCode = useState(false); + var hasRequestedCode = _hasRequestedCode[0]; + var setHasRequestedCode = _hasRequestedCode[1]; + + var timerRef = useRef(null); + + useEffect(function () { + return function () { + if (timerRef.current) { + clearInterval(timerRef.current); + } + }; + }, []); + + var handleChange = function (field, value) { + var next = {}; + next[field] = value; + setFormData(function (prev) { + return Object.assign({}, prev, next); + }); + }; + + var handleGetCode = function () { + if (countdown > 0) return; + setHasRequestedCode(true); + setCountdown(60); + timerRef.current = setInterval(function () { + setCountdown(function (prev) { + if (prev <= 1) { + clearInterval(timerRef.current); + timerRef.current = null; + return 0; + } + return prev - 1; + }); + }, 1000); + }; + + var handleSubmit = function () { + if (!formData.agreeTerms) { + antd.message.warning('请阅读并同意《用户服务协议》和《隐私政策》'); + return; + } + if (loginMode === 'phone') { + if (!formData.phone) { + antd.message.warning('请输入手机号'); + return; + } + if (!formData.smsCode) { + antd.message.warning('请输入验证码'); + return; + } + } else { + if (!formData.username) { + antd.message.warning('请输入用户名'); + return; + } + if (!formData.password) { + antd.message.warning('请输入密码'); + return; + } + } + antd.message.success('登录成功(原型演示)'); + if (window.$axure && typeof window.$axure.navigateToUrl === 'function') { + window.$axure.navigateToUrl('工作台'); + } else { + window.location.hash = '工作台'; + } + }; + + var switchToPhone = function (e) { + if (e && e.preventDefault) e.preventDefault(); + setLoginMode('phone'); + }; + var switchToAccount = function (e) { + if (e && e.preventDefault) e.preventDefault(); + setLoginMode('account'); + }; + + var styles = { + wrap: { + display: 'flex', + minHeight: '100vh', + width: '100%', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + backgroundColor: '#f5f5f5' + }, + left: { + flex: '1', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: '48px', + background: 'linear-gradient(135deg, #1d2129 0%, #2d3748 50%, #1d2129 100%)', + color: '#fff' + }, + leftContent: { + maxWidth: '400px', + textAlign: 'center' + }, + title: { + fontSize: '28px', + fontWeight: '600', + marginBottom: '16px', + letterSpacing: '0.5px' + }, + desc: { + fontSize: '14px', + opacity: 0.85, + lineHeight: 1.6 + }, + right: { + width: '480px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: '48px', + backgroundColor: '#fff', + boxShadow: '-4px 0 24px rgba(0,0,0,0.06)' + }, + formCard: { + width: '100%', + maxWidth: '360px' + }, + formTitle: { + fontSize: '24px', + fontWeight: '600', + color: '#1d2129', + marginBottom: '8px', + textAlign: 'center' + }, + formSub: { + fontSize: '14px', + color: '#86909c', + marginBottom: '32px', + textAlign: 'center' + }, + formItem: { + marginBottom: '20px' + }, + label: { + display: 'block', + fontSize: '14px', + color: '#4e5969', + marginBottom: '8px' + }, + input: { + width: '100%', + height: '40px', + padding: '8px 12px', + fontSize: '14px', + border: '1px solid #e5e6eb', + borderRadius: '6px', + boxSizing: 'border-box', + outline: 'none' + }, + codeRow: { + display: 'flex', + gap: '8px', + alignItems: 'center' + }, + codeInput: { + flex: '1', + height: '40px', + padding: '8px 12px', + fontSize: '14px', + border: '1px solid #e5e6eb', + borderRadius: '6px', + boxSizing: 'border-box', + outline: 'none' + }, + getCodeBtn: { + flexShrink: 0, + height: '40px', + padding: '0 16px', + fontSize: '14px', + color: '#165dff', + backgroundColor: '#fff', + border: '1px solid #165dff', + borderRadius: '6px', + cursor: 'pointer' + }, + getCodeBtnDisabled: { + color: '#86909c', + borderColor: '#e5e6eb', + cursor: 'not-allowed' + }, + row: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '24px' + }, + submitBtn: { + width: '100%', + height: '44px', + fontSize: '16px', + fontWeight: '500', + borderRadius: '6px' + }, + tabBar: { + display: 'flex', + marginBottom: '24px', + borderBottom: '1px solid #e5e6eb' + }, + tab: { + flex: 1, + textAlign: 'center', + padding: '12px 0', + fontSize: '15px', + color: '#86909c', + cursor: 'pointer', + borderBottom: '2px solid transparent', + marginBottom: '-1px' + }, + tabActive: { + color: '#165dff', + fontWeight: '500', + borderBottomColor: '#165dff' + } + }; + + return React.createElement( + React.Fragment, + null, + React.createElement('style', { + dangerouslySetInnerHTML: { + __html: '@media (max-width: 768px) { .login-right { width: 100% !important; min-width: 100% !important; } .login-left { min-height: 200px !important; } .login-wrap { flex-direction: column !important; } }' + } + }), + React.createElement( + 'div', + { style: styles.wrap, className: 'login-wrap' }, + React.createElement( + 'div', + { style: styles.left, className: 'login-left' }, + React.createElement( + 'div', + { style: styles.leftContent }, + React.createElement('h1', { style: styles.title }, '羚牛氢能'), + React.createElement('p', { style: styles.desc }, '数字化资产ONE-OS运管平台') + ) + ), + React.createElement( + 'div', + { style: styles.right, className: 'login-right' }, + React.createElement( + 'div', + { style: styles.formCard }, + React.createElement('h2', { style: styles.formTitle }, '欢迎登录'), + React.createElement( + 'div', + { style: styles.tabBar }, + React.createElement( + 'div', + { + style: loginMode === 'account' ? Object.assign({}, styles.tab, styles.tabActive) : styles.tab, + onClick: switchToAccount, + role: 'button', + tabIndex: 0 + }, + '账号密码登录' + ), + React.createElement( + 'div', + { + style: loginMode === 'phone' ? Object.assign({}, styles.tab, styles.tabActive) : styles.tab, + onClick: switchToPhone, + role: 'button', + tabIndex: 0 + }, + '手机号登录' + ) + ), + React.createElement('p', { style: styles.formSub }, loginMode === 'phone' ? '请输入手机号与验证码' : '请输入您的账号与密码'), + loginMode === 'phone' + ? React.createElement( + React.Fragment, + null, + React.createElement( + 'div', + { style: styles.formItem }, + React.createElement('label', { style: styles.label }, '手机号'), + React.createElement('input', { + style: styles.input, + placeholder: '请输入手机号', + value: formData.phone, + onChange: function (e) { + handleChange('phone', e.target.value); + } + }) + ), + React.createElement( + 'div', + { style: styles.formItem }, + React.createElement('label', { style: styles.label }, '验证码'), + React.createElement( + 'div', + { style: styles.codeRow }, + React.createElement('input', { + style: styles.codeInput, + placeholder: '请输入验证码', + value: formData.smsCode, + onChange: function (e) { + handleChange('smsCode', e.target.value); + } + }), + React.createElement( + 'button', + { + type: 'button', + style: countdown > 0 ? Object.assign({}, styles.getCodeBtn, styles.getCodeBtnDisabled) : styles.getCodeBtn, + disabled: countdown > 0, + onClick: handleGetCode + }, + countdown > 0 ? countdown + '秒后重新获取' : (hasRequestedCode ? '再次获取' : '获取验证码') + ) + ) + ) + ) + : React.createElement( + React.Fragment, + null, + React.createElement( + 'div', + { style: styles.formItem }, + React.createElement('label', { style: styles.label }, '用户名'), + React.createElement('input', { + style: styles.input, + placeholder: '请输入用户名', + value: formData.username, + onChange: function (e) { + handleChange('username', e.target.value); + } + }) + ), + React.createElement( + 'div', + { style: styles.formItem }, + React.createElement('label', { style: styles.label }, '密码'), + React.createElement('input', { + style: styles.input, + type: 'password', + placeholder: '请输入密码', + value: formData.password, + onChange: function (e) { + handleChange('password', e.target.value); + } + }) + ) + ), + React.createElement( + 'div', + { style: styles.row }, + React.createElement( + 'label', + { style: { display: 'flex', alignItems: 'center', cursor: 'pointer', fontSize: '14px', color: '#4e5969' } }, + React.createElement('input', { + type: 'checkbox', + checked: formData.remember, + onChange: function (e) { + handleChange('remember', e.target.checked); + }, + style: { marginRight: '8px' } + }), + '记住我' + ) + ), + React.createElement( + 'label', + { + style: { + display: 'flex', + alignItems: 'flex-start', + cursor: 'pointer', + fontSize: '14px', + color: '#4e5969', + marginBottom: '20px', + lineHeight: 1.5 + } + }, + React.createElement('input', { + type: 'checkbox', + checked: formData.agreeTerms, + onChange: function (e) { + handleChange('agreeTerms', e.target.checked); + }, + style: { marginRight: '8px', marginTop: '3px', flexShrink: 0 } + }), + React.createElement( + 'span', + null, + '阅读并同意', + React.createElement('a', { href: '#', style: { color: '#165dff', marginLeft: '2px' }, onClick: function (e) { e.preventDefault(); } }, '《用户服务协议》'), + '和', + React.createElement('a', { href: '#', style: { color: '#165dff' }, onClick: function (e) { e.preventDefault(); } }, '《隐私政策》') + ) + ), + antd && antd.Button + ? React.createElement(antd.Button, { + type: 'primary', + block: true, + size: 'large', + style: styles.submitBtn, + onClick: handleSubmit + }, '登 录') + : React.createElement( + 'button', + { + style: Object.assign({}, styles.submitBtn, { + backgroundColor: '#165dff', + color: '#fff', + border: 'none', + cursor: 'pointer' + }), + onClick: handleSubmit + }, + '登 录' + ) + ) + ) + ) + ); +}; diff --git a/web端/财务管理/提车首付款.jsx b/web端/财务管理/提车首付款.jsx new file mode 100644 index 0000000..0817308 --- /dev/null +++ b/web端/财务管理/提车首付款.jsx @@ -0,0 +1,1048 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 提车首付款 - 车辆资产管理后台(运维管理-财务管理-提车首付款) + +const Component = function() { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + + // 分页 + var pageState = useState(1); + var currentPage = pageState[0]; + var setCurrentPage = pageState[1]; + var pageSizeState = useState(10); + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + // 筛选:表单输入值(用户填写) + var contractInputState = useState(''); + var contractInput = contractInputState[0]; + var setContractInput = contractInputState[1]; + var projectInputState = useState(''); + var projectInput = projectInputState[0]; + var setProjectInput = projectInputState[1]; + var customerInputState = useState(''); + var customerInput = customerInputState[0]; + var setCustomerInput = customerInputState[1]; + var statusInputState = useState([]); + var statusInput = statusInputState[0]; + var setStatusInput = statusInputState[1]; + // 可搜索选择器下拉展开:contract | project | customer | null + var filterDropdownOpenState = useState(null); + var filterDropdownOpen = filterDropdownOpenState[0]; + var setFilterDropdownOpen = filterDropdownOpenState[1]; + // 付款状态多选下拉展开 + var statusDropdownOpenState = useState(false); + var statusDropdownOpen = statusDropdownOpenState[0]; + var setStatusDropdownOpen = statusDropdownOpenState[1]; + // 筛选:已生效条件(点击查询后与列表联动) + var appliedFilterState = useState({ + contract: '', + project: '', + customer: '', + status: [] + }); + var appliedFilter = appliedFilterState[0]; + var setAppliedFilter = appliedFilterState[1]; + + // 查看页:当前视图 list | detail + var currentViewState = useState('list'); + var currentView = currentViewState[0]; + var setCurrentView = currentViewState[1]; + // 详情模式:view 查看 | payment 付款 + var detailModeState = useState('view'); + var detailMode = detailModeState[0]; + var setDetailMode = detailModeState[1]; + // 付款表单(付款模式可编辑) + var paymentFormState = useState({ + discountAmount: '', + discountReason: '', + hydrogenPaidAmount: '', + vehicleList: [], + hydrogenList: [], + violationList: [], + returnFeeList: [] + }); + var paymentForm = paymentFormState[0]; + var setPaymentForm = paymentFormState[1]; + // 服务费明细弹窗 + var popoverState = useState({ type: null, data: null }); + var popover = popoverState[0]; + var setPopover = popoverState[1]; + // 照片查看器 + var photoViewerState = useState({ visible: false, photos: [], currentIndex: 0 }); + var photoViewer = photoViewerState[0]; + var setPhotoViewer = photoViewerState[1]; + // 当前查看/付款的账单 id + var viewingBillIdState = useState(null); + var viewingBillId = viewingBillIdState[0]; + var setViewingBillId = viewingBillIdState[1]; + // 审核状态覆盖(id -> draft|pending|auditing|completed|withdrawn|rejected)待提交|审核中|审核完成|撤回|审核驳回 + var approvalStatusMapState = useState({}); + var approvalStatusMap = approvalStatusMapState[0]; + var setApprovalStatusMap = approvalStatusMapState[1]; + // 撤回二次确认:待撤回的 row id + var withdrawConfirmIdState = useState(null); + var withdrawConfirmId = withdrawConfirmIdState[0]; + var setWithdrawConfirmId = withdrawConfirmIdState[1]; + // 查看需求明细弹窗 + var requirementDetailVisibleState = useState(false); + var requirementDetailVisible = requirementDetailVisibleState[0]; + var setRequirementDetailVisible = requirementDetailVisibleState[1]; + + // 需求明细文案(提车首付款) + var requirementDetailContent = '提车首付款\n\n1.面包屑:\n1.1.运维管理-财务管理-提车首付款\n\n2.筛选:\n2.1.合同编码:合同编码显示租赁合同编码;选择器,支持从输入框进行内容模糊搜索;\n2.2.项目名称:项目名称显示租赁合同对应项目名称;选择器,支持从输入框输入内容模糊搜索;\n2.3.客户名称:客户名称显示租赁合同对应客户名称,选择器,支持从输入框输入内容模糊搜索;\n2.4.付款状态:显示合同提车首付款付款状态,选择器,支持多选;选项为全部、部分付款、未付款、已付款;\n2.5.右侧为查询和重置按钮,筛选条件以且的模式进行筛选,点击重置清空条件;\n\n3.列表:\n3.1.列表下方为分页器,支持选择单页数据条数;\n3.2.列表字段按照以下排序:付款状态、合同编码、项目名称、客户名称、合同生效日期、首期应付金额、实付金额、减免金额、未付金额、备注、操作;\n3.3.审核状态:分为待提交、审核中、审核完成、审核驳回、撤回;\n 流程流转为:待提交(交车任务生效/完成收费补充保存) → 完成收费补充提交 → 审核中(可撤回,撤回后显示为撤回)→ 审核完成(最终节点审核完成) / 审核驳回(驳回显示为审核驳回);\n a.待提交:交车单交车成功时系统会自动生成提车首付款,状态为「待提交」;业务人员点击收费补充信息点击保存,状态也为「待提交」,同时操作可:查看、收费;\n b.审核中:提车首付款完成收费信息填写,点击提交审核,状态变更为「审核中」,同时操作可:查看、撤回;\n c.审核完成:提车首付款完成审核后,显示为审核完成,同时操作可:查看;\n d.审核驳回:提车首付款在任意审核节点被驳回时,显示为审核驳回,同时操作可:查看、收费;\n e.撤回:提车首付款在审核中时,发起人可进行撤回,撤回后可重新补充收费信息并再次提交;操作可:查看、收费;\n3.4.付款状态:显示付款状态,状态分为:已付款、部分付款、未付款;\n a.已付款:应付金额为0时,显示为已付款;\n b.部分付款:未付金额>0且<应付金额时,显示为部分付款;\n c.未付款:实付金额为0时,显示为未付款;\n3.5.合同编码:显示租赁合同-合同编码;\n3.6.项目名称:显示该项目租赁合同-项目名称;\n3.7.客户名称:显示该项目租赁合同-客户名称;\n3.8.合同生效日期:显示该项目租赁合同-生效日期,格式为YYYY-MM-DD;\n3.9.首期应付金额:显示该笔账单首期应付金额,精确至2位小数,计算方式为:应付款总额=车辆月租金总计+服务费总计+保证金总计+氢气预付款金额;\n3.10.实付金额:显示该笔账单实付金额,精确至2位小数,如未付则显示为0;如已上传付款信息,则显示金额,计算方式为:实付款金额=实付月租金总计+实付服务费总计+实付保证金总计+氢气实付款金额\n3.11.减免金额:显示该笔账单减免金额,精确至2位小数,如无则显示为0;\n3.12.未付金额:显示该笔账单未付金额,精确至2位小数,如无则显示为0;未付款金额=应付款总额-实付金额-减免金额\n3.13.减免原因:显示减免金额添加原因;\n3.14.操作:查看、收费、撤回;\n a.查看:点击查看进入查看页;\n b.收费:点击收费,录入收费信息;\n c.撤回:审核状态为审核中时,点击撤回,进行二次提示,确认后该条数据审核状态显示为撤回,可重新进行收费操作;'; + + // 模拟数据:合同编码、项目、客户选项(用于选择器展示,可选) + var contractOptions = useMemo(function() { + return [ + { value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' }, + { value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' }, + { value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' }, + { value: 'HT-ZL-2024-088', label: 'HT-ZL-2024-088' }, + { value: 'HT-ZL-2024-099', label: 'HT-ZL-2024-099' } + ]; + }, []); + var projectOptions = useMemo(function() { + return [ + { value: '北京朝阳区租赁项目', label: '北京朝阳区租赁项目' }, + { value: '上海浦东车辆租赁', label: '上海浦东车辆租赁' }, + { value: '广州天河运营项目', label: '广州天河运营项目' }, + { value: '深圳南山首期项目', label: '深圳南山首期项目' } + ]; + }, []); + var customerOptions = useMemo(function() { + return [ + { value: '某某科技有限公司', label: '某某科技有限公司' }, + { value: '某某物流有限公司', label: '某某物流有限公司' }, + { value: '某某制造有限公司', label: '某某制造有限公司' }, + { value: '某某出行服务公司', label: '某某出行服务公司' } + ]; + }, []); + + // 模拟列表数据 + var mockList = useMemo(function() { + var list = []; + var statuses = ['paid', 'partial', 'unpaid']; + var approvalStatuses = ['draft', 'pending', 'auditing', 'completed', 'withdrawn', 'rejected']; + var contracts = ['HT-ZL-2025-001', 'HT-ZL-2025-002', 'HT-ZL-2025-003', 'HT-ZL-2024-088', 'HT-ZL-2024-099']; + var projects = ['北京朝阳区租赁项目', '上海浦东车辆租赁', '广州天河运营项目', '深圳南山首期项目']; + var customers = ['某某科技有限公司', '某某物流有限公司', '某某制造有限公司', '某某出行服务公司']; + for (var i = 1; i <= 28; i++) { + var status = statuses[i % 3]; + var approvalStatus = approvalStatuses[i % 6]; + var firstPayable = 15000 + i * 800; + var paid = 0; + var discount = 0; + if (status === 'paid') { + paid = firstPayable; + discount = i % 4 === 0 ? 200 : 0; + } else if (status === 'partial') { + paid = Math.floor(firstPayable * 0.5); + discount = i % 5 === 0 ? 100 : 0; + } + var unpaid = Math.max(0, firstPayable - paid - discount); + var remark = status !== 'unpaid' ? '已上传付款凭证,备注编号' + i : '-'; + var discountReason = discount > 0 ? (i % 4 === 0 ? '长期合作客户优惠' : '首期减免') : '-'; + list.push({ + id: 'FP' + i, + paymentStatus: status, + approvalStatus: approvalStatus, + contractCode: contracts[i % contracts.length], + projectName: projects[i % projects.length], + customerName: customers[i % customers.length], + contractEffectiveDate: '2025-' + (String((i % 12) + 1)).padStart(2, '0') + '-' + (String((i % 28) + 1)).padStart(2, '0'), + firstPayableAmount: firstPayable, + paidAmount: paid, + discountAmount: discount, + unpaidAmount: unpaid, + discountReason: discountReason, + remark: remark + }); + } + return list; + }, []); + + // 查看页 mock 数据(按 查看页面需求说明 结构) + var mockBillDetail = useMemo(function() { + return { + startDate: '2025-01-01', + endDate: '2025-01-31', + contractCode: 'HT-ZL-2025-001', + projectName: '北京朝阳区租赁项目', + customerName: '某某科技有限公司', + department: '运营部', + responsible: '张三', + paymentCycle: '先付(付款周期:1个月)', + discountTotal: '200.00', + discountReason: '长期合作客户优惠,首期账单减免部分金额' + }; + }, []); + var mockVehicleList = useMemo(function() { + return [ + { brand: '奔驰', model: 'E300L', plateNo: '京A12345', planDelivery: '2024-12-25', planDeliveryEnd: '2024-12-31', actualDelivery: '2024-12-28', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '8000.00', paidMonthlyRent: '7600.00', serviceFee: '500.00', paidServiceFee: '480.00', deposit: '2000.00', paidDeposit: '2000.00', serviceItems: [{ name: '保养服务', price: '300.00', effectiveDate: '2025-01-01' }, { name: '保险', price: '200.00', effectiveDate: '2025-01-01' }] }, + { brand: '宝马', model: '530Li', plateNo: '京B67890', planDelivery: '2024-12-20', planDeliveryEnd: '2024-12-26', actualDelivery: '2024-12-22', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '7000.00', paidMonthlyRent: '7000.00', serviceFee: '400.00', paidServiceFee: '380.00', deposit: '1500.00', paidDeposit: '1500.00', serviceItems: [{ name: '保养服务', price: '250.00', effectiveDate: '2025-01-01' }] } + ]; + }, []); + // 氢费账单:氢费付款方式、氢气预付款、氢气实付款金额(元,两位小数) + var mockHydrogenData = useMemo(function() { + return { + paymentMethod: '预付', + prepaymentAmount: '3580.00', + paidAmount: '3200.00' + }; + }, []); + var mockViolationData = useMemo(function() { + return { + violationCount: 3, + totalAmount: '650.00', + paidTotalAmount: '600.00', + list: [ + { violationTime: '2025-01-10 08:30:00', plateNo: '京A12345', violationType: '违停', location: '北京市朝阳区xxx路', fineAmount: '200.00', paidFineAmount: '200.00' }, + { violationTime: '2025-01-18 14:20:00', plateNo: '京B67890', violationType: '超速', location: '北京市海淀区xxx大道', fineAmount: '200.00', paidFineAmount: '180.00' }, + { violationTime: '2025-01-25 09:15:00', plateNo: '京A12345', violationType: '闯红灯', location: '北京市东城区xxx路口', fineAmount: '250.00', paidFineAmount: '220.00' } + ] + }; + }, []); + var mockReturnFeeData = useMemo(function() { + return { + totalAmount: '2850.00', + paidTotalAmount: '2700.00', + list: [ + { feeName: '车辆外观损伤费', amount: '800.00', paidAmount: '760.00', photos: ['https://picsum.photos/80/80?random=1', 'https://picsum.photos/80/80?random=2'], attachments: [{ name: '外观损伤说明.pdf' }] }, + { feeName: '轮胎磨损费', amount: '1200.00', paidAmount: '1150.00', photos: ['https://picsum.photos/80/80?random=3'], attachments: [{ name: '轮胎检测报告.pdf' }, { name: '维修单据.pdf' }] }, + { feeName: '内饰清洁费', amount: '450.00', paidAmount: '430.00', photos: ['https://picsum.photos/80/80?random=4', 'https://picsum.photos/80/80?random=5', 'https://picsum.photos/80/80?random=6'], attachments: [] }, + { feeName: '油量补充费', amount: '400.00', paidAmount: '360.00', photos: [], attachments: [{ name: '加油凭证.jpg' }] } + ] + }; + }, []); + var vehicleBillTotals = useMemo(function() { + var list = mockVehicleList || []; + var monthlyRentTotal = 0, paidRentTotal = 0, serviceFeeTotal = 0, paidServiceFeeTotal = 0, depositTotal = 0, paidDepositTotal = 0; + list.forEach(function(v) { + monthlyRentTotal += parseFloat(v.monthlyRent || 0); + paidRentTotal += parseFloat(v.paidMonthlyRent || 0); + serviceFeeTotal += parseFloat(v.serviceFee || 0); + paidServiceFeeTotal += parseFloat(v.paidServiceFee || 0); + depositTotal += parseFloat(v.deposit || 0); + paidDepositTotal += parseFloat(v.paidDeposit || 0); + }); + return { + monthlyRentTotal: monthlyRentTotal.toFixed(2), + paidRentTotal: paidRentTotal.toFixed(2), + serviceFeeTotal: serviceFeeTotal.toFixed(2), + paidServiceFeeTotal: paidServiceFeeTotal.toFixed(2), + depositTotal: depositTotal.toFixed(2), + paidDepositTotal: paidDepositTotal.toFixed(2) + }; + }, [mockVehicleList]); + var vehicleTotals = useMemo(function() { + var monthlyRentTotal = parseFloat(vehicleBillTotals.monthlyRentTotal || 0); + var serviceFeeTotal = parseFloat(vehicleBillTotals.serviceFeeTotal || 0); + var depositTotal = parseFloat(vehicleBillTotals.depositTotal || 0); + var hydrogenTotal = parseFloat(mockHydrogenData.prepaymentAmount || 0); + var violationTotal = parseFloat(mockViolationData.totalAmount || 0); + var returnFeeTotal = parseFloat(mockReturnFeeData.totalAmount || 0); + var payableTotal = monthlyRentTotal + serviceFeeTotal + depositTotal + hydrogenTotal + violationTotal + returnFeeTotal; + var paidTotal = 0; + (mockVehicleList || []).forEach(function(v) { + paidTotal += parseFloat(v.paidMonthlyRent || 0) + parseFloat(v.paidServiceFee || 0) + parseFloat(v.paidDeposit || 0); + }); + var discountTotal = parseFloat(mockBillDetail.discountTotal || 0); + var unpaidTotal = payableTotal - paidTotal - discountTotal; + if (unpaidTotal < 0) unpaidTotal = 0; + return { + payableTotal: payableTotal.toFixed(2), + paidTotal: paidTotal.toFixed(2), + unpaidTotal: unpaidTotal.toFixed(2) + }; + }, [vehicleBillTotals, mockHydrogenData, mockViolationData, mockReturnFeeData, mockVehicleList, mockBillDetail]); + + // 筛选后的列表(使用已生效条件,点击查询后才与选择器联动) + var filteredList = useMemo(function() { + var list = mockList; + var af = appliedFilter; + // 合同编码选择器:与列表 contractCode 精确匹配 + if (af.contract && af.contract.trim()) { + var cf = af.contract.trim(); + list = list.filter(function(item) { + return (item.contractCode || '') === cf; + }); + } + // 项目名称选择器:与列表 projectName 精确匹配 + if (af.project && af.project.trim()) { + var pf = af.project.trim(); + list = list.filter(function(item) { + return (item.projectName || '') === pf; + }); + } + // 客户名称选择器:与列表 customerName 精确匹配 + if (af.customer && af.customer.trim()) { + var cuf = af.customer.trim(); + list = list.filter(function(item) { + return (item.customerName || '') === cuf; + }); + } + if (af.status && af.status.length > 0) { + list = list.filter(function(item) { + return af.status.indexOf(item.paymentStatus) >= 0; + }); + } + return list; + }, [mockList, appliedFilter]); + + var totalCount = filteredList.length; + var totalPages = Math.ceil(totalCount / pageSize) || 1; + var paginatedList = useMemo(function() { + var start = (currentPage - 1) * pageSize; + return filteredList.slice(start, start + pageSize); + }, [filteredList, currentPage, pageSize]); + + var getStatusText = function(status) { + if (status === 'paid') return '已付款'; + if (status === 'partial') return '部分付款'; + return '未付款'; + }; + var getStatusColor = function(status) { + if (status === 'paid') return '#52c41a'; + if (status === 'partial') return '#faad14'; + return '#ff4d4f'; + }; + + var formatAmount = function(num) { + var n = typeof num === 'number' ? num : parseFloat(num); + if (isNaN(n)) return '0.00'; + return n.toFixed(2); + }; + + var handleQuery = useCallback(function() { + setAppliedFilter({ + contract: contractInput, + project: projectInput, + customer: customerInput, + status: statusInput + }); + setCurrentPage(1); + }, [contractInput, projectInput, customerInput, statusInput]); + var handleReset = useCallback(function() { + setContractInput(''); + setProjectInput(''); + setCustomerInput(''); + setStatusInput([]); + setAppliedFilter({ contract: '', project: '', customer: '', status: [] }); + setCurrentPage(1); + }, []); + var getApprovalStatus = useCallback(function(row) { + return approvalStatusMap[row.id] != null ? approvalStatusMap[row.id] : row.approvalStatus; + }, [approvalStatusMap]); + var getApprovalStatusText = function(s) { + if (s === 'draft') return '待提交'; + if (s === 'pending' || s === 'auditing') return '审核中'; + if (s === 'completed') return '审核完成'; + if (s === 'withdrawn') return '撤回'; + if (s === 'rejected') return '审核驳回'; + return s || '-'; + }; + var handleView = useCallback(function(id) { + setViewingBillId(id); + setDetailMode('view'); + setCurrentView('detail'); + }, []); + var handlePayment = useCallback(function(id) { + setViewingBillId(id); + setDetailMode('payment'); + var vl = mockVehicleList.map(function(v) { + return { paidMonthlyRent: v.paidMonthlyRent || '', paidServiceFee: v.paidServiceFee || '', paidDeposit: v.paidDeposit || '' }; + }); + var hl = []; + var viol = mockViolationData.list.map(function(item) { return { paidFineAmount: item.paidFineAmount || '' }; }); + var rfl = mockReturnFeeData.list.map(function(item) { return { paidAmount: item.paidAmount || '' }; }); + setPaymentForm({ + discountAmount: mockBillDetail.discountTotal || '', + discountReason: mockBillDetail.discountReason || '', + hydrogenPaidAmount: mockHydrogenData.paidAmount || '', + vehicleList: vl, + hydrogenList: hl, + violationList: viol, + returnFeeList: rfl + }); + setCurrentView('detail'); + }, []); + var handleBackToList = useCallback(function() { + setCurrentView('list'); + setDetailMode('view'); + setViewingBillId(null); + }, []); + var handleSave = useCallback(function() { + if (viewingBillId) { + setApprovalStatusMap(function(prev) { + var next = {}; + for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; } + next[viewingBillId] = 'draft'; + return next; + }); + } + alert('保存成功'); + handleBackToList(); + }, [viewingBillId, handleBackToList]); + var handleWithdraw = useCallback(function(rowId) { + setWithdrawConfirmId(rowId); + }, []); + var handleWithdrawConfirm = useCallback(function() { + if (withdrawConfirmId) { + setApprovalStatusMap(function(prev) { + var next = {}; + for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; } + next[withdrawConfirmId] = 'draft'; + return next; + }); + setWithdrawConfirmId(null); + alert('已撤回,可重新编辑后提交'); + } + }, [withdrawConfirmId]); + var handlePaymentFormChange = useCallback(function(section, index, field, value) { + setPaymentForm(function(prev) { + var next = { + discountAmount: prev.discountAmount, + discountReason: prev.discountReason, + hydrogenPaidAmount: prev.hydrogenPaidAmount, + vehicleList: prev.vehicleList.slice(), + hydrogenList: prev.hydrogenList.slice(), + violationList: prev.violationList.slice(), + returnFeeList: prev.returnFeeList.slice() + }; + if (section === 'bill') { + if (field === 'discountAmount') next.discountAmount = value; + if (field === 'discountReason') next.discountReason = value; + if (field === 'hydrogenPaidAmount') next.hydrogenPaidAmount = value; + } else if (section === 'vehicle' && index >= 0 && index < next.vehicleList.length) { + next.vehicleList[index] = Object.assign({}, next.vehicleList[index]); + next.vehicleList[index][field] = value; + } else if (section === 'hydrogen' && index >= 0 && index < next.hydrogenList.length) { + next.hydrogenList[index] = Object.assign({}, next.hydrogenList[index]); + next.hydrogenList[index][field] = value; + } else if (section === 'violation' && index >= 0 && index < next.violationList.length) { + next.violationList[index] = Object.assign({}, next.violationList[index]); + next.violationList[index][field] = value; + } else if (section === 'returnFee' && index >= 0 && index < next.returnFeeList.length) { + next.returnFeeList[index] = Object.assign({}, next.returnFeeList[index]); + next.returnFeeList[index][field] = value; + } + return next; + }); + }, []); + var handleSubmit = useCallback(function() { + var errors = []; + if (!paymentForm.discountAmount || String(paymentForm.discountAmount).trim() === '') errors.push('减免金额'); + if (!paymentForm.discountReason || String(paymentForm.discountReason).trim() === '') errors.push('减免原因'); + if (!paymentForm.hydrogenPaidAmount || String(paymentForm.hydrogenPaidAmount).trim() === '') errors.push('氢气实付款金额'); + paymentForm.vehicleList.forEach(function(v, i) { + if (!v.paidMonthlyRent || String(v.paidMonthlyRent).trim() === '') errors.push('车辆账单-实付月租金(第' + (i + 1) + '行)'); + if (!v.paidServiceFee || String(v.paidServiceFee).trim() === '') errors.push('车辆账单-实付服务费(第' + (i + 1) + '行)'); + if (!v.paidDeposit || String(v.paidDeposit).trim() === '') errors.push('车辆账单-实付保证金(第' + (i + 1) + '行)'); + }); + paymentForm.violationList.forEach(function(v, i) { + if (!v.paidFineAmount || String(v.paidFineAmount).trim() === '') errors.push('违章费用-实付罚款金额(第' + (i + 1) + '行)'); + }); + paymentForm.returnFeeList.forEach(function(r, i) { + if (!r.paidAmount || String(r.paidAmount).trim() === '') errors.push('还车费用-实付金额(第' + (i + 1) + '行)'); + }); + if (errors.length > 0) { + alert('请填写必填项:' + errors.join('、')); + return; + } + if (viewingBillId) { + setApprovalStatusMap(function(prev) { + var next = {}; + for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; } + next[viewingBillId] = 'pending'; + return next; + }); + } + alert('提交成功'); + handleBackToList(); + }, [paymentForm, handleBackToList, viewingBillId]); + + // 样式 + var styles = { + page: { + padding: '24px', + backgroundColor: '#f5f5f5', + minHeight: '100vh', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' + }, + breadcrumb: { + marginBottom: '16px', + fontSize: '14px', + color: '#666' + }, + breadcrumbSep: { color: '#bfbfbf', margin: '0 8px' }, + content: { + backgroundColor: '#fff', + borderRadius: '8px', + padding: '24px', + boxShadow: '0 1px 2px rgba(0,0,0,0.03)', + overflowX: 'auto' + }, + filterRow: { + display: 'flex', + flexWrap: 'nowrap', + gap: '16px', + marginBottom: '20px', + alignItems: 'center', + overflowX: 'auto' + }, + filterItem: { + display: 'flex', + alignItems: 'center', + gap: '8px' + }, + label: { fontSize: '14px', color: '#333', minWidth: '80px' }, + input: { + padding: '8px 12px', + border: '1px solid #d9d9d9', + borderRadius: '4px', + fontSize: '14px', + width: '200px' + }, + select: { + padding: '8px 12px', + border: '1px solid #d9d9d9', + borderRadius: '4px', + fontSize: '14px', + width: '200px' + }, + filterSelectWrap: { position: 'relative', width: '200px' }, + filterDropdown: { + position: 'absolute', + top: '100%', + left: 0, + right: 0, + marginTop: '2px', + maxHeight: '200px', + overflowY: 'auto', + backgroundColor: '#fff', + border: '1px solid #d9d9d9', + borderRadius: '4px', + boxShadow: '0 2px 8px rgba(0,0,0,0.15)', + zIndex: 10 + }, + filterDropdownItem: { + padding: '8px 12px', + fontSize: '14px', + cursor: 'pointer', + borderBottom: '1px solid #f0f0f0' + }, + selectMultiple: { + padding: '8px 12px', + border: '1px solid #d9d9d9', + borderRadius: '4px', + fontSize: '14px', + minWidth: '200px', + minHeight: '36px' + }, + statusMultiWrap: { position: 'relative', display: 'inline-block' }, + statusMultiDisplay: { + padding: '8px 12px', + border: '1px solid #d9d9d9', + borderRadius: '4px', + fontSize: '14px', + width: '200px', + backgroundColor: '#fff', + cursor: 'pointer', + minHeight: '36px', + boxSizing: 'border-box' + }, + statusDropdownPanel: { + position: 'absolute', + left: 0, + top: '100%', + marginTop: '2px', + width: '100%', + maxHeight: '220px', + overflowY: 'auto', + backgroundColor: '#fff', + border: '1px solid #d9d9d9', + borderRadius: '4px', + boxShadow: '0 2px 8px rgba(0,0,0,0.15)', + zIndex: 10 + }, + statusDropdownOption: { + padding: '8px 12px', + fontSize: '14px', + cursor: 'pointer', + borderBottom: '1px solid #f0f0f0' + }, + table: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' }, + th: { + textAlign: 'left', + padding: '12px 16px', + backgroundColor: '#fafafa', + borderBottom: '1px solid #f0f0f0', + fontWeight: 600, + color: '#333', + whiteSpace: 'nowrap' + }, + td: { + padding: '12px 16px', + borderBottom: '1px solid #f0f0f0', + color: '#333', + whiteSpace: 'nowrap' + }, + actionColTh: { + position: 'sticky', + right: 0, + backgroundColor: '#fafafa', + boxShadow: '-2px 0 4px rgba(0,0,0,0.06)', + zIndex: 1 + }, + actionColTd: { + position: 'sticky', + right: 0, + backgroundColor: '#fff', + boxShadow: '-2px 0 4px rgba(0,0,0,0.06)', + zIndex: 1 + }, + actionBtn: { + padding: '4px 12px', + marginRight: '8px', + borderRadius: '4px', + border: 'none', + cursor: 'pointer', + fontSize: '13px' + }, + viewBtn: { backgroundColor: '#e6f7ff', color: '#1890ff' }, + payBtn: { backgroundColor: '#f6ffed', color: '#52c41a' }, + withdrawBtn: { backgroundColor: '#fff7e6', color: '#fa8c16' }, + pagination: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginTop: '20px', + flexWrap: 'wrap', + gap: '12px' + }, + pageSizeSelect: { + padding: '6px 10px', + border: '1px solid #d9d9d9', + borderRadius: '4px', + fontSize: '13px' + }, + pageInfo: { fontSize: '14px', color: '#666' }, + pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px', flexWrap: 'wrap', gap: '12px' }, + requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: '14px', textDecoration: 'none' }, + requirementModalBody: { maxHeight: '70vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: '14px', lineHeight: 1.6, color: '#333', padding: '0 4px' }, + queryBtn: { + padding: '8px 20px', + backgroundColor: '#1890ff', + color: '#fff', + border: 'none', + borderRadius: '4px', + cursor: 'pointer', + fontSize: '14px' + }, + resetBtn: { + padding: '8px 20px', + backgroundColor: '#fff', + color: '#666', + border: '1px solid #d9d9d9', + borderRadius: '4px', + cursor: 'pointer', + fontSize: '14px' + }, + // 查看页样式(按 查看页面需求说明) + detailPage: { padding: '24px', backgroundColor: '#f5f5f5', minHeight: '100vh', width: '100%', boxSizing: 'border-box' }, + backBtn: { marginBottom: '16px', padding: '8px 16px', backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' }, + detailContent: { display: 'flex', flexDirection: 'column', gap: '24px', width: '100%' }, + detailCard: { backgroundColor: '#fff', borderRadius: '8px', padding: '24px', boxShadow: '0 1px 2px rgba(0,0,0,0.03)', width: '100%', boxSizing: 'border-box' }, + detailCardTitle: { fontSize: '16px', fontWeight: 600, marginBottom: '16px', paddingBottom: '12px', borderBottom: '1px solid #f0f0f0' }, + detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: '12px', fontSize: '14px' }, + detailItem: {}, + detailItemInline: { display: 'flex', alignItems: 'center' }, + detailItemInputRight: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, + detailLabel: { color: '#666', marginRight: '8px' }, + amountInputWrap: { display: 'flex', alignItems: 'center', gap: '8px' }, + amountInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '14px', width: '120px' }, + amountSuffix: { color: '#666', fontSize: '14px' }, + detailTextarea: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '14px', width: '100%', minWidth: '200px', minHeight: '60px', resize: 'vertical' }, + vehicleTable: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' }, + hydrogenSummary: { display: 'flex', gap: '48px', marginBottom: '16px', fontSize: '14px' }, + hydrogenSummaryItem: { color: '#333' }, + photoThumb: { width: '40px', height: '40px', objectFit: 'cover', borderRadius: '4px', cursor: 'pointer', marginRight: '4px', verticalAlign: 'middle' }, + attachmentLink: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none', marginRight: '8px', fontSize: '14px' }, + amountLink: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, + photoViewerOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.85)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1001 }, + photoViewerContent: { position: 'relative', textAlign: 'center' }, + photoViewerImg: { maxWidth: '90vw', maxHeight: '85vh', objectFit: 'contain' }, + photoViewerBtn: { position: 'absolute', top: '50%', marginTop: '-20px', padding: '10px 16px', backgroundColor: 'rgba(255,255,255,0.9)', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' }, + photoViewerPrev: { left: '20px' }, + photoViewerNext: { right: '20px' }, + photoViewerClose: { position: 'absolute', top: '-40px', right: '0', padding: '8px 16px', backgroundColor: 'rgba(255,255,255,0.9)', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' }, + photoViewerCounter: { color: '#fff', marginTop: '16px', fontSize: '14px' }, + popover: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }, + popoverCard: { backgroundColor: '#fff', borderRadius: '8px', padding: '24px', maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' }, + popoverHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px', whiteSpace: 'nowrap' }, + popoverTitle: { fontSize: '16px', fontWeight: 600, whiteSpace: 'nowrap' }, + popoverClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: '4px', cursor: 'pointer' }, + popoverTable: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' }, + popoverTh: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' }, + popoverTd: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333', whiteSpace: 'nowrap' }, + modalFooter: { marginTop: '24px', textAlign: 'right', display: 'flex', justifyContent: 'center', gap: '12px' } + }; + + // 查看页:按 查看页面需求说明 渲染(租赁账单详情) + if (currentView === 'detail') { + return ( + React.createElement('div', { style: styles.detailPage }, + React.createElement('div', { style: Object.assign({}, styles.breadcrumb, { marginBottom: '16px' }) }, + React.createElement('span', null, '财务管理'), + React.createElement('span', { style: styles.breadcrumbSep }, '>'), + React.createElement('span', null, '提车首付款'), + React.createElement('span', { style: styles.breadcrumbSep }, '>'), + React.createElement('span', null, detailMode === 'payment' ? '收费' : '查看') + ), + React.createElement('div', { style: styles.detailContent }, + React.createElement('div', { style: styles.detailCard }, + React.createElement('div', { style: styles.detailCardTitle }, '账单信息'), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '账单开始日期:'), React.createElement('span', null, mockBillDetail.startDate)), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '账单结束日期:'), React.createElement('span', null, mockBillDetail.endDate)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '合同编码:'), React.createElement('span', null, mockBillDetail.contractCode)), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '项目名称:'), React.createElement('span', null, mockBillDetail.projectName)), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '客户名称:'), React.createElement('span', null, mockBillDetail.customerName)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '业务部门:'), React.createElement('span', null, mockBillDetail.department)), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '业务负责人:'), React.createElement('span', null, mockBillDetail.responsible)), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '付款周期:'), React.createElement('span', null, mockBillDetail.paymentCycle)) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '首期应付金额:'), React.createElement('span', { style: { color: '#1890ff', fontWeight: 600 } }, vehicleTotals.payableTotal + ' 元')), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '实付款总额:'), React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, vehicleTotals.paidTotal + ' 元')), + React.createElement('div', { style: Object.assign({}, styles.detailItem, styles.detailItemInline) }, React.createElement('span', { style: styles.detailLabel }, '减免金额:'), detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: paymentForm.discountAmount, onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountAmount', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : React.createElement('span', { style: { fontWeight: 600 } }, mockBillDetail.discountTotal + ' 元')), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '未付款总额:'), React.createElement('span', { style: { color: '#ff4d4f', fontWeight: 600 } }, vehicleTotals.unpaidTotal + ' 元')) + ), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: Object.assign({}, styles.detailItem, { gridColumn: '1 / -1' }) }, React.createElement('span', { style: styles.detailLabel }, '减免原因:'), detailMode === 'payment' ? React.createElement('textarea', { style: styles.detailTextarea, value: paymentForm.discountReason, onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountReason', e.target.value); }, placeholder: '请输入减免原因' }) : React.createElement('span', null, mockBillDetail.discountReason || '-')) + ) + ), + React.createElement('div', { style: styles.detailCard }, + React.createElement('div', { style: styles.detailCardTitle }, '车辆账单'), + React.createElement('table', { style: styles.vehicleTable }, + React.createElement('thead', null, React.createElement('tr', null, + React.createElement('th', { style: styles.th }, '品牌'), React.createElement('th', { style: styles.th }, '型号'), React.createElement('th', { style: styles.th }, '车牌号'), React.createElement('th', { style: styles.th }, '计划交车日期'), React.createElement('th', { style: styles.th }, '实际交车日期'), React.createElement('th', { style: styles.th }, '账单开始日期'), React.createElement('th', { style: styles.th }, '计费结束日期'), React.createElement('th', { style: styles.th }, '车辆月租金'), React.createElement('th', { style: styles.th }, '实付月租金'), React.createElement('th', { style: styles.th }, '服务费'), React.createElement('th', { style: styles.th }, '实付服务费'), React.createElement('th', { style: styles.th }, '保证金'), React.createElement('th', { style: styles.th }, '实付保证金') + )), + React.createElement('tbody', null, mockVehicleList.map(function(v, idx) { + var pf = paymentForm.vehicleList[idx] || {}; + return React.createElement('tr', { key: v.plateNo }, + React.createElement('td', { style: styles.td }, v.brand), React.createElement('td', { style: styles.td }, v.model), React.createElement('td', { style: styles.td }, v.plateNo), + React.createElement('td', { style: styles.td }, (v.planDelivery && v.planDeliveryEnd) ? (v.planDelivery + '至' + v.planDeliveryEnd) : (v.planDelivery || '未设置')), React.createElement('td', { style: styles.td }, v.actualDelivery || '未交车'), React.createElement('td', { style: styles.td }, v.billStart || '未设置'), React.createElement('td', { style: styles.td }, v.billEnd || '未设置'), + React.createElement('td', { style: styles.td }, v.monthlyRent), + React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidMonthlyRent || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidMonthlyRent', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidMonthlyRent || 0)).toFixed(2)), + React.createElement('td', { style: styles.td }, detailMode === 'payment' ? v.serviceFee : React.createElement('span', { style: styles.amountLink, onClick: function() { setPopover({ type: 'service', data: v.serviceItems }); } }, v.serviceFee)), + React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidServiceFee || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidServiceFee', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidServiceFee || 0)).toFixed(2)), + React.createElement('td', { style: styles.td }, v.deposit), + React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidDeposit || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidDeposit', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidDeposit || 0)).toFixed(2)) + ); + })), + React.createElement('tfoot', null, React.createElement('tr', { style: { backgroundColor: '#fafafa', fontWeight: 600 } }, React.createElement('td', { style: styles.td, colSpan: 7 }, '总计'), React.createElement('td', { style: styles.td }, vehicleBillTotals.monthlyRentTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidRentTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.serviceFeeTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidServiceFeeTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.depositTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidDepositTotal))) + ) + ), + React.createElement('div', { style: styles.detailCard }, + React.createElement('div', { style: styles.detailCardTitle }, '氢费账单'), + React.createElement('div', { style: styles.detailRow }, + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '氢费付款方式:'), React.createElement('span', null, mockHydrogenData.paymentMethod || '-')), + React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '氢气预付款:'), React.createElement('span', null, (parseFloat(mockHydrogenData.prepaymentAmount || 0)).toFixed(2) + ' 元')), + React.createElement('div', { style: Object.assign({}, styles.detailItem, styles.detailItemInline) }, React.createElement('span', { style: styles.detailLabel }, '氢气实付款金额:'), detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: paymentForm.hydrogenPaidAmount, onChange: function(e) { handlePaymentFormChange('bill', -1, 'hydrogenPaidAmount', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : React.createElement('span', null, (parseFloat(mockHydrogenData.paidAmount || 0)).toFixed(2) + ' 元')) + ) + ) + ), + React.createElement('div', { style: { marginTop: '24px', textAlign: 'center', display: 'flex', justifyContent: 'center', gap: '16px' } }, + detailMode === 'payment' && React.createElement('button', { type: 'button', style: Object.assign({}, styles.backBtn, { backgroundColor: '#1890ff', color: '#fff', border: 'none' }), onClick: handleSubmit }, '提交审核'), + detailMode === 'payment' && React.createElement('button', { type: 'button', style: styles.backBtn, onClick: handleSave }, '保存'), + React.createElement('button', { type: 'button', style: styles.backBtn, onClick: handleBackToList }, '取消') + ), + photoViewer.visible && photoViewer.photos.length > 0 && React.createElement('div', { style: styles.photoViewerOverlay, onClick: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); } }, + React.createElement('div', { style: styles.photoViewerContent, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerClose), onClick: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); } }, '关闭'), + photoViewer.currentIndex > 0 ? React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerPrev), onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex - 1 }); } }, '上一张') : null, + React.createElement('img', { style: styles.photoViewerImg, src: photoViewer.photos[photoViewer.currentIndex].replace('80/80', '600/600'), alt: '' }), + photoViewer.currentIndex < photoViewer.photos.length - 1 ? React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerNext), onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex + 1 }); } }, '下一张') : null, + React.createElement('div', { style: styles.photoViewerCounter }, (photoViewer.currentIndex + 1) + ' / ' + photoViewer.photos.length) + ) + ), + (popover.type === 'service' && React.createElement('div', { style: styles.popover, onClick: function() { setPopover({ type: null, data: null }); } }, + React.createElement('div', { style: styles.popoverCard, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.popoverHeader }, React.createElement('span', { style: styles.popoverTitle }, '服务费明细'), React.createElement('button', { style: styles.popoverClose, onClick: function() { setPopover({ type: null, data: null }); } }, '关闭')), + React.createElement('table', { style: styles.table }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.th }, '服务项'), React.createElement('th', { style: styles.th }, '价格'), React.createElement('th', { style: styles.th }, '服务生效日期'))), React.createElement('tbody', null, popover.data ? popover.data.map(function(s, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.td }, s.name), React.createElement('td', { style: styles.td }, s.price), React.createElement('td', { style: styles.td }, s.effectiveDate)); }) : null)) + ) + )) + ) + ); + } + + + return ( + React.createElement('div', { style: styles.page }, + React.createElement('div', { style: styles.pageHeader }, + React.createElement('div', { style: styles.breadcrumb }, + React.createElement('span', null, '财务管理'), + React.createElement('span', { style: styles.breadcrumbSep }, '>'), + React.createElement('span', null, '提车首付款') + ), + React.createElement('a', { style: styles.requirementLink, onClick: function() { setRequirementDetailVisible(true); }, role: 'button', tabIndex: 0, onKeyDown: function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setRequirementDetailVisible(true); } } }, '查看需求明细') + ), + React.createElement('div', { style: styles.content }, + // 筛选:合同编码、项目名称、客户名称(选择器+输入搜索)、付款状态(多选);右侧 查询、重置 + React.createElement('div', { style: styles.filterRow }, + React.createElement('div', { style: styles.filterItem }, + React.createElement('span', { style: styles.label }, '合同编码:'), + React.createElement('div', { style: styles.filterSelectWrap }, + React.createElement('input', { + style: styles.input, + placeholder: '请输入合同编码', + value: contractInput, + onChange: function(e) { setContractInput(e.target.value); }, + onFocus: function() { setFilterDropdownOpen('contract'); }, + onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); } + }), + filterDropdownOpen === 'contract' && React.createElement('div', { style: styles.filterDropdown }, + contractOptions.filter(function(opt) { + var kw = (contractInput || '').toLowerCase(); + if (!kw) return true; + return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0; + }).map(function(opt) { + return React.createElement('div', { + key: opt.value, + style: styles.filterDropdownItem, + onMouseDown: function(e) { e.preventDefault(); setContractInput(opt.value); setFilterDropdownOpen(null); } + }, opt.label); + }) + ) + ) + ), + React.createElement('div', { style: styles.filterItem }, + React.createElement('span', { style: styles.label }, '项目名称:'), + React.createElement('div', { style: styles.filterSelectWrap }, + React.createElement('input', { + style: styles.input, + placeholder: '请输入项目名称', + value: projectInput, + onChange: function(e) { setProjectInput(e.target.value); }, + onFocus: function() { setFilterDropdownOpen('project'); }, + onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); } + }), + filterDropdownOpen === 'project' && React.createElement('div', { style: styles.filterDropdown }, + projectOptions.filter(function(opt) { + var kw = (projectInput || '').toLowerCase(); + if (!kw) return true; + return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0; + }).map(function(opt) { + return React.createElement('div', { + key: opt.value, + style: styles.filterDropdownItem, + onMouseDown: function(e) { e.preventDefault(); setProjectInput(opt.value); setFilterDropdownOpen(null); } + }, opt.label); + }) + ) + ) + ), + React.createElement('div', { style: styles.filterItem }, + React.createElement('span', { style: styles.label }, '客户名称:'), + React.createElement('div', { style: styles.filterSelectWrap }, + React.createElement('input', { + style: styles.input, + placeholder: '请输入客户名称', + value: customerInput, + onChange: function(e) { setCustomerInput(e.target.value); }, + onFocus: function() { setFilterDropdownOpen('customer'); }, + onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); } + }), + filterDropdownOpen === 'customer' && React.createElement('div', { style: styles.filterDropdown }, + customerOptions.filter(function(opt) { + var kw = (customerInput || '').toLowerCase(); + if (!kw) return true; + return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0; + }).map(function(opt) { + return React.createElement('div', { + key: opt.value, + style: styles.filterDropdownItem, + onMouseDown: function(e) { e.preventDefault(); setCustomerInput(opt.value); setFilterDropdownOpen(null); } + }, opt.label); + }) + ) + ) + ), + React.createElement('div', { style: styles.filterItem }, + React.createElement('span', { style: styles.label }, '付款状态:'), + React.createElement('div', { + style: styles.statusMultiWrap, + tabIndex: 0, + onBlur: function() { setTimeout(function() { setStatusDropdownOpen(false); }, 200); } + }, + React.createElement('div', { + style: styles.statusMultiDisplay, + onClick: function() { setStatusDropdownOpen(!statusDropdownOpen); } + }, statusInput.length === 0 ? '全部' : statusInput.map(function(s) { return getStatusText(s); }).join('、')), + statusDropdownOpen ? React.createElement('div', { style: styles.statusDropdownPanel }, + ['partial', 'unpaid', 'paid'].map(function(s) { + var checked = statusInput.indexOf(s) >= 0; + return React.createElement('div', { + key: s, + style: styles.statusDropdownOption, + onMouseDown: function(e) { + e.preventDefault(); + var idx = statusInput.indexOf(s); + var next = statusInput.slice(); + if (idx >= 0) next.splice(idx, 1); + else next.push(s); + setStatusInput(next); + } + }, React.createElement('label', { style: { cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '6px' } }, + React.createElement('input', { type: 'checkbox', checked: checked, readOnly: true }), + getStatusText(s) + )); + }) + ) : null + ) + ), + React.createElement('div', { style: { display: 'flex', gap: '8px', marginLeft: 'auto' } }, + React.createElement('button', { type: 'button', style: styles.queryBtn, onClick: handleQuery }, '查询'), + React.createElement('button', { type: 'button', style: styles.resetBtn, onClick: handleReset }, '重置') + ) + ), + // 列表:审核状态、付款状态、合同编码、项目名称、客户名称、合同生效日期、首期应付金额、实付款总额、减免金额、未付款总额、减免原因、操作(固定右侧) + React.createElement('table', { style: styles.table }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.th }, '审核状态'), + React.createElement('th', { style: styles.th }, '付款状态'), + React.createElement('th', { style: styles.th }, '合同编码'), + React.createElement('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.actionColTh) }, '操作') + ) + ), + React.createElement('tbody', null, + paginatedList.map(function(row) { + var approvalStatus = getApprovalStatus(row); + var showPay = approvalStatus === 'draft' || approvalStatus === 'withdrawn' || approvalStatus === 'rejected'; + var showWithdraw = approvalStatus === 'auditing' || approvalStatus === 'pending'; + return React.createElement('tr', { key: row.id }, + React.createElement('td', { style: styles.td }, getApprovalStatusText(approvalStatus)), + React.createElement('td', { style: styles.td }, + React.createElement('span', { style: { color: getStatusColor(row.paymentStatus) } }, getStatusText(row.paymentStatus)) + ), + React.createElement('td', { style: styles.td }, row.contractCode), + React.createElement('td', { style: styles.td }, row.projectName), + React.createElement('td', { style: styles.td }, row.customerName), + React.createElement('td', { style: styles.td }, row.contractEffectiveDate), + React.createElement('td', { style: styles.td }, formatAmount(row.firstPayableAmount)), + React.createElement('td', { style: styles.td }, formatAmount(row.paidAmount)), + React.createElement('td', { style: styles.td }, formatAmount(row.discountAmount)), + React.createElement('td', { style: styles.td }, formatAmount(row.unpaidAmount)), + React.createElement('td', { style: styles.td }, row.discountReason || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.actionColTd) }, + React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.actionBtn, styles.viewBtn), + onClick: function() { handleView(row.id); } + }, '查看'), + showPay && React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.actionBtn, styles.payBtn), + onClick: function() { handlePayment(row.id); } + }, '收费'), + showWithdraw && React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.actionBtn, styles.withdrawBtn), + onClick: function() { handleWithdraw(row.id); } + }, '撤回') + ) + ); + }) + ) + ), + // 分页:支持选择单页数据条数 + React.createElement('div', { style: styles.pagination }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, + React.createElement('span', { style: styles.pageInfo }, '共 ' + totalCount + ' 条'), + React.createElement('select', { + style: styles.pageSizeSelect, + value: pageSize, + onChange: function(e) { + setPageSize(Number(e.target.value)); + setCurrentPage(1); + } + }, + React.createElement('option', { value: 10 }, '10 条/页'), + React.createElement('option', { value: 20 }, '20 条/页'), + React.createElement('option', { value: 50 }, '50 条/页') + ) + ), + React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center' } }, + React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.actionBtn, { padding: '6px 12px' }), + disabled: currentPage <= 1, + onClick: function() { setCurrentPage(currentPage - 1); } + }, '上一页'), + React.createElement('span', { style: styles.pageInfo }, currentPage + ' / ' + totalPages), + React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.actionBtn, { padding: '6px 12px' }), + disabled: currentPage >= totalPages, + onClick: function() { setCurrentPage(currentPage + 1); } + }, '下一页') + ) + ), + withdrawConfirmId && React.createElement('div', { style: styles.popover, onClick: function() { setWithdrawConfirmId(null); } }, + React.createElement('div', { style: styles.popoverCard, onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.popoverTitle }, '确认撤回?'), + React.createElement('p', { style: { margin: '16px 0', fontSize: '14px', color: '#666' } }, '确认后将撤回该数据,审核状态变为撤回。'), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { type: 'button', style: styles.backBtn, onClick: function() { setWithdrawConfirmId(null); } }, '取消'), + React.createElement('button', { type: 'button', style: Object.assign({}, styles.backBtn, { backgroundColor: '#fa8c16', color: '#fff', border: 'none' }), onClick: handleWithdrawConfirm }, '确认') + ) + ) + ), + requirementDetailVisible && React.createElement('div', { style: styles.popover, onClick: function() { setRequirementDetailVisible(false); } }, + React.createElement('div', { style: Object.assign({}, styles.popoverCard, { maxWidth: '720px', width: '90%' }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.popoverHeader }, + React.createElement('span', { style: styles.popoverTitle }, '需求明细'), + React.createElement('button', { style: styles.popoverClose, onClick: function() { setRequirementDetailVisible(false); } }, '关闭') + ), + React.createElement('div', { style: styles.requirementModalBody }, requirementDetailContent) + ) + ) + ) + ) + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/车辆租赁合同/.DS_Store b/web端/车辆租赁合同/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..20c35fe9e4632995b5e740214bfafbbf957e9889 GIT binary patch literal 6148 zcmeHKyGjE=6ulDzZW6&Xf{n1GPf!cRZe3zwWnl_CB{7N`NI=9Fgsto(f8ZBbh@D?! zWvRcQ=gf>@^CF#!+zWTkJnostnXoq_A|}2#ToEaXNB}2hawx_a$LS-M@jbIZW9KN# zvaCuOzCIGI9e+^)-rWN3i}$vMo9o}-_3Qm+EvU`yd^9&YFH(^kIg!qGtKF!#+I6t- zlQB#1(x)|hxPXlA`)wETr*bY=L-8dwTj z^T%(Og(+*0Gq5ktKZV16r8&7?>#VUqPX)&B%<%N-lc1@l3aA3Az^^EPoJ|V~4z*MT zQ~_0>uK>RfA)FX_OdQ%z2Z{`u9M2MIsPipB3_V636Neaq8Ak;=s_{<@q`?8=zOuWbAa#n{!+AKGv+kwYz20aaj7ff?51SOeQnaT`OIks!FPVbh_WQ z9tCM&5EKB2exMkil$DMJPFwlPq%r zav_r|t*AV>3kItBkZ39l7fyxE;77m@0T$E92nH+%)CLL0e>4{Usa(`c>N~05Vf}3F z{(3_@(Z(Zu6Pg*|_Ih!%vU9nzW9{nyq_}swdw(z>vG4hJuDAU<_vb)|t-UolUjw3r z3|aSlV8TS)oXL=MF>rg;#m<&Ezc+EJuX&6t8rb;dZG1nT59(;O>}tRB>p;iv%dCJ+ zVckn_y)VyvlANa^VG@w{*ae*#dn>A~Y`DiDWa4k>N@7a${niXGX6)FUs5bp10#B@< zDDpYmcpr*%3n_i*U9_oBQd5y_ZRJgzb3*JnA$w?GPSb&!b(Z-OecE%mhBWxc<#Y@`e#VNY=d;r56x%q#``+YLC zFKMSuft~>igh|*I7yoV=-?yZGTsfJuEST*#I9hQn;>Egaieu*a15e772WZHVZP8#> zVMn!&A*p2tT*;932U7kzw-mz|Si7rpZ5WnL3cHQWk{d-nj-xLOx0f0g=LQJKv&6mc z=rUbp7VPvZ^zejDN(Rq|z$Bf;jm0}D4cvhFWH=u;hVzid!m!Dwh_5Yy;L5>K#p_E0h+p3oR^JRfYoHs|Iz~eV*4M6@ zRLw9<+oW_ap(jjf z79I`#4Xj2Q(FtpO-)^EBQVgUECJcYxJ7wDi%OcPLTO^5AHuw^1E`wXtCI8=*FN9Z+NN1#M@yf@`6kzXkgJy7FsYSbk2UDGo6ks5d65CcSaZ z-N|}s*X_sz(_7W@(@4IxMg?E?uBC6so_>~7_>!qY;!z;^XnT_yQ>!GiFuGScG#5}F zFzrhDx1Yq-40$nWZ z0h${LNBVA$o$Z`QC-Jfv5|5*(ABr2^5Vmk8BJGfpv*Su;PKly3BgY1Vl`BurYQaf{ zIHK*Z-`Jez^YSjvqd_f+_o&agouAPkWiiRLds|@RRe1TXLIgF(RHWV}fMz1Hw+Mcp zUdX<@!ETSB-sA_|xtX!|D-FM|GM*Bm8RjbH;?7p?mupGUlm`}EE`F~m;OjC^vEOTL zcrv>zINh(C)^a=c*k`?_%d?YMXZHj+jYW9V1e)g#KNJ}{KH0gQx>0l3=V7_kYn95kRm zIU-VipL>T6C4GR`>Swy{EI*Z~@qZ2w)dmGOdmU^$=oH+0O6%ejOVFDgn2E>;m9%j% zD8skN@wWLC9jj?0l}QlnEGo;*jtYfMT(30irlREYL=xvz;U-jGASN*_N+SB)3o=C@ zif(`?n3QJCuz{Lvk`T=fYot6d_n5mF1;8sR+vt?A?VJdU=v3|aW@}+7IsFZ#?;L!Y z6PVtkj~{!%;ngw>bK_h!7DJX&hQIrLar^Wc* ziXkvTmI5A%`CzE8DsWASO*Y#M4>$E)0s~KlQ5$KJr0sJGpkV9~dwWjn`2=kT_T%k1 z;n4&Qlt|*;aGdYQC1aibJPno0zK!Ke6EV;SI%GKA{Bel9RT`eguX?|o!bjBqNoZ*d zM^YxDJP(x)T>Ug~z3)_ZQJ2>_Yz`_y(#)uYf;V^>Zi#H$|(2 z2My5zLt8*0;vC6z)SGpDPHRjrcNi@XciOPAyRx}FpXo7| zE1HbAOz*Yqi>Q0&i(Fp7CDZ?bWCy#*nzF*QjI=nypIaOlkT6v{{ZwLq?z8TCNTAqI8K6LP}sj_s-Vs_ z#+r1N30~0AxpLm+F&xE3Ecs+buT*49qvZ2V`Ma^5u8UT=OefqNoC)aHW;Em|K+XTO zy4wR?yY)zoQ=Vu@?ksEKG5@g~yjqT@{9@vZyYulZUXM95>I9{@IkLel{M&Us=7s+a zQLWtln+h+CFCNC%NB)UuR`#a`pLM5-p^c<$NVcdZa}Jx8j~rTNa@S=;%K7?UvD==i z-7y(x{qi<|C)a!GWW>4pv+eAMK`>R?(_6Y^bpryWo%8C>SHxa`4a*4rUJD1KgpVn0BE!n%( z+kNWxhYV(&)qH1F(#~o`d|15?`$R|TGEGJ#&Q8YKkg@dpjCbhu){A?lQk$c*r<@hr z#ZxOQLPH9K!JV`A(x)z@vaX7cNAq0^fB%~ndwefu_nPu3t7GjiQO9PtX5V$Xx$cQ% z!)fSE{R)je*oukK=E3vUi-bOAneB3B8&Wfl49`T%ZmFgoeYN7BDVLY41<6y9@b* zQs&S5#pzj{@fa|ki^heT-aes3&-k`pp0cx3hR0#l*%B7)vpUJl;zeLBeFC%LjamHW zh=zUX36QndF&YGvxs0mJvD*{@jJ+sR*eyMVaGNXvWpn*w zecLG{0v&ZNkOcfv4PG7fv^CC%$;**$;Mq&f&feEak(aw=^l>FpkE+VS!xi7hJkzNDG!Y_(9Y{z}WkUImK$V)s7>WHO%(@v!rBC4MuWhQZZ3jEl%Yz+y#qq9836#7>R1`;fq0kAr#>D5tQJ- z+0t(1$brNZCE-2)A94g3JlGK=+tuUxP2T63+Gua88rpvZv3cAINoFiLV(>mSu^0A;qv=xy~*^O|_AxhyYk$q%(0l!ae zK)fjJ$j5W&K!3<($_XE)&dW2!AQ4-Sblv;q^hF2cqe9p6Xz=RJ4)nncM=Gf{-OZEvu-IM>@pC3rED{Hyt4bC zo08&(J!Ls2K+P}pY`Y|t(h0Q-Dawquc#3!<65|QJ7v`uQC`kCMgZLmMD#^P9Wez3| z62ve#;}({1=q?vUR`pJBYk`c;rw2I@kwR>;{Xg?VU$tI=BjTQKib zEnFTgy>=d!Zc?8#kPWNRUL+}4=Ns0WV+1R>0Q)1A|mXFpI)6%K=DxoleDk+L@!cef@PN;9sX4c6^ zAd4eV0IRo8!dyKV1SmvHSuChKE z1>ejHFJp~f&bmGmgI+vMdzjj_O4aD_cZoQ?ef)Yy{@e7K%6xC;mfn1CaVP6yw_k4W zdoI}4SzDjeZSR*UGqQFzrH($)7wz4RN96=+=i#wQj@+I-y{tXc1iVSY3VXk$;LR%W zHiUVD`*HEutPDLa#62>J_&-3!qfk}NcxyON(l6rXlNW~7hOz>{a^|y8#YaLd*M}Zo z)gm}6$Y@Cmq_7Dh{4gQ3hpR)W9IJT^bjj=Zo4bSzm<8Mkc)`P6$ai!6&|mwP?)^ji zgt~ml+|pVOTSHYsxjVY(5+{mWJYtps2{N|eH1pRm`43LPT68vVnWO9}7m1dNM9Ile z{a5)iQqqkVNs;Q;eefm^u%yZMC`|8SWXbkYi4*KdnI7Ov@iLQC@Wh!O(y7GAC}Ata zNXdF!7)bkQ)1Lwa=#+svDiB9SG6d+-h~uKbfp>v4;&^;v0+I)Nrd~a5>Mga`#*WHU zYZKDPXwZ|jbQsjt0c^LWh%wyZ3A9ahAG zWq~ZQ2<8&XA<)!kl@$Sui>O&a`sNT#=7fm_)L^;ncb^05r}IxRqTTaZJWimaVDlA_ z9WY8(Ad$6dJ`O{8ell~zi~wF1ig}i83Bpc_JR`c^edc?s~U-a%$2V*3@&# zdp%nEIm8*2Z*K2QtV%!28gEdK=|B_9_S-sa=ioVrn_s3ep77wAn7?1%;%AR=C8kaP z{z;kSPzS1u-P7uCcG4dil~p!$wjLV!g!U-iS$*6d4eR}op*5d+wW8hB`8lhwSFRG)3gp%wFjkr zF6HwCYQ<6R{8AwM`#Y!{L7FXvGMZlqo?J-EKBfZq2Ie~td%TSk+vaV)(C**|@I&YK zIz7vi5V@UwZrQrde?VDIVx4p{QbWa}5hQjvC7*K)nGvPseIF@vD%GV`w!Wpko(bXv zNYJC>qFM$fT(+8gsXaT&u?*93%6=@Z`-GG4G#^AI^o)b6eS|1=Og77M`nk40mswiy zb@_NYt)zg*2OOkaz9q{%<8eO`tK+7WY^xO~CclxoJzEr>o@&xMY+f#Jr81(-!|Xxd{xY2BUrVXNk5Ic($~PaSRZS$j_VKSmGt??w%}6so0QTQ} zEp|75%9Q8i9iw=pRL?T$8(*00y`=BSosS_%ESp<3+W)ZlIS(?NUB;=Ef5qS8rXQe4 z^d?@Eu?1-Lv>IcvwfktEz3YLxEVFEw+M12=!AF-?xX~nvPMOQd>?tRW@_{#ZMW?`D z#s5FHU;P^X?Y&@nhc~&>eo}Zq9Y_}u@c2f1!N1^ zQON?)i?7bJ#4fCdeg)-2MX#L0)sOO)m3?(fjv~P1q0^F(;ZhCl4=)En$Cv)46AXHoE68hy7P@Z&~PqqZRZl`l0 z8rv1O{Utuy$fHt~mdD{0f_yl{1C-mF1U1KV4X&Fy`wC#hCvXVv4{{Q)@ua~#g43es zTJl&}i;m7XeeCJ#Qb2jFP!cRDm63i8(l9QGh!i#O$b2N%JNh_@+;XHde1e18a8TXI ztZn<>uB#F85%BNMSkIJt5)IjEmDmj=SMMZGWLVb&AX7R3pwrr?^of+`($q6Ct6Q!l zqB;8xLT;j(Kr;*@Y!Z48*s$(F>wo{nlh_Y}U+DdhV9&qw0sF7uh4aMh^# zG}HFrmCR?)DizDN!>6Rgz8dwRW3S&sC#}!ha%^ zs;^x96N`Z$=;sE@&df!i?8T*ol1jAaVDdAtdr-o-oArI{( zVWdloBz9F5BYduM#nT8f3qrpK?DnTw1!~?#*|?>>!$M}M;mL!>5fQ9En#MdxmqF68 zsS4&mH}6sTPq7B-dvsJb^3D8*Ygo^cq(aBwMW`y0*2}Vy;rcCRH&krchWY!scArTQ zZVEw=IHcSI&)}<~82i%><|9@}Wc`fy`vc5G*lihz zK&PZi3eSi(F$`!b#HL}DhsFMVh|b1D13O8*-tquZTVB%28g)FJj!*z9n__8jp6E4_Q!1we_2-A+Yc|tbunpJ8b zZ>DWppKZ@#tZ$jPlV8W9hqwB-@g@3MuW{ol3eU%;n6OKW8ODH!t)2q1o|kt?l6FFAMq` zqte8)a8bmrWst=a&7wfslF8m0BesUr>DX=};SHs}(8T6{*b)!WI_)bQ9ZM0p&+ z)nb}t;1ivy)sYWyAzF+X3GcC|Uuo#2My!K!D%X-P_()#g()S-i_dh{>oFt*^o!%l= zMx9a6)Xkx(Zrmrsj(+l4Q3v9w&-hWRJBPUFXx~UT8=+mVabVV?}%< zO6S=V{ga>LVqN&-l)RUV-(j+?OXPSMwXzu1rRmldE{`Kg74Up4%-Lba7vCh?h3;ux zDm$r8f4UF8QI&0v=aXOGCuoaj zIWj)clN_1E8i;ggU*I^~``p296SHaaE76!Ji}nccU6&cE<8sU~=@kKn{#dh6AiD9a z_Kvl74j4)6abdO>`7EUvITCuFqm;DjB`7B^o7RjxnJ|YISS*o1Mr#x`V}p@yWUjVn z$C2FEVdGf&sjF~JzzBc6i#{O52(?Uc9q18>T08$C#UMqA4{Oxrl_IMKVp3WI1s4{b zR4Ua{DXH^!6BZ4NX8Vei=teZ`XF2jMdtZ|m%3^gBNRnVftR{;@>2_968h1dIhLkMP z&1tlkJo2UfT$2~WoBA|>kkmv{iB33@dKHp8k@o(Aob+Ci(o8}cHulN#aaTUpWXD69 zlZM`uDz6IGE8ASal^^4}FKJu`xv@**QFl6PKJveeU;`foUv;N+eLj8;)C#zr!ST51 zW|#{+mx9N4w!?QWs6B%>152?D1I`2_96c&~AsTJL?wW*P{H7w842hIX(v$wXwS4!T z5S)YUUVd`R$XmqvIf9!}(mR9D|5GzaiPUbDc_QIO>1R(Ab4`O=0Z_c9dz%Pg+}@ai zrhlW#%r^)P_`>#L)O;QgrfcJcOCulQ_A&^2!|QVlc#_~@^(zC`Fl=) z-u_Lx>a`$s&q>J~H+hr;xtZPj60L1?$zIXot%mD$tvAGUSpRo?ZK(b?u<*sEB(WKZ zN3A43Ul)wKea5QTojz&S0Z2RcYZkwhXTbQ_NG5kC=XU5M9k%%Px^%zNY>5)VQ|2o=Za-V!8TyMLgvN>* zQ!HHAuh?&z8zA*ye!gZ8VS^hEC3L7X&eKD+4HAq1*J3rq5)&Ue-3Wwny!|uj`YAB+Yn4d(2z61{oC-|Chx$`$ z0$qpsf|7=JL$|ftacpsgQQ_9UhbjxXSLlv7tw?dv28bE(m5ag66g*{b7b=F@jgMjp20qDuZ{k$KlVnV)t$;FS0&m z`fRDHw^!gx83UVb{B|q&E&2(XlXd;W+Uj-S6@9jH({kOj)R|Tc!noahJiz1SrT&cV zPT`6apFc>0P*MH(%-ww#b5n-@Gr}4rv-1&AP)u)&CA`fMe%%H(a_ESu_hnr);7oN) zTxG{e)Ye!Oxn+KN(doT%7@Is-<`qFO^QdPk7LFiu?Y)IMx_A0G!UbvNDiqIU2)eF< z5>8#VPW1(TImh@!Jt!o=$S>{UfG@vA-bA##V<~Xhz#&FJaWyicMexM$E-cD@{SI!f z)x)_77gjn1DXk|M$o0=!puwJdAj=mz3_vPKrZ~WgVhReJstDpR-X1Wy4;ESa)&1#v z=g8mzPb>-*O{20q+Wzob{aJ{4)K?;!$%05V>Y8XLr$)pZj#Z;bmvEj2H+dh^NHEU9 z_VUWkCKP}CeEWI>d{#v9UMb`KAjUT^rxd!pcDmyH(D+8QvQ2{41V96h_f_6wT+i#| zOdqc-6f8MHa*V}dXi5oMG#oeDFrI&YWsgf9gvWJ0)wOK(F*a zS_nkBnIY^N17{y$g3n(MX9AJ7;rxp1AddWiC@_ZDCT-8L{4VcoJA2c&gnR3ngc^W( z?Tdt7#DH-E1I?B)uTjAmuq2rEeBWz0f|>a#Cnp;&2*H9_8(S6*UvLH-Y<|L>PV)y9R6~5uj@uw7|DXhORkhHYnm1~nEVtqS7S2pKezC=2ZQQ_GCWKT8F*6U&c zS`9SS{KPsoAE)%|H`FR{@fps^9D59A%;h|10FPb5&EjxiJpGjh-UsOZ`0bidh@Dc4 zu~P8^Ee;nQ`;<7@(Vubh>8HIpCQAQi?aa5naeivid8f-~sG`%JSOm zpgB}bKr$paIA_88)Ug`ua-G_MyL_{7_!Gc!!Ty&#I{Q!6k}2d?)Q%aq#jAd~d)A_% zZ9ClddKlT{hH@EVVaD}nN|hjQu8hz2Y#!`wzusWir2I6OJGf{E!edMVecSqTM}oOT z@n65-v5#~D)DJm6`2-v{c?1%w+5~nn$wK~l|9B69yIe7(Cq1hpM1>B;6ar2DHMAT4 zKV)^_wWvcS`!ndd4(VN{GeV;K@7SR_<>TD-W5hfUZx_2UEbk`D zjEi4#5>p$Nq;hJVfCXB*V?!n*j}D1$s@eJMsyNS0lw(v@?A?%s~B*o=6QR(bM^MWO`NS; zn;q;>wUjti&Jiv8Cd7nZP%ZK@!~vDejYlRQQU490IlXR9clkJBp!kj>(B4uGktFIa z72@-g?Ad)Lcl!^NJ+iy*h7X41Z@f;;B;oYm)vvyycVR}yfTlCpJFn3@TVcIoU`@~e zFslj0x$qZ-_Y@EuE4S#pX!i1Y{LS2+owFBU9l0X0D@m5`x(gQZ;;T?Grmtd!^0mz!7Oc6^kTC7d)1D<2JMUd!T__)sukr%-_99| z4h~DF>~nnZ^1;n>TEg1X2mL*;@6FQd?UlF#O8F;5m?={rR;6_ve&A+Y!D`Y}yt2vd z#7+o2gPrsm5|hx}knY!P9W#Hno`;aWcGt-cb^)-R`O-s{)idfjeJ1=!#mziSON{0tE)s(^TyFEA^HkGax{U)C&oBzj#21fkM|>io{7`<${iNNFNz;@ z+uZ(jsVYQZsqg>pN14N;YnutZHGaEP+P*bxGUHAX*|` zmJ41zqyqJo7D@0*Z&s|ETt7rD_6G-gVzO}X2S9Mk_|*cj#9l~U=A2JQcqwe9&HpP) zIIC?FHOemovSh&*Y(3W_LmhbMkWe%ACYBrx+*cjJV^Px~6esOJS$A@*=Zq5v1b^bP;*(^f_3C3p3OM zX#Y}3gx|LkQV984qyZi0R^W!IFx1KUro;Y-JA=+LCrn^u{Hou-49g_L^O;_w=4CVl zs_i$7mn=gjzIz-;SCx3?me-W2aG3%^lq(Kdp1~_^f#K@wyV6~@w^0K%oS6&-k0Q!6Zm8OIug zp~kJy_Ua=>vNonasJE*#gR>=%OE{a6|H;hj9^2^q9jX~**c8y1XZPOp&>8qNM1_oe zz8VJqE(z~%Z^Cz8F|&ZctP@2vNHiJ!+H71)<_c!8xQ^}FvDTp5kOc5k2ByMwjJl%zho5x!B$S z+Xk+$$SvGEvP+1{N9@nVJO>5LPq3dP;#C9|qD`Ij#`mYM-Rok)tfVbp7tQ@YbCk9p zH?ZHY0+@UEdAbDIEMDSGoBK?)(4_x6NONu<^p{{uYigH(T=i4@wZjm+RVb#EDB!6g zhMgB#Sd7Hn!T=JY=MRR!U_Y;Bs%aN1am3LN z_fbTnx9XoA`_siOV9){G8i6R!QYF+UB0^i@S@Y7OfxB2;XD`Y?!s}O6QLT_m;fFRu z%_GhX!bYN&+(K;v@v2at;HKZ3v4qKh_#I%9${B?jS+^iLV~JgJT3RBAfxqsexOPyH zvHHYLF4P?DtI?Zj!fFgIn4j;gp6S3;^|g}$c+j5i<|;gQ9+ypz-u2V{s17B3=e^T)6}2mE%F9zCQ4QrR{}x zOUQB&*cnP5fE&rHdbCwhv5-5k;mhWB8vMr-6g0flvYqRRw#*+jtdYm zTu_p2Iyi#RLEaAyzyT}l3{)*tm^=T_$^#fLi8s>?W9b7S3-2qf!dEU95po5vFfmcp ze_s+eh>jMyweSG=QOvn-if)S|H?-VHt6B|%k_+LBq0rKQ&`XS37|d#G=%x0{6Hxb_ z;e8~pQ@Rr}KY1spZR1m`dJCT0sE|W|-o$tWsLuX~vqsAN4hB}IK)Xu>-?02m2%A)| zTO=u$k$rF5ZOnRcbH}yx2x`LXCKl_Z2kj3S4l zwsIe^h@{_pjNL#%K1I`Q?Q)Cuf@+*fh!%9t2S38}9vJ@$Zfw!aD3B}6c2mgKnk8Lu zFJ91=p<#~twZ2DTyX$bPDe4w-9m6j3eM0phtFY@*69M(b0PmcLMn@h`9WPN8l}u=) zsx{#T36gJxFufHGQe*YwO}8!8>&?ImSEL$@_|>g=ALHgONPl|_xqz{>3a0s#8hx1h zypcmq-8cdsDsaRut-Ks?gx?jyeagM-tD6zC*06t`(-s;y``Oi=-+89d`p(tg6~rDA zwse8l$puk}(HUY+f)G#ms62=?G|4p@KO0l0C`!y)+-e`f!!Xj=B+>0W3DUVe|#x7JmY@8Fn}h1(!Q=>`j(Y8 zbe0v1o_^^1?_YzEz776=QIY_ddK@=>K$R%nQ)ow#N$$Sqi??UCPSVR|0)Apb!pWd` zV#Msiml=J86B5Tb&6I?l^{^hE^9`NyyuLL4rEpUUJC{|}XZDIx#+Y#4N_!P^Cf=c1 zc?<5BVQs@$D-i4pw~QS#x7Wel3Uc@_y@E721mx?vBRU-bK)^KsfWrS4JtzE!p6lz& z3JS|BYtYl_|G!h{zQ%=?#s;Xy<_1Ovcd{~SckpTv3IRX~7{KHR*np0N2*GdBm=Qp6 zgoJ_$WD;XA1Sew3gAoYmva@q5(liv~k}K2{ll0Ov($ms%rUoWP^R7^_8QM=vApbTE z4l%pH->>!$d@uf||NkMXBmY-^{v^NjG$;E1hM#9LD70k#CzgNO;r(_-7H{f5tzUn; zkW(8$qn=;_qjH6pq3{4jhkB%Qr>B3mqOHB|9oNcjVRPfCvKcS{9woz&85qq#ARxr( zxZ9^MxY+e~TBs|@fA|Sm<7MvV{w$S0beJoPY2jYlS^7^~Vs17Lzk6J_dH-@b|9P7; zR)1Q+GIbto;#+|Oh96;7DDHRfFnPJ9MH?E+87xP`EM&ZHMuoWMCvySwRr)B{*vTy33x1h7(>K3~NVD=WK~j8K^mTs=rZe&0i>C`zCjtG5qJ z^eqPFDcOKW(y5aiki(nVqnx2DRNnECAmuA5k0qs@4l7!E%=>LjXldHPb~9Q)PqS_e z&{5jp7OJrw>?p_!!a}P+^;VT436VQTRDtmmw7b;Yn!&oGk_nC8s{PIZC6k`5fTppb zR)2$zo*wnC79bbeb0n-*FKCB2zhak&k3x<%Q=P059H2w<&#o>YBX$`*w9v0;ZKygcQAB3!Wdfzd84|hllo0k>aclT}uZpG;}vZ zX+XFy0^`}Cc&z0UlsL;eqeMya!Ou3Z;tvW%D^3X|Y=!7KBm{!klcpiN5j!msTC!4> znBP>Cdyo-FquUTPQNdYq8Iq3cy}dBz5fM(v>E5 z77gf?;D+bwOrI*U%HV}6=GY#WG0U^FvkC1=@j0!Rf{cm~NsJkwa1Z^p(jd5)I)xw!6JV3({Mf}j84VJxDbfCiRDN11v z06_RLPcRac&me?UV7&k#kexqzm8v$x{fQPdFsz+$LrUw`E&P1SN}B*of-Z~x5EyY! z(D*fM7rG~L*|bZ=;ZNCRY{8{IHTP>QUWEzgypWx$tf5qu=_>D zCWtq8TNdM|rKfzenw<~H{zxidleD_Ed4dj1j8jauWG5N{VMg{T>u054D=1NNA|Fg} zKp+Lj9=)%orzNiWV@_I2{k33c0Z2h(rK2W2e$mt9CIn7l6C6w<+6o+1P%^V|w$74B zjcFOT5r8orXn_AM=0fEH{0;5N9Tx@!B&d}xmJZN8T4RzUQR@Cz;e+G;fS3^7c8V4q+e72x-;@5}|V*kY`yGZ2t#BOlm@y0xTK%;UkRJ|{32_+6RFJ{h*- zlaqeAp2M<=Ppbd(!QRwvuxJ6FyhU2YtcKD^v2{-DnYgYh{0w|R}IHV%t zb+mZ%(waP<_LHuR1K!Q%!F2pWCBT_~4GGqNHn!^rZR#@Vq?F{Ox~y6|7?>=eATlH` z31Z+aH%3~8Wh>N0EuG<}6#a7cMCMo2{k{t&zvY}uO$K0t6DOH}#%kz_qNZXh%mbd$ zeLI`&$z9QSZiGpNQJZ0#sumho$Hp#9D+EInO~%1b>>}|@$$}MB+b=n~Z2M|nGZ3tj zjQE22&@9UP+OD6jH%&Wpv_^@2&^+;+l_pj{Jq-Hd8q+r9ZCyp-C)Z#C>hqMtOI=<+ z_xi)uI*DtO{Jk7l{UR)fY_v&2Fv z9Dh1FMZzO=xsAHi85h#&ZU6CdM=d3kLruuaN;3|?(l{uV0rz``XF|1jd zEehPMK;HY_Bn4)i$xbFGRxgJ|p_`K|SjD0~lJbxZ&0=%I}hbpyRh*HMtl5{`2Ygay0<)A@_T zZe6{*q6PE^qK+b~C0L|K3=1;eeH2r?Op%B0lu|_>34>F);`T*G0C~Vtarx4T7it@T zSTi8Hzl(pGq8zdFIbylfj3sl=ZcSmTC@FnKCjBw@YBlMw7@gtNW1WwBzEAGpxgz`r zKo^J0uKR<3d@m$?_2SO0Z;gOcI2qiU92cQN-AE-!mM=F!ru|>3IX4eooLE^hAUk;} zmZ}{_fC5G1*m|w`Cq@L)AA^l%Sh2=zSA_t?jGo*GvOMHDfcJrq_B2c%&0=yy_s*TH zn8>BGriqSf3sab266B$vAQ&rA6qRgQI(oB5I*2h!na)wEg74KzVnR+#{yE-Y%kMBy zVMuFJ8!8+tAB4VUv$4t@L|;VePnnFbO-~1^6w?Tr7&|k;R<2GcrJ+Oo1trQ1!EnGzI)vmFI@YM*%i9E1Yy?ui zS(5Q!Qc#*tic7o^Fi?sxz5uBd6bX~#dqRGLc56ettW!AFS)mP05s40k@Y3VX+4x7+ z9e9S{EwA(jEJR9t@MsOsN0n^IOptG!{h9ZX4Y3Xw#-f9Nf5<|9IN8QNvJd&`mXb?| z4@W%i_eBry-H-8aFxT?1mZ&>q`0pEhP6~f6ZjT$YOWRK!WBpzb)B2mG(t|?zhvYvK ziLXCjp~2yA_?(9ITG-3wdH3p%J5jx3#?lV9g~pNhZ3kQYzfv=%=Vt&bgTe=#QR zL@~{(o^SU}5{r9zb~av+(u%IRpl*+hg0ta?tM(9H%2QUI$UZoR-yK+TEW=OX(v7fV ztSXegxcnuM%U~{IYm$Xfcufk?LADhZlrIoi%HbKM9aAb2X4`|vRtD^M!M|cB)t6!j zKpJaXUA20Wf_s@MrwHIq@hKN-yYxh8Y-_8wWp+-v#AhFA%5=Jt2R?3Ylr8kD(}7hr z1>=-ZLzw*3=saA5j54c3E1}|!1uAOZipD_Yg~sp@v()l2s_HyzEqF@s9#dl&-&TbF zBDpE0RSik0zn0PfR;^4Q?ZT6PU?n9GtdLp0py9r6g9quf!OSM$C!ldIQBTa-wK}e$ z(QBjQU-j3NlZB-8tjh7ThW7_HXl@Jk5!p4Ngk7uB?IV`b_n@mHZpBXmBo2JXO5ew#R+!y-j5YgIwT6 zb@}tTr}1@{pJngcUX-JuO#f>Ej~{E*Tn`ap4U9~@7&0 zq~y?2J;N<2z3GoEULsht0bue4K_D{(+}Rnvl`TB2UrHeoGI#N{?p647JxYV{w>P$p zN_~j4(H$AyWxL*Gecw`jF15a|rdHpum3&_CT+Q~b3DSxGc)+X%3|N;1&2Hk^He|BJ zTL>Y%<&nZ<`uQ1c=8w796d*c(Xk}ee;$om?%Exi(;OJDCG6g505e6g~f*MPg+%A8w zHrHd7aIl_eE z&=vfcP|3lu9~cN}c2vq};1PEAXyr3nAhrUwe?`en`I3!v#ym39aU`{ehHTb-ZPswC zS&d<|PH&4l#Ck%o-Kb2%oULtrjipo+-C&5oUeP>zx7#0Q#;733yZ${y9O9{j{DYo6 z_i}G%q{zE7^|5H>^ag40#8aFpL{F@aFYwd{WBM?SyK{!fZv3`ZgWJBF?|d8r0;bIY zMl2;jDMp=vrUdwLrbwgckZ=-`mh2j-Byu&reBHni8@_fC^wd;o-I0Mn8Bzh56pm?T z(J}SCLXGz-w1~PYguaH})2f*0PRw?A2^3?tL!+(P4zTQLa@ozIWnpBf2u;kgi%r=X zV;EpB1)!e1;EGnso)ak1#kA7oVB_9;)n3!&x5UA!oD{?(pX_6 zQF$tzEUMh!LXQ_x4ML35XzE9`cRp;F_M9aElioDUfmix{rY_VBasN49|n+kzjHpzv5Vmj01szzez!LNj0A)MY2}Z} z>j!q^kLlEdZKau+07Eu@mWX}iRTvD6BR|$~B{i>JB`)Vo2(!31dF7?M2w!t!M%rfd z7;v87YBs4yClk;98by05LnVE91e(OHxeEpq@2a4BpRBz0n z>(v&8W?=4sd$@uE+E*9l{HYaWi$K_gN1Mxz9$4jo42s5MCWm93bQIvE^$XJpR1}8Z>Rl0Z z1O-S+O9}&Wl3~PS9vq1=4LeYG2K%GffoGR^Ao~n{R|4mk)a{=Ovy!BkO~GA&I^80A zl2^20ce*xm-d(R*ZJg5s7`2}00X5uWTtKdDz(^37kktkcO8BYlLhRc&uVtV=WZ5V{ zSaUZZ(0%>WqqwBVmh$i(q~ki6cBV6B6{>j;*xpd}`W*&j?pP+)&nqpVmyxdpF++2Z zuE;%b*W}7is`=AF^Mg1N;rA6-%Y}tfgdd)wK{B*9qpJUlu^J` zlHM&IVDEtS`?HjTiAEqc6U!1qwvcHD*{pIhqpbtkBnz6qQTj`kScvx0evU1L-rVY8 zI5&CI%%_AMOlbblnBh2f&;dH0zInAMVEyK>itt6$wLkxUC1O7oKOW*ubp|CQq*RZ5 z?8;74&k8mn*N-a6vRR@ypS>xcy|p6vjHV26$@@phQ*tl^Da*n39ICb07!+|iqX~Dh zR#zXVGxERp)loC!w_+B2qLUz;W*TqTxjx!JEdtjhR^_uyvVD&XnP5hFtN}J2pAdMe zwD^692~L`=B&9G~J2j=WWkISYqwoBen=%*g%1FG~QIi-c=x2vcl{nLQrqVK~FqhR8 zZfOD=$LkZ49K~EL)3>8Ju>IXAe`M_|Js=U*?G}%=BLGu9wPV;3Lc0!0ek9>o_~4*@`UGPhw)z6B%kEqNlxHi+k9?8U*cOWhOI7 z&n@-KDo9SUO!KP`1M(cukWZ1kMO_PbNX*g}Ouz+-8n0BRUpKz$5kgbSnBRBOMBL}V z25k#y8Kj^y-_X20H>J>Xgl)TjgXFsaW*M+u^|ZDgXfPa!r#>l69WaRK$NmLJ+MvmOM$9l^T) z5_(q$I_>hu^ltf>;LDZ@w!!2Yr3|gm4@sO%TNk$YXsi;VbLbg^1d4iOYbwr&*@uv^ z@uKwO&ScxFk)#7}rYbFMp#B;N90PCV-!;1=o4UIwD0{O_y()F{Y$Ngh#(>Kt29EFV zd+lM$_x_F)aO$5>1@dcY`ul0iwJ)zjx-~~R)5W+&)ujtI2@S_Q!ZG27xl4?y;k)xO z0t7hI1)Skcp9Vow*&pOI^k>kIF$cc%t+jU;n5Ek>m^PtxRBWw6(V7r4!I3K0hRG~7 z6|6}?okNe7QqqNv!hy;P45Ru~&i3v-G3M4eTxfsfO>Z2BridZ|sCiDDeVf zkY`;P#g|c%*h_N_H$><90{`CYtPP@g@hf&Ld^2=5ewKcR}wQuNj zwIniB!^S*_uzyfM)ZN~sn8S_OouRE;ughgeS)@2h_Yfogn9m;8Ow~kaoT9e|Li!on z7JUv2T+7pkkZN0`EeAuVDSz$= z$(o*VZ9;AuR-?8_Yn84K8I~E8V)Qa8{hY~G&TM0knaf=tzO(P}RdNc+h3ufcBWn;Bf$Qz)%w>wMx{$4Sog1up<2m5T5sW zv#{J~5G3fhH7BtWd7+Kvkn10nj}IgTGb3RZ8v~1*MtSJSfOpY&$)sfzyxzl}m(}(u z67hNOH%(09Iii^)OafB;#)Lex*f!ah2`PRfvxy?~=-TYYoYLI9Tl2smxy3vzOj)Tm z?Pr`I=Bf)=V(Vu4+r#adzt5Z%X}aC&Ye7SB;bSB;e@M`^4=q~7-HtyyW}FeX#kTF{ zI72(~)7b8D(wUy+#+1LMo3G6W%33RrsmEAv{Zf;|iZa#~WN2kR?~Z&B6jeIM*$uOP zs55VKo>LLkHUG2vG!|%Ev2E5nWsV{SPqGW?JILJGvc_k;>4U9~+50UaO%R=6B(J`e z1UiNAIU==j~!EJm{b%U7d<&?4-C z!#`aEe%zN`v#9=|k;bu)H?2`m#zz8j70dQH)?aP3sr^0NRwGvY)K;7C+T%XkrGwnB z(C}`B=5v@e-`tnUmXEqx{(@B@GHX@FuC`B9tEc`>*YK&(_dB+SOW{WA`(Fx0H$x(( zX$8aDTAFSkkGWET5wmOtmF678HTlZjSEZ}fRyGD3R^>9=+EJuMz)9H82YovUw(Q^i za;qwOzcK+lLU|l8$ot5N`uG%k{~~zSYKy5>7KvC&?bB<$!-6s5Z#m-9rK;ukx3q3{ zegTa`RAvO1N0OxV#`-*qRdL4&=w;TptTkizF5FFdld3nM;*rhDcpuW|D3O_4Mgezq zRqyZQG`DIWd{@RsP_`=$Q&=fm2HN&tJINQIg5PUN7bgXL+jo%LV?8@w`GPqCb!dU> z$Vmbxd3Jt?&SHUeT6Xn>6elv1h)<&rBNMR6(g!aEvQ9lX_n{AU)phxx8i`IOx3mso zP*&$8C-nnbP!N$w!1OGP;gH70d_9nBF_VY|UbrD}&w*CxeQqxmLe22>gzc4jey;E_ zAiOYbhFy11vjTlp`k71&-(&i!anXQlS_up%OgDh~$?)lIaPRMLbH3GS2SiT3{W=1> z!1vhj%ZzgKwgm_%(bavI;oGAr+!&}xFRj> z6OUD#GGyx5wbq28+Ol|D02D~mYB!CZZpmH8VA16ti1S3E>a07X?Vdi$Vm4t<)3!S; z=@=f;k*~H;0VvTtoHCOb&^Nh+IkvuSQ+6>Tz4Sh3((T9G$yzpLHtUEXnx7DOPw&V|6qgJ|7Bl##>wPwG*&*t4%z~U{CwQjxmsB&0fgXjr2M*&aY=CzPD69VOiQ= z*_+0*n4(ozouRdI5_tx|Qf#!(`Ythd8LF!NmlwLMRDR92q($rx_caooQOqinA3{qy zFky>j9fhVFCd1FQ7;eQ5X>9;E2K3ak+e6g%Nz)65h`$lwZfVMbOgw*S`ahF9{o(P^ z`IXxgYR_N1&6hT>cVZd_%TR+0E6AhZwnY%QHjjZP%*JqQg=~^nXqYsV*Ktc5FP4U4 z0B&Vgf_?++ik+Mn6+8k7_z$8zcbHQMXrZ>{6}i4mArw50USSn`-I6ecWA(+C9ly#dkB~? z>g+&hD-E>GaKS5HDQDX7*)b~f;Gbkn?(Eh^IxL53TZ46O#x_OBv!NR^0iWqY$-$i?#V;8F8~x-8PDny+eSnw4ye8h;hFx)@3GX!2c+R zL`ZF!^3Ph|SzM*K`T31Su2k4-tJA+?s@mMcQZC{*;(xOmvIIVVB#D85r1Jl7tj7Na zoczCI8~;CmlkD*S6*xiqN6q|4(s1-GAm53V2dr(l$uqa|~LxzlcqLZnhVMxu55ge0o(s91fq~@u?HHaWX_n`jtTWJso z6oL`Nu9a><@*VAkvd<9h<>hX-m!0ks`pIV|Cp#8^#m%R z1UauBuZit^$~l@EnpzC{9w^dBPQ%I;621mZd902la}x_o6I6PUBfep*zFvZ6{42k9OT zg)=Uunb4PS?qAj_#({fi71e9zV8kcg{yo&@}BVoI2P(Pm>2=b#3l7 z-iQLa{zk{^USMS`@BA$^FzHEl1)sIE2X}Fz$76-u9|#0hMMY~xCbh}mrVgJ)aa+0{ zY)hQUGAS%?Ix=L=pGAtN_J8}N2h&J1ba?#8A{DgX(sbMViK~Ne3NMAd*Ue30lD{Hy zhgx$Bl&SMjcTc6fH#6w4y;v$l#f81ycEVZoj~BwCzx{uR@=bVjfu{DV zp59BJ96R`20{t_cD5oD*Yur|+BHr!+GArwb5B-4ih)D-58L;fxl*9^@gL-#6Q(HVe z_~*ekV&ALn2CT1u))66=q>xs>D}`eOjHLD-OFTIQII|cF8o|CjI5-NYQD4j^K~--= zJDQbt>YbJ8pMcplH3~QT4;tR(tSS6JXr4I=a+@4Eez%D5MMM3QsMg`{La

|O~=>`D&o@zv&rgRCSV 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�OioLp?EwZ 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 0) { + var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; + 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; + } + return true; + }; + + var addAuthorized = function() { + setAuthorizedList(authorizedList.concat([{ name: '', phone: '', idCard: '' }])); + }; + var removeAuthorized = function(index) { + var next = authorizedList.slice(0); + next.splice(index, 1); + setAuthorizedList(next.length ? next : [{ name: '', phone: '', idCard: '' }]); + }; + var updateAuthorized = function(index, field, value) { + var next = authorizedList.slice(0); + var cur = next[index] || {}; + var patch = {}; + patch[field] = value; + next[index] = Object.assign({}, cur, patch); + setAuthorizedList(next); + }; + + var addRentalRow = function() { + setRentalOrders(rentalOrders.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }])); + }; + var removeRentalRow = function(index) { + var next = rentalOrders.slice(0); + next.splice(index, 1); + setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); + }; + var updateRentalOrder = function(index, field, value) { + var next = rentalOrders.slice(0); + var cur = next[index] || {}; + var newRow = Object.assign({}, cur); + newRow[field] = value; + if (field === 'plateNo') { + var v = vehicleList.find(function(x) { return x.plateNo === value; }); + newRow.vin = v ? v.vin : ''; + } + if (field === 'brand') newRow.model = ''; + next[index] = newRow; + setRentalOrders(next); + }; + var openServiceModal = function(index) { setServiceModalRowIndex(index); }; + var closeServiceModal = function() { setServiceModalRowIndex(null); }; + var addServiceItem = function() { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var removeServiceItem = function(siIndex) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + items.splice(siIndex, 1); + if (items.length === 0) items = [{ project: '', fee: '', effectiveDate: '' }]; + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var updateServiceItem = function(siIndex, field, value) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + var item = items[siIndex] || {}; + var newItem = {}; + for (var k in item) { if (item.hasOwnProperty(k)) newItem[k] = item[k]; } + newItem[field] = value; + items[siIndex] = newItem; + var newRow = {}; + for (var rk in row) { if (row.hasOwnProperty(rk) && rk !== 'serviceItems') newRow[rk] = row[rk]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + + React.useEffect(function() { + if (!deliveryRegionOpen) return; + var handler = function(e) { + if (deliveryRegionClickInsideRef.current) { deliveryRegionClickInsideRef.current = false; return; } + var el = document.getElementById('delivery-region-wrap'); + if (el && !el.contains(e.target)) setDeliveryRegionOpen(false); + }; + document.addEventListener('mousedown', handler); + return function() { document.removeEventListener('mousedown', handler); }; + }, [deliveryRegionOpen]); + + React.useEffect(function() { + if (plateNoFocusRow === null) { setPlateNoDropdownRect(null); return; } + var timer = setTimeout(function() { + var el = document.getElementById('plate-no-input-' + plateNoFocusRow); + if (el && el.getBoundingClientRect) { + var rect = el.getBoundingClientRect(); + setPlateNoDropdownRect({ top: rect.bottom + 2, left: rect.left, width: rect.width }); + } else { setPlateNoDropdownRect(null); } + }, 0); + return function() { clearTimeout(timer); }; + }, [plateNoFocusRow]); + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, + breadcrumb: { marginBottom: 16, color: '#666' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#999', cursor: 'not-allowed' }, + inputError: { borderColor: '#ff4d4f' }, + select: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + textarea: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, minHeight: 80, resize: 'vertical' }, + errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + authRow: { display: 'flex', gap: 12, alignItems: 'flex-start', marginBottom: 12 }, + authInput: { flex: 1, padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4 }, + btnDel: { padding: '8px 16px', color: '#ff4d4f', border: '1px solid #ff4d4f', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer' }, + btnAdd: { padding: '8px 16px', color: '#1890ff', border: '1px dashed #1890ff', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer', marginBottom: 16 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 }, + regionCascader: { position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4, backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: 4, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', zIndex: 10, display: 'flex', minHeight: 200 }, + regionCascaderCol: { flex: 1, borderRight: '1px solid #f0f0f0', overflowY: 'auto' }, + regionCascaderColLast: { flex: 1 }, + regionCascaderItem: { padding: '10px 12px', cursor: 'pointer' }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'top' }, + rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + rentalInput: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 13 }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#999' }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 }, + btnGroup: { display: 'inline-flex', border: '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden' }, + btnGroupItem: { padding: '8px 16px', border: 'none', borderRight: '1px solid #d9d9d9', backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, + btnGroupItemLast: { borderRight: 'none' }, + btnGroupItemActive: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff', borderRightColor: '#1890ff' }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 }, + modalFormInput: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, height: 36, boxSizing: 'border-box' } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var FormItem = function(props) { + var colStyle = props.fullWidth ? styles.formColFull : (props.colStyle ? Object.assign({}, styles.formCol, props.colStyle) : styles.formCol); + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null + ); + }; + + var customerOptions = customerList.map(function(c) { return React.createElement(Option, { key: c.id, value: c.id }, c.name); }); + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, onChange: function(id) { var c = customerList.find(function(x) { return x.id === id; }); selectCustomer(c || null); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, status: formErrors.customer ? 'error' : undefined }, customerOptions)), + React.createElement(FormItem, { label: '客户统一信用代码' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.creditCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.address : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.phone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电子邮箱' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.email : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业名称' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyPhone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.mailingAddress : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setBusinessOwner(v || ''); setOwnerFocusError(''); }, onFocus: handleOwnerFocus, onBlur: handleOwnerBlur, disabled: !businessDept, status: (formErrors.businessOwner || ownerFocusError) ? 'error' : undefined }, ownerOptions.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCodeDisplay, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同类型', required: true, error: formErrors.contractType }, React.createElement(Select, { placeholder: '请选择合同类型', style: { width: '100%' }, value: contractType || undefined, onChange: function(v) { setContractType(v || ''); }, status: formErrors.contractType ? 'error' : undefined }, React.createElement(Option, { value: '正式合同' }, '正式合同'), React.createElement(Option, { value: '试用合同' }, '试用合同'))), + React.createElement(FormItem, { label: '生效日期', required: true, error: formErrors.effectiveDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效日期', value: effectiveDate && window.moment ? window.moment(effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))), + React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : React.createElement('span', { style: { color: '#999' } }, '根据租赁订单自动反写'))), + React.createElement(FormItem, { label: '结束日期', required: true, error: formErrors.endDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择结束日期', value: endDate && window.moment ? window.moment(endDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEndDate(dateStr || ''); }, status: formErrors.endDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款周期', required: true, error: formErrors.paymentPeriod }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: paymentPeriod || undefined, onChange: function(v) { setPaymentPeriod(v || ''); }, status: formErrors.paymentPeriod ? 'error' : undefined }, [1,2,3,4,5,6,7,8,9,10,11,12].map(function(n) { return React.createElement(Option, { key: n, value: String(n) }, n + '个月'); }))), + React.createElement(FormItem, { label: '签约公司', required: true, error: formErrors.signingCompany }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: signingCompany || undefined, onChange: function(v) { setSigningCompany(v || ''); }, status: formErrors.signingCompany ? 'error' : undefined }, orgList.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))), + React.createElement('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.contractOriginal }, + React.createElement('div', null, + React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '续签时需重新上传合同原件附件'), + contractOriginal + ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginal(); } }, contractOriginal.name), + contractOriginal.size || contractOriginal.uploadTime ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size ? contractOriginal.size : '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + ), + React.createElement(Button, { type: 'button', size: 'small', danger: true, onClick: function() { setContractOriginal(null); } }, '删除') + ) + : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement('input', { ref: contractOriginalRef, type: 'file', style: { display: 'none' }, onChange: function(e) { + var f = e.target.files && e.target.files[0]; + if (f) { + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + setContractOriginal({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); + } + e.target.value = ''; + } }), + React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件') + ) + ) + ) + ), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, style: { width: '100%' } })) + ); + var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setRemarks(e.target.value); }, style: styles.textarea, rows: 4 }))); + + var authorizedContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人姓名'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人联系电话'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人身份证'), React.createElement('span', { style: { flex: '0 0 80px' } })), + authorizedList.map(function(item, index) { return React.createElement('div', { key: index, style: styles.authRow }, React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人姓名', value: item.name, onChange: function(e) { updateAuthorized(index, 'name', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人联系电话', value: item.phone, onChange: function(e) { updateAuthorized(index, 'phone', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人身份证号', value: item.idCard, onChange: function(e) { updateAuthorized(index, 'idCard', e.target.value); } }), React.createElement(Button, { type: 'button', danger: true, onClick: function() { removeAuthorized(index); } }, '删除')); }), + formErrors.authorizedList ? React.createElement('div', { style: styles.errMsg }, formErrors.authorizedList) : null, + React.createElement(Button, { type: 'dashed', style: { width: '100%', marginBottom: 16 }, onClick: addAuthorized }, '添加一行') + ); + + var hydrogenFormRow = (function() { + var hydrogenFields = []; + hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); + if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } + var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setReturnHydrogenPrice(e.target.value); }, addonAfter: '元', status: formErrors.returnHydrogenPrice ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenColStyle = hydrogenPaymentMethod === '预付' ? { flex: '1 1 0', minWidth: 180 } : { flex: '0 0 calc(50% - 8px)', minWidth: 180 }; + hydrogenFields.push(React.createElement('div', { key: 'hydrogen-amount-row', style: { display: 'flex', gap: 16, flex: '1 1 100%' } }, hydrogenPaymentMethod === '预付' ? React.createElement(FormItem, { label: '氢气预付款', required: true, error: formErrors.hydrogenPrepay, colStyle: { flex: '1 1 0', minWidth: 180 } }, hydrogenPrepayInput) : null, React.createElement(FormItem, { label: '退还车氢气单价', required: true, error: formErrors.returnHydrogenPrice, colStyle: returnHydrogenColStyle }, returnHydrogenInput))); + return React.createElement.apply(React, ['div', { style: Object.assign({}, styles.formRow, { marginTop: 20 }) }].concat(hydrogenFields)); + })(); + + var plateNoOptions = vehicleList.map(function(v) { return React.createElement(Option, { key: v.plateNo, value: v.plateNo }, v.plateNo); }); + var rentalTableBody = rentalOrders.map(function(row, idx) { + var modelOpts = row.brand ? (modelByBrand[row.brand] || []) : []; + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTdCenter }, idx + 1), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.brand || undefined, onChange: function(v) { updateRentalOrder(idx, 'brand', v || ''); }, placeholder: '请选择' }, brandList.map(function(b, i) { return React.createElement(Option, { key: i, value: b }, b); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.model || undefined, onChange: function(v) { updateRentalOrder(idx, 'model', v || ''); }, disabled: !row.brand, placeholder: '请选择' }, modelOpts.map(function(m, i) { return React.createElement(Option, { key: i, value: m }, m); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择或输入搜索', value: row.plateNo || undefined, onChange: function(v) { updateRentalOrder(idx, 'plateNo', v || ''); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; } }, plateNoOptions)), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { value: row.vin || '', disabled: true, style: { width: '100%' } })), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.monthRent || '', onChange: function(e) { updateRentalOrder(idx, 'monthRent', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(idx); } }, '管理')), + React.createElement('td', { style: styles.rentalTdCenter }, calcRowServiceFee(row) + ' 元'), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateRentalOrder(idx, 'deposit', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '备注', value: row.remark || '', onChange: function(e) { updateRentalOrder(idx, 'remark', e.target.value); }, style: Object.assign({}, styles.rentalInput, { width: '100%' }) })), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeRentalRow(idx); } }, '删除')) + ); + }); + + var rentalSummary = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalVehicles + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalRentService.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalDeposit.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalHydrogen.toFixed(2) + ' 元')) + ); + var reqStarStyle = { color: '#ff4d4f', marginRight: 4 }; + var reqStar = React.createElement('span', { style: reqStarStyle }, '*'); + var rentalTh1 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 50, verticalAlign: 'middle' }) }, '序号'); + var rentalTh2 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '品牌'); + var rentalTh3 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '型号'); + var rentalTh4 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, '车牌号'); + var rentalTh5 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 160 }) }, '车辆识别代码'); + var rentalTh6 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, reqStar, '车辆月租金'); + var rentalTh7 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80, verticalAlign: 'middle' }) }, '服务费项目'); + var rentalTh8 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 90, verticalAlign: 'middle' }) }, '服务费'); + var rentalTh9 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '保证金'); + var rentalTh10 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '备注'); + var rentalTh11 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 60, verticalAlign: 'middle' }) }, '操作'); + var rentalTableThead = React.createElement('thead', null, React.createElement('tr', null, rentalTh1, rentalTh2, rentalTh3, rentalTh4, rentalTh5, rentalTh6, rentalTh7, rentalTh8, rentalTh9, rentalTh10, rentalTh11)); + var rentalTableTbody = React.createElement('tbody', null, rentalTableBody); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody); + var rentalTableWrap = React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl); + var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); + + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.project); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.standard); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.serviceFee); + return React.createElement('tr', { key: i }, td1, td2, td3); + }; + var makeFeeRow5 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.category); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.part); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.partName); + var td4 = React.createElement('td', { style: styles.rentalTd }, r.qty); + var td5 = React.createElement('td', { style: styles.rentalTd }, r.feeDetail); + return React.createElement('tr', { key: i }, td1, td2, td3, td4, td5); + }; + var feeCertRows = feeTemplateCertFees.map(makeFeeRow3); + var feePenaltyRows = feeTemplatePenaltyFees.map(makeFeeRow3); + var feeConsumablesRows = feeTemplateConsumables.map(makeFeeRow5); + var feeOtherRows = feeTemplateOtherFees.map(makeFeeRow3); + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeCertRows)); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows)); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows)); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows)); + var feeTemplateSelect = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择费用模板', required: true, error: formErrors.feeTemplate }, React.createElement(Select, { placeholder: '请选择费用模板', style: { width: '100%' }, value: feeTemplate || undefined, onChange: function(v) { setFeeTemplate(v || ''); }, status: formErrors.feeTemplate ? 'error' : undefined }, feeTemplates.map(function(f, i) { return React.createElement(Option, { key: i, value: f }, f); })))); + var feeTemplateBody = feeTemplate ? React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), + feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), + feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), + feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), + feeOtherTable + ) : null; + var feeContent = React.createElement('div', null, feeTemplateSelect, feeTemplateBody); + + var billingBtnBase = { padding: '12px 16px', border: '1px solid #d9d9d9', borderRadius: 0, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14, textAlign: 'left', flex: 1, minWidth: 0, width: 0, height: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', overflow: 'visible' }; + var billingContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', border: formErrors.billingMethod ? '1px solid #ff4d4f' : '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden', alignItems: 'stretch' } }, + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) + ), + formErrors.billingMethod ? React.createElement('div', { style: styles.errMsg }, formErrors.billingMethod) : null + ); + + var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; + var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; + + var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; + var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; + var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; + var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; + var reqSpecBlock = { marginBottom: 8 }; + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-新增租赁合同')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) + ); + var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) + ); + var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) + ); + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 640 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '合同续签')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement('div', { style: styles.anchorWrap }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式') + ), + React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))), + React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)), + React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)), + React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { style: { height: 60 } }), + serviceModalContent, + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { message.success('租赁合同已提交审核。'); } } }, '提交并审核'), React.createElement(Button, { onClick: function() { message.info('保存,加入租赁合同列表(仅操作人可查看编辑)'); } }, '保存'), React.createElement(Button, { onClick: function() { message.info('取消'); } }, '取消')) + ); +}; diff --git a/web端/车辆租赁合同/新增租赁合同.jsx b/web端/车辆租赁合同/新增租赁合同.jsx new file mode 100644 index 0000000..9dc4b85 --- /dev/null +++ b/web端/车辆租赁合同/新增租赁合同.jsx @@ -0,0 +1,655 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 车辆管理系统 - 新增租赁合同模块(整体实现,IE11+ 兼容,采用 antd) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var Modal = antd.Modal; + var message = antd.message; + var Option = Select.Option; + + var cs1 = React.useState(''); + var customerSearch = cs1[0]; + var setCustomerSearch = cs1[1]; + var cs2 = React.useState(null); + var selectedCustomer = cs2[0]; + var setSelectedCustomer = cs2[1]; + var cs3 = React.useState(false); + var customerDropdownOpen = cs3[0]; + var setCustomerDropdownOpen = cs3[1]; + var cs4 = React.useState(''); + var businessDept = cs4[0]; + var setBusinessDept = cs4[1]; + var cs5 = React.useState(''); + var businessOwner = cs5[0]; + var setBusinessOwner = cs5[1]; + var cs6 = React.useState(''); + var ownerFocusError = cs6[0]; + var setOwnerFocusError = cs6[1]; + var contractOriginalRef = React.useRef(null); + var csContractOriginal = React.useState(null); + var contractOriginal = csContractOriginal[0]; + var setContractOriginal = csContractOriginal[1]; + + var bs1 = React.useState(''); + var projectName = bs1[0]; + var setProjectName = bs1[1]; + var bs2 = React.useState(''); + var contractType = bs2[0]; + var setContractType = bs2[1]; + var bs3 = React.useState('2026-02-16'); + var effectiveDate = bs3[0]; + var setEffectiveDate = bs3[1]; + var bs4 = React.useState(''); + var paymentMethod = bs4[0]; + var setPaymentMethod = bs4[1]; + var bs6 = React.useState('2027-02-16'); + var endDate = bs6[0]; + var setEndDate = bs6[1]; + var bs7 = React.useState('1'); + var paymentPeriod = bs7[0]; + var setPaymentPeriod = bs7[1]; + var bs8 = React.useState('嘉兴羚牛'); + var signingCompany = bs8[0]; + var setSigningCompany = bs8[1]; + var bs9 = React.useState(''); + var deliveryProvince = bs9[0]; + var setDeliveryProvince = bs9[1]; + var bs10 = React.useState(''); + var deliveryCity = bs10[0]; + var setDeliveryCity = bs10[1]; + var bs10b = React.useState(false); + var deliveryRegionOpen = bs10b[0]; + var setDeliveryRegionOpen = bs10b[1]; + var deliveryRegionClickInsideRef = React.useRef(false); + var bs11 = React.useState(''); + var deliveryLocation = bs11[0]; + var setDeliveryLocation = bs11[1]; + var bs12 = React.useState(''); + var remarks = bs12[0]; + var setRemarks = bs12[1]; + + var as1 = React.useState([{ name: '', phone: '', idCard: '' }]); + var authorizedList = as1[0]; + var setAuthorizedList = as1[1]; + + var emptyRentalRow = { brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }; + var os1 = React.useState([emptyRentalRow]); + var rentalOrders = os1[0]; + var setRentalOrders = os1[1]; + var os1b = React.useState(null); + var serviceModalRowIndex = os1b[0]; + var setServiceModalRowIndex = os1b[1]; + var os1c = React.useState('客户'); + var hydrogenBearer = os1c[0]; + var setHydrogenBearer = os1c[1]; + var os1d = React.useState('预付'); + var hydrogenPaymentMethod = os1d[0]; + var setHydrogenPaymentMethod = os1d[1]; + var os1e = React.useState(''); + var hydrogenPrepay = os1e[0]; + var setHydrogenPrepay = os1e[1]; + var os1f = React.useState(''); + var returnHydrogenPrice = os1f[0]; + var setReturnHydrogenPrice = os1f[1]; + var os1g = React.useState(null); + var plateNoFocusRow = os1g[0]; + var setPlateNoFocusRow = os1g[1]; + var os1h = React.useState(''); + var plateNoSearch = os1h[0]; + var setPlateNoSearch = os1h[1]; + var os1i = React.useState(null); + var serviceItemFocusRow = os1i[0]; + var setServiceItemFocusRow = os1i[1]; + var os1j = React.useState(''); + var serviceItemSearch = os1j[0]; + var setServiceItemSearch = os1j[1]; + var os1k = React.useState(null); + var plateNoDropdownRect = os1k[0]; + var setPlateNoDropdownRect = os1k[1]; + var os2 = React.useState(''); + var feeTemplate = os2[0]; + var setFeeTemplate = os2[1]; + var os3 = React.useState('month'); + var billingMethod = os3[0]; + var setBillingMethod = os3[1]; + + var cc1State = React.useState(false); + var cc1 = cc1State[0]; + var setCc1 = cc1State[1]; + var cc2State = React.useState(false); + var cc2 = cc2State[0]; + var setCc2 = cc2State[1]; + var cc3State = React.useState(false); + var cc3 = cc3State[0]; + var setCc3 = cc3State[1]; + var cc4State = React.useState(false); + var cc4 = cc4State[0]; + var setCc4 = cc4State[1]; + var cc5State = React.useState(false); + var cc5 = cc5State[0]; + var setCc5 = cc5State[1]; + var cc6State = React.useState(false); + var cc6 = cc6State[0]; + var setCc6 = cc6State[1]; + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + var formErrorsState = React.useState({}); + var formErrors = formErrorsState[0]; + var setFormErrors = formErrorsState[1]; + + var customerList = [ + { id: '1', name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }, + { id: '2', name: '上海某某运输公司', creditCode: '91310000MA2XXXXX2', address: '上海市浦东新区张江高科技园区', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '上海某某运输公司', companyPhone: '021-66666666', mailingAddress: '上海市浦东新区张江高科技园区', bank: '中国建设银行上海分行', bankAccount: '6217001234567890123', taxId: '91310000MA2XXXXX2' }, + { id: '3', name: '杭州某某租赁有限公司', creditCode: '91330100MA2XXXXX3', address: '浙江省杭州市余杭区未来科技城', contact: '王五', phone: '13800138003', email: 'wangwu@example.com', companyName: '杭州某某租赁有限公司', companyPhone: '0571-99999999', mailingAddress: '浙江省杭州市余杭区未来科技城', bank: '中国农业银行杭州分行', bankAccount: '6228481234567890123', taxId: '91330100MA2XXXXX3' } + ]; + var deptList = [{ id: 'YW1', name: '业务1部', owners: ['张经理', '李专员', '王专员'] }, { id: 'YW2', name: '业务2部', owners: ['赵经理', '钱专员'] }, { id: 'YW3', name: '业务3部', owners: ['孙经理', '周专员'] }]; + var orgList = ['嘉兴羚牛', '上海羚牛', '广东羚牛']; + var regionList = [ + { province: '浙江省', cities: ['杭州市', '宁波市', '嘉兴市', '湖州市'] }, + { province: '上海市', cities: ['上海市'] }, + { province: '广东省', cities: ['广州市', '深圳市', '东莞市'] } + ]; + var feeTemplates = ['标准费用模板A', '标准费用模板B', '定制费用模板C']; + var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }]; + var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }]; + var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }]; + var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; + var brandList = ['品牌A', '品牌B', '品牌C', '品牌D']; + var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2', '型号C3'], '品牌D': ['型号D1'] }; + var vehicleList = [{ plateNo: '浙A10001', vin: 'L1234567890ABCDEF' }, { plateNo: '浙B20002', vin: 'L2234567890ABCDEF' }, { plateNo: '沪A30003', vin: 'L3234567890ABCDEF' }, { plateNo: '粤A40004', vin: 'L4234567890ABCDEF' }]; + var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板']; + + var filteredCustomers = customerList.filter(function(c) { return !customerSearch || c.name.indexOf(customerSearch) !== -1; }); + var currentDept = deptList.find(function(d) { return d.id === businessDept; }); + var ownerOptions = currentDept ? currentDept.owners : []; + var contractCode = (function() { + var cityCode = 'JX'; + var typeCode = 'ZL'; + var signCode = contractType === '正式合同' ? 'A' : 'B'; + var dateStr = effectiveDate ? effectiveDate.replace(/-/g, '') : '20260216'; + var deptCode = businessDept || 'YW1'; + return cityCode + typeCode + dateStr + deptCode + '01235' + signCode; + })(); + var mainVehicleModelsDisplay = (function() { + var models = []; + var seen = {}; + for (var i = 0; i < rentalOrders.length; i++) { + var m = rentalOrders[i].model; + if (m && !seen[m]) { seen[m] = true; models.push(m); } + } + return models.join('、'); + })(); + var calcRowServiceFee = function(row) { + var sum = 0; + for (var i = 0; i < (row.serviceItems || []).length; i++) { + var fee = parseFloat(row.serviceItems[i].fee); + if (!isNaN(fee)) sum += fee; + } + return sum.toFixed(2); + }; + var rentalTotalVehicles = rentalOrders.length; + var rentalTotalRentService = rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.monthRent) || 0) + (parseFloat(calcRowServiceFee(r)) || 0); }, 0); + var rentalTotalDeposit = rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.deposit) || 0); }, 0); + var rentalTotalHydrogen = (hydrogenPaymentMethod === '预付' && hydrogenBearer === '客户') ? (parseFloat(hydrogenPrepay) || 0) : 0; + + var selectCustomer = function(c) { + setSelectedCustomer(c); + setCustomerSearch(c ? c.name : ''); + setCustomerDropdownOpen(false); + }; + var deliveryRegionDisplay = deliveryProvince && deliveryCity ? deliveryProvince + ' / ' + deliveryCity : ''; + var selectDeliveryRegion = function(province, city) { + setDeliveryProvince(province); + setDeliveryCity(city); + setDeliveryRegionOpen(false); + }; + var scrollToCard = function(id) { + var el = document.getElementById(id); + if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; + var handleOwnerFocus = function() { + if (!businessDept) setOwnerFocusError('请先选择业务部门'); + else setOwnerFocusError(''); + }; + var handleOwnerBlur = function() { setOwnerFocusError(''); }; + var openContractOriginal = function() { + if (contractOriginal && contractOriginal.file) { + var url = URL.createObjectURL(contractOriginal.file); + window.open(url); + } + }; + + var validateSubmitAndReview = function() { + var errs = {}; + if (!selectedCustomer) errs.customer = '请选择客户'; + if (!businessDept) errs.businessDept = '请选择业务部门'; + if (!contractOriginal) errs.contractOriginal = '请上传合同原件'; + if (!businessOwner) errs.businessOwner = '请选择业务负责人'; + if (!projectName || !projectName.trim()) errs.projectName = '请输入项目名称'; + if (!contractType) errs.contractType = '请选择合同类型'; + if (!effectiveDate) errs.effectiveDate = '请选择生效日期'; + if (!paymentMethod) errs.paymentMethod = '请选择付款方式'; + if (!endDate) errs.endDate = '请选择结束日期'; + if (!paymentPeriod) errs.paymentPeriod = '请选择付款周期'; + if (!signingCompany) errs.signingCompany = '请选择签约公司'; + if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; + var authInvalid = authorizedList.some(function(a) { return !a.name || !a.name.trim() || !a.phone || !a.phone.trim() || !a.idCard || !a.idCard.trim(); }); + if (authInvalid) errs.authorizedList = '请完整填写被授权人姓名、联系电话、身份证'; + var rentalInvalid = rentalOrders.some(function(r) { return !r.brand || !r.model || !(r.monthRent && String(r.monthRent).trim()) || !(r.deposit && String(r.deposit).trim()); }); + if (rentalInvalid) errs.rentalOrders = '请完整填写租赁订单的品牌、型号、车辆月租金、保证金'; + if (!hydrogenBearer) errs.hydrogenBearer = '请选择氢费承担方'; + if (hydrogenBearer === '客户' && !hydrogenPaymentMethod) errs.hydrogenPaymentMethod = '请选择付款方式'; + if (hydrogenBearer === '客户' && hydrogenPaymentMethod === '预付' && (!hydrogenPrepay || !String(hydrogenPrepay).trim())) errs.hydrogenPrepay = '请输入氢气预付款'; + if (!returnHydrogenPrice || !String(returnHydrogenPrice).trim()) errs.returnHydrogenPrice = '请输入退还车氢气单价'; + if (!feeTemplate) errs.feeTemplate = '请选择费用模板'; + if (!billingMethod) errs.billingMethod = '请选择账单计算方式'; + setFormErrors(errs); + if (Object.keys(errs).length > 0) { + var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; + 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; + } + return true; + }; + + var addAuthorized = function() { + setAuthorizedList(authorizedList.concat([{ name: '', phone: '', idCard: '' }])); + }; + var removeAuthorized = function(index) { + var next = authorizedList.slice(0); + next.splice(index, 1); + setAuthorizedList(next.length ? next : [{ name: '', phone: '', idCard: '' }]); + }; + var updateAuthorized = function(index, field, value) { + var next = authorizedList.slice(0); + var cur = next[index] || {}; + var patch = {}; + patch[field] = value; + next[index] = Object.assign({}, cur, patch); + setAuthorizedList(next); + }; + + var addRentalRow = function() { + setRentalOrders(rentalOrders.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }])); + }; + var removeRentalRow = function(index) { + var next = rentalOrders.slice(0); + next.splice(index, 1); + setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); + }; + var updateRentalOrder = function(index, field, value) { + var next = rentalOrders.slice(0); + var cur = next[index] || {}; + var newRow = Object.assign({}, cur); + newRow[field] = value; + if (field === 'plateNo') { + var v = vehicleList.find(function(x) { return x.plateNo === value; }); + newRow.vin = v ? v.vin : ''; + } + if (field === 'brand') newRow.model = ''; + next[index] = newRow; + setRentalOrders(next); + }; + var openServiceModal = function(index) { setServiceModalRowIndex(index); }; + var closeServiceModal = function() { setServiceModalRowIndex(null); }; + var addServiceItem = function() { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var removeServiceItem = function(siIndex) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + items.splice(siIndex, 1); + if (items.length === 0) items = [{ project: '', fee: '', effectiveDate: '' }]; + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var updateServiceItem = function(siIndex, field, value) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + var item = items[siIndex] || {}; + var newItem = {}; + for (var k in item) { if (item.hasOwnProperty(k)) newItem[k] = item[k]; } + newItem[field] = value; + items[siIndex] = newItem; + var newRow = {}; + for (var rk in row) { if (row.hasOwnProperty(rk) && rk !== 'serviceItems') newRow[rk] = row[rk]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + + React.useEffect(function() { + if (!deliveryRegionOpen) return; + var handler = function(e) { + if (deliveryRegionClickInsideRef.current) { deliveryRegionClickInsideRef.current = false; return; } + var el = document.getElementById('delivery-region-wrap'); + if (el && !el.contains(e.target)) setDeliveryRegionOpen(false); + }; + document.addEventListener('mousedown', handler); + return function() { document.removeEventListener('mousedown', handler); }; + }, [deliveryRegionOpen]); + + React.useEffect(function() { + if (plateNoFocusRow === null) { setPlateNoDropdownRect(null); return; } + var timer = setTimeout(function() { + var el = document.getElementById('plate-no-input-' + plateNoFocusRow); + if (el && el.getBoundingClientRect) { + var rect = el.getBoundingClientRect(); + setPlateNoDropdownRect({ top: rect.bottom + 2, left: rect.left, width: rect.width }); + } else { setPlateNoDropdownRect(null); } + }, 0); + return function() { clearTimeout(timer); }; + }, [plateNoFocusRow]); + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, + breadcrumb: { marginBottom: 16, color: '#666' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#999', cursor: 'not-allowed' }, + inputError: { borderColor: '#ff4d4f' }, + select: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + textarea: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, minHeight: 80, resize: 'vertical' }, + errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + authRow: { display: 'flex', gap: 12, alignItems: 'flex-start', marginBottom: 12 }, + authInput: { flex: 1, padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4 }, + btnDel: { padding: '8px 16px', color: '#ff4d4f', border: '1px solid #ff4d4f', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer' }, + btnAdd: { padding: '8px 16px', color: '#1890ff', border: '1px dashed #1890ff', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer', marginBottom: 16 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 }, + regionCascader: { position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4, backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: 4, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', zIndex: 10, display: 'flex', minHeight: 200 }, + regionCascaderCol: { flex: 1, borderRight: '1px solid #f0f0f0', overflowY: 'auto' }, + regionCascaderColLast: { flex: 1 }, + regionCascaderItem: { padding: '10px 12px', cursor: 'pointer' }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'top' }, + rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + rentalInput: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 13 }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#999' }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 }, + btnGroup: { display: 'inline-flex', border: '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden' }, + btnGroupItem: { padding: '8px 16px', border: 'none', borderRight: '1px solid #d9d9d9', backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, + btnGroupItemLast: { borderRight: 'none' }, + btnGroupItemActive: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff', borderRightColor: '#1890ff' }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 }, + modalFormInput: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, height: 36, boxSizing: 'border-box' } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var FormItem = function(props) { + var colStyle = props.fullWidth ? styles.formColFull : (props.colStyle ? Object.assign({}, styles.formCol, props.colStyle) : styles.formCol); + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null + ); + }; + + var customerOptions = customerList.map(function(c) { return React.createElement(Option, { key: c.id, value: c.id }, c.name); }); + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, onChange: function(id) { var c = customerList.find(function(x) { return x.id === id; }); selectCustomer(c || null); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, status: formErrors.customer ? 'error' : undefined }, customerOptions)), + React.createElement(FormItem, { label: '客户统一信用代码' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.creditCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.address : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.phone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电子邮箱' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.email : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业名称' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyPhone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.mailingAddress : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setBusinessOwner(v || ''); setOwnerFocusError(''); }, onFocus: handleOwnerFocus, onBlur: handleOwnerBlur, disabled: !businessDept, status: (formErrors.businessOwner || ownerFocusError) ? 'error' : undefined }, ownerOptions.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCode, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同类型', required: true, error: formErrors.contractType }, React.createElement(Select, { placeholder: '请选择合同类型', style: { width: '100%' }, value: contractType || undefined, onChange: function(v) { setContractType(v || ''); }, status: formErrors.contractType ? 'error' : undefined }, React.createElement(Option, { value: '正式合同' }, '正式合同'), React.createElement(Option, { value: '试用合同' }, '试用合同'))), + React.createElement(FormItem, { label: '生效日期', required: true, error: formErrors.effectiveDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效日期', value: effectiveDate && window.moment ? window.moment(effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))), + React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : React.createElement('span', { style: { color: '#999' } }, '根据租赁订单自动反写'))), + React.createElement(FormItem, { label: '结束日期', required: true, error: formErrors.endDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择结束日期', value: endDate && window.moment ? window.moment(endDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEndDate(dateStr || ''); }, status: formErrors.endDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款周期', required: true, error: formErrors.paymentPeriod }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: paymentPeriod || undefined, onChange: function(v) { setPaymentPeriod(v || ''); }, status: formErrors.paymentPeriod ? 'error' : undefined }, [1,2,3,4,5,6,7,8,9,10,11,12].map(function(n) { return React.createElement(Option, { key: n, value: String(n) }, n + '个月'); }))), + React.createElement(FormItem, { label: '签约公司', required: true, error: formErrors.signingCompany }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: signingCompany || undefined, onChange: function(v) { setSigningCompany(v || ''); }, status: formErrors.signingCompany ? 'error' : undefined }, orgList.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))), + React.createElement('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.contractOriginal }, + contractOriginal + ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginal(); } }, contractOriginal.name), + contractOriginal.size || contractOriginal.uploadTime ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size ? contractOriginal.size : '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + ), + React.createElement(Button, { type: 'button', size: 'small', danger: true, onClick: function() { setContractOriginal(null); } }, '删除') + ) + : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement('input', { ref: contractOriginalRef, type: 'file', style: { display: 'none' }, onChange: function(e) { + var f = e.target.files && e.target.files[0]; + if (f) { + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + setContractOriginal({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); + } + e.target.value = ''; + } }), + React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件') + ) + ) + ), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, style: { width: '100%' } })) + ); + var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setRemarks(e.target.value); }, style: styles.textarea, rows: 4 }))); + + var authorizedContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人姓名'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人联系电话'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人身份证'), React.createElement('span', { style: { flex: '0 0 80px' } })), + authorizedList.map(function(item, index) { return React.createElement('div', { key: index, style: styles.authRow }, React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人姓名', value: item.name, onChange: function(e) { updateAuthorized(index, 'name', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人联系电话', value: item.phone, onChange: function(e) { updateAuthorized(index, 'phone', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人身份证号', value: item.idCard, onChange: function(e) { updateAuthorized(index, 'idCard', e.target.value); } }), React.createElement(Button, { type: 'button', danger: true, onClick: function() { removeAuthorized(index); } }, '删除')); }), + formErrors.authorizedList ? React.createElement('div', { style: styles.errMsg }, formErrors.authorizedList) : null, + React.createElement(Button, { type: 'dashed', style: { width: '100%', marginBottom: 16 }, onClick: addAuthorized }, '添加一行') + ); + + var hydrogenFormRow = (function() { + var hydrogenFields = []; + hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); + if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } + var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setReturnHydrogenPrice(e.target.value); }, addonAfter: '元', status: formErrors.returnHydrogenPrice ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenColStyle = hydrogenPaymentMethod === '预付' ? { flex: '1 1 0', minWidth: 180 } : { flex: '0 0 calc(50% - 8px)', minWidth: 180 }; + hydrogenFields.push(React.createElement('div', { key: 'hydrogen-amount-row', style: { display: 'flex', gap: 16, flex: '1 1 100%' } }, hydrogenPaymentMethod === '预付' ? React.createElement(FormItem, { label: '氢气预付款', required: true, error: formErrors.hydrogenPrepay, colStyle: { flex: '1 1 0', minWidth: 180 } }, hydrogenPrepayInput) : null, React.createElement(FormItem, { label: '退还车氢气单价', required: true, error: formErrors.returnHydrogenPrice, colStyle: returnHydrogenColStyle }, returnHydrogenInput))); + return React.createElement.apply(React, ['div', { style: Object.assign({}, styles.formRow, { marginTop: 20 }) }].concat(hydrogenFields)); + })(); + + var plateNoOptions = vehicleList.map(function(v) { return React.createElement(Option, { key: v.plateNo, value: v.plateNo }, v.plateNo); }); + var rentalTableBody = rentalOrders.map(function(row, idx) { + var modelOpts = row.brand ? (modelByBrand[row.brand] || []) : []; + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTdCenter }, idx + 1), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.brand || undefined, onChange: function(v) { updateRentalOrder(idx, 'brand', v || ''); }, placeholder: '请选择' }, brandList.map(function(b, i) { return React.createElement(Option, { key: i, value: b }, b); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.model || undefined, onChange: function(v) { updateRentalOrder(idx, 'model', v || ''); }, disabled: !row.brand, placeholder: '请选择' }, modelOpts.map(function(m, i) { return React.createElement(Option, { key: i, value: m }, m); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择或输入搜索', value: row.plateNo || undefined, onChange: function(v) { updateRentalOrder(idx, 'plateNo', v || ''); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; } }, plateNoOptions)), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { value: row.vin || '', disabled: true, style: { width: '100%' } })), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.monthRent || '', onChange: function(e) { updateRentalOrder(idx, 'monthRent', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(idx); } }, '管理')), + React.createElement('td', { style: styles.rentalTdCenter }, calcRowServiceFee(row) + ' 元'), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateRentalOrder(idx, 'deposit', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '备注', value: row.remark || '', onChange: function(e) { updateRentalOrder(idx, 'remark', e.target.value); }, style: Object.assign({}, styles.rentalInput, { width: '100%' }) })), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeRentalRow(idx); } }, '删除')) + ); + }); + + var rentalSummary = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalVehicles + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalRentService.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalDeposit.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalHydrogen.toFixed(2) + ' 元')) + ); + var reqStarStyle = { color: '#ff4d4f', marginRight: 4 }; + var reqStar = React.createElement('span', { style: reqStarStyle }, '*'); + var rentalTh1 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 50, verticalAlign: 'middle' }) }, '序号'); + var rentalTh2 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '品牌'); + var rentalTh3 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '型号'); + var rentalTh4 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, '车牌号'); + var rentalTh5 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 160 }) }, '车辆识别代码'); + var rentalTh6 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, reqStar, '车辆月租金'); + var rentalTh7 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80, verticalAlign: 'middle' }) }, '服务费项目'); + var rentalTh8 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 90, verticalAlign: 'middle' }) }, '服务费'); + var rentalTh9 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '保证金'); + var rentalTh10 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '备注'); + var rentalTh11 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 60, verticalAlign: 'middle' }) }, '操作'); + var rentalTableThead = React.createElement('thead', null, React.createElement('tr', null, rentalTh1, rentalTh2, rentalTh3, rentalTh4, rentalTh5, rentalTh6, rentalTh7, rentalTh8, rentalTh9, rentalTh10, rentalTh11)); + var rentalTableTbody = React.createElement('tbody', null, rentalTableBody); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody); + var rentalTableWrap = React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl); + var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); + + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.project); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.standard); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.serviceFee); + return React.createElement('tr', { key: i }, td1, td2, td3); + }; + var makeFeeRow5 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.category); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.part); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.partName); + var td4 = React.createElement('td', { style: styles.rentalTd }, r.qty); + var td5 = React.createElement('td', { style: styles.rentalTd }, r.feeDetail); + return React.createElement('tr', { key: i }, td1, td2, td3, td4, td5); + }; + var feeCertRows = feeTemplateCertFees.map(makeFeeRow3); + var feePenaltyRows = feeTemplatePenaltyFees.map(makeFeeRow3); + var feeConsumablesRows = feeTemplateConsumables.map(makeFeeRow5); + var feeOtherRows = feeTemplateOtherFees.map(makeFeeRow3); + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeCertRows)); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows)); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows)); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows)); + var feeTemplateSelect = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择费用模板', required: true, error: formErrors.feeTemplate }, React.createElement(Select, { placeholder: '请选择费用模板', style: { width: '100%' }, value: feeTemplate || undefined, onChange: function(v) { setFeeTemplate(v || ''); }, status: formErrors.feeTemplate ? 'error' : undefined }, feeTemplates.map(function(f, i) { return React.createElement(Option, { key: i, value: f }, f); })))); + var feeTemplateBody = feeTemplate ? React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), + feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), + feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), + feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), + feeOtherTable + ) : null; + var feeContent = React.createElement('div', null, feeTemplateSelect, feeTemplateBody); + + var billingBtnBase = { padding: '12px 16px', border: '1px solid #d9d9d9', borderRadius: 0, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14, textAlign: 'left', flex: 1, minWidth: 0, width: 0, height: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', overflow: 'visible' }; + var billingContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', border: formErrors.billingMethod ? '1px solid #ff4d4f' : '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden', alignItems: 'stretch' } }, + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) + ), + formErrors.billingMethod ? React.createElement('div', { style: styles.errMsg }, formErrors.billingMethod) : null + ); + + var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; + var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; + + var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; + var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; + var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; + var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; + var reqSpecBlock = { marginBottom: 8 }; + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-新增租赁合同')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) + ); + var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) + ); + var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) + ); + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 640 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '新增租赁合同')), React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')), + React.createElement('div', { style: styles.anchorWrap }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式') + ), + React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))), + React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)), + React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)), + React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { style: { height: 60 } }), + serviceModalContent, + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { message.success('租赁合同已提交审核。'); } } }, '提交并审核'), React.createElement(Button, { onClick: function() { message.info('保存,加入租赁合同列表(仅操作人可查看编辑)'); } }, '保存'), React.createElement(Button, { onClick: function() { message.info('取消'); } }, '取消')) + ); +}; diff --git a/web端/车辆租赁合同/新增车辆.jsx b/web端/车辆租赁合同/新增车辆.jsx new file mode 100644 index 0000000..1f65e5f --- /dev/null +++ b/web端/车辆租赁合同/新增车辆.jsx @@ -0,0 +1,448 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 业务管理 - 车辆租赁合同 - 新增车辆(与查看合同页布局一致,仅新增车辆信息卡片可编辑) + +const Component = function() { + var useState = React.useState; + var useCallback = React.useCallback; + var antd = window.antd; + var Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Table = antd.Table; + var DatePicker = antd.DatePicker; + var message = antd.message; + var Option = Select.Option; + + var cc1State = React.useState(false); + var cc1 = cc1State[0]; + var setCc1 = cc1State[1]; + var cc2State = React.useState(false); + var cc2 = cc2State[0]; + var setCc2 = cc2State[1]; + var cc3State = React.useState(false); + var cc3 = cc3State[0]; + var setCc3 = cc3State[1]; + var cc4State = React.useState(false); + var cc4 = cc4State[0]; + var setCc4 = cc4State[1]; + var cc5State = React.useState(false); + var cc5 = cc5State[0]; + var setCc5 = cc5State[1]; + var cc6State = React.useState(false); + var cc6 = cc6State[0]; + var setCc6 = cc6State[1]; + var cc7State = React.useState(false); + var cc7 = cc7State[0]; + var setCc7 = cc7State[1]; + + var emptyNewRow = { brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }; + var _newVehicleRows = useState([Object.assign({}, emptyNewRow)]); + var newVehicleRows = _newVehicleRows[0]; + var setNewVehicleRows = _newVehicleRows[1]; + var _serviceModalRowIndex = useState(null); + var serviceModalRowIndex = _serviceModalRowIndex[0]; + var setServiceModalRowIndex = _serviceModalRowIndex[1]; + + var brandList = ['品牌A', '品牌B', '品牌C', '品牌D']; + var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板']; + var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2'], '品牌D': ['型号D1'] }; + var plateNoOptions = ['浙A10001', '浙A10002', '浙B20001', '浙B20002', '浙C30001', '浙C30002', '沪D40001', '沪D40002', '苏E50001', '苏E50002', '京F60001', '京F60002'].map(function(p) { return { value: p, label: p }; }); + + var addNewVehicleRow = useCallback(function() { + setNewVehicleRows(function(prev) { return prev.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); }); + }, []); + var removeNewVehicleRow = useCallback(function(index) { + setNewVehicleRows(function(prev) { + var next = prev.slice(); + next.splice(index, 1); + if (next.length === 0) next = [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]; + return next; + }); + }, []); + var updateNewVehicleRow = useCallback(function(index, field, value) { + setNewVehicleRows(function(prev) { + var next = prev.slice(); + var row = next[index] || emptyNewRow; + var o = {}; + o[field] = value; + if (field === 'brand') o.model = ''; + if (field === 'plateNo') o.vin = value; + next[index] = Object.assign({}, row, o); + return next; + }); + }, []); + + var calcRowServiceFee = function(row) { + var sum = 0; + for (var i = 0; i < (row.serviceItems || []).length; i++) { + var fee = parseFloat(row.serviceItems[i].fee); + if (!isNaN(fee)) sum += fee; + } + return sum.toFixed(2); + }; + var openServiceModal = function(index) { setServiceModalRowIndex(index); }; + var closeServiceModal = function() { setServiceModalRowIndex(null); }; + var addServiceItem = function() { + if (serviceModalRowIndex === null) return; + var next = newVehicleRows.slice(); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setNewVehicleRows(next); + }; + var removeServiceItem = function(siIndex) { + if (serviceModalRowIndex === null) return; + var next = newVehicleRows.slice(); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(); + items.splice(siIndex, 1); + if (items.length === 0) items = [{ project: '', fee: '', effectiveDate: '' }]; + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setNewVehicleRows(next); + }; + var updateServiceItem = function(siIndex, field, value) { + if (serviceModalRowIndex === null) return; + var next = newVehicleRows.slice(); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(); + var item = items[siIndex] || {}; + var newItem = {}; + for (var k in item) { if (item.hasOwnProperty(k)) newItem[k] = item[k]; } + newItem[field] = value; + items[siIndex] = newItem; + var newRow = {}; + for (var rk in row) { if (row.hasOwnProperty(rk) && rk !== 'serviceItems') newRow[rk] = row[rk]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setNewVehicleRows(next); + }; + var newOrderSummary = (function() { + var count = newVehicleRows.length; + var rentService = 0; + var depositTotal = 0; + for (var i = 0; i < newVehicleRows.length; i++) { + rentService += parseFloat(newVehicleRows[i].monthRent) || 0; + rentService += parseFloat(calcRowServiceFee(newVehicleRows[i])) || 0; + depositTotal += parseFloat(newVehicleRows[i].deposit) || 0; + } + return { vehicleCount: count, totalRentService: rentService.toFixed(2), totalDeposit: depositTotal.toFixed(2) }; + })(); + + var scrollToCard = function(id) { + var el = document.getElementById(id); + if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, + breadcrumb: { marginBottom: 16, color: '#666' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#666', cursor: 'default', borderColor: '#e8e8e8' }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 }, + sectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginBottom: 12 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 }, + rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var FormItemReadOnly = function(props) { + var colStyle = props.fullWidth ? styles.formColFull : styles.formCol; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, props.label), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled) }, props.value || '—') + ); + }; + + var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }; + var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + var mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', remarks: '' }; + var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }]; + var mockRentalOrders = [{ brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }, { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }]; + var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' }; + var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' }; + var mockFeeTemplate = '标准费用模板A'; + var mockBillingMethod = '按自然月结算'; + var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }]; + var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }]; + var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }]; + var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.project), React.createElement('td', { style: styles.rentalTd }, r.standard), React.createElement('td', { style: styles.rentalTd }, r.serviceFee)); }; + var makeFeeRow5 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.category), React.createElement('td', { style: styles.rentalTd }, r.part), React.createElement('td', { style: styles.rentalTd }, r.partName), React.createElement('td', { style: styles.rentalTd }, r.qty), React.createElement('td', { style: styles.rentalTd }, r.feeDetail)); }; + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateCertFees.map(makeFeeRow3))); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplatePenaltyFees.map(makeFeeRow3))); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeTemplateConsumables.map(makeFeeRow5))); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateOtherFees.map(makeFeeRow3))); + var feeTemplateBody = React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), feeOtherTable + ); + + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '客户名称', value: mockCustomer.name }), + React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockCustomer.creditCode }), + React.createElement(FormItemReadOnly, { label: '客户地址', value: mockCustomer.address }), + React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockCustomer.contact }), + React.createElement(FormItemReadOnly, { label: '客户电话', value: mockCustomer.phone }), + React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockCustomer.email }), + React.createElement(FormItemReadOnly, { label: '企业名称', value: mockCustomer.companyName }), + React.createElement(FormItemReadOnly, { label: '企业电话', value: mockCustomer.companyPhone }), + React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockCustomer.mailingAddress }), + React.createElement(FormItemReadOnly, { label: '开户银行', value: mockCustomer.bank }), + React.createElement(FormItemReadOnly, { label: '银行账号', value: mockCustomer.bankAccount }), + React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockCustomer.taxId }), + React.createElement(FormItemReadOnly, { label: '业务部门', value: '业务1部' }), + React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' }) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }), + React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }), + React.createElement(FormItemReadOnly, { label: '合同类型', value: mockContract.contractType }), + React.createElement(FormItemReadOnly, { label: '生效日期', value: mockContract.effectiveDate }), + React.createElement(FormItemReadOnly, { label: '付款方式', value: mockContract.paymentMethod }), + React.createElement(FormItemReadOnly, { label: '主要车型', value: mockContract.mainVehicleModels }), + React.createElement(FormItemReadOnly, { label: '结束日期', value: mockContract.endDate }), + React.createElement(FormItemReadOnly, { label: '付款周期', value: mockContract.paymentPeriod }), + React.createElement(FormItemReadOnly, { label: '签约公司', value: mockContract.signingCompany }), + React.createElement('div', { style: styles.formCol }, + React.createElement(FormItemReadOnly, { label: '交车区域', value: mockContract.deliveryRegion }), + React.createElement('div', null, + React.createElement('label', { style: styles.label }, '合同原件'), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) }, + contractOriginal + ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name), + (contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + ) + : '—' + ) + ) + ), + React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }) + ); + var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' })); + + var authorizedContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人姓名'), + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人联系电话'), + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人身份证') + ), + mockAuthorized.map(function(item, index) { + return React.createElement('div', { key: index, style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } }, + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.name), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.phone), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.idCard) + ); + }) + ); + + var rentalSummaryEl = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.vehicleCount + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalRentService + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalDeposit + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.hydrogenPrepay + ' 元')) + ); + var rentalTableBody = mockRentalOrders.map(function(row, idx) { + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTd }, idx + 1), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.brand)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.model)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.plateNo)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vin)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—')) + ); + }); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '品牌'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '型号'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '车牌号'), + React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆识别代码'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '车辆月租金'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注') + ) + ), + React.createElement('tbody', null, rentalTableBody) + ); + var hydrogenReadOnly = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '氢费承担方', value: mockHydrogen.bearer }), + React.createElement(FormItemReadOnly, { label: '付款方式', value: mockHydrogen.paymentMethod }), + React.createElement(FormItemReadOnly, { label: '氢气预付款', value: mockHydrogen.hydrogenPrepay + ' 元' }), + React.createElement(FormItemReadOnly, { label: '退还车氢气单价', value: mockHydrogen.returnPrice + ' 元' }) + ); + var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly); + + var newVehicleColumns = [ + { title: '序号', key: 'no', width: 60, render: function(_, __, i) { return i + 1; } }, + { title: '品牌', key: 'brand', width: 90, render: function(_, row, i) { return React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: row.brand || undefined, onChange: function(v) { updateNewVehicleRow(i, 'brand', v || ''); }, options: brandList.map(function(b) { return { value: b, label: b }; }), allowClear: true }); } }, + { title: '型号', key: 'model', width: 100, render: function(_, row, i) { var opts = (row.brand && modelByBrand[row.brand]) ? modelByBrand[row.brand].map(function(m) { return { value: m, label: m }; }) : []; return React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: row.model || undefined, onChange: function(v) { updateNewVehicleRow(i, 'model', v || ''); }, options: opts, allowClear: true, disabled: !row.brand }); } }, + { title: '车牌号', key: 'plateNo', width: 120, render: function(_, row, i) { return React.createElement(Select, { placeholder: '请选择或输入搜索', style: { width: '100%' }, value: row.plateNo || undefined, onChange: function(v) { updateNewVehicleRow(i, 'plateNo', v || ''); }, options: plateNoOptions, allowClear: true, showSearch: true, filterOption: function(input, option) { var label = (option && option.label) ? option.label : ''; return label.toLowerCase().indexOf((input || '').toLowerCase()) >= 0; } }); } }, + { title: '车辆识别代码', key: 'vin', width: 160, render: function(_, row, i) { return React.createElement(Input, { placeholder: '由车牌号反写', value: row.plateNo || row.vin || '', disabled: true }); } }, + { title: '车辆月租金', key: 'monthRent', width: 120, render: function(_, row, i) { return React.createElement(Input, { placeholder: '0.00', value: row.monthRent || '', onChange: function(e) { updateNewVehicleRow(i, 'monthRent', e.target.value); }, addonAfter: '元' }); } }, + { title: '服务费项目', key: 'serviceItems', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(i); } }, '管理'); } }, + { title: '服务费', key: 'serviceFee', width: 90, render: function(_, row, i) { return calcRowServiceFee(row) + ' 元'; } }, + { title: '保证金', key: 'deposit', width: 100, render: function(_, row, i) { return React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateNewVehicleRow(i, 'deposit', e.target.value); }, addonAfter: '元' }); } }, + { title: '备注', key: 'remark', width: 80, render: function(_, row, i) { return React.createElement(Input, { placeholder: '请输入', value: row.remark || '', onChange: function(e) { updateNewVehicleRow(i, 'remark', e.target.value); } }); } }, + { title: '操作', key: 'action', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeNewVehicleRow(i); } }, '删除'); } } + ]; + + var newOrderSummaryEl = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.vehicleCount + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.totalRentService + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.totalDeposit + ' 元')) + ); + + var serviceModalRows = serviceModalRowIndex !== null && newVehicleRows[serviceModalRowIndex] ? newVehicleRows[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) { + 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 newVehicleCardContent = React.createElement('div', null, + React.createElement('div', { style: styles.sectionTitle }, '新增订单'), + newOrderSummaryEl, + React.createElement(Table, { rowKey: function(_, i) { return String(i); }, size: 'small', columns: newVehicleColumns, dataSource: newVehicleRows, pagination: false, scroll: { x: 1100 } }), + React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addNewVehicleRow }, '添加一行') + ); + + var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody); + var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod)); + var attachmentContent = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '盖章合同附件')), + React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { color: '#666', fontSize: 14 } }, '租赁合同-盖章版.pdf · 1.2 MB · 2026-02-16 14:30')) + ); + + var changeHistorySorted = [{ changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' }]; + var historyTableRows = changeHistorySorted.map(function(row, index) { + return React.createElement('tr', { key: index }, + React.createElement('td', { style: styles.rentalTd }, index + 1), + React.createElement('td', { style: styles.rentalTd }, row.changeTime), + React.createElement('td', { style: styles.rentalTd }, row.opType), + React.createElement('td', { style: styles.rentalTd }, row.operator), + React.createElement('td', { style: styles.rentalTd }, row.remark) + ); + }); + var historyTable = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 60 }, '序号'), + React.createElement('th', { style: styles.rentalTh, width: 140 }, '变更时间'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '操作类型'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '操作人'), + React.createElement('th', { style: styles.rentalTh }, '备注') + ) + ), + React.createElement('tbody', null, historyTableRows) + ); + var changeHistoryContent = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '合同变更历史记录')), + React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { overflowX: 'auto' } }, historyTable)) + ); + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '新增车辆')) + ), + React.createElement('div', { style: styles.anchorWrap }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-new-vehicle'); } }, '新增车辆信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'), + React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录') + ), + React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))), + React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)), + React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)), + React.createElement('div', { id: 'card-new-vehicle', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '新增车辆信息', collapsed: cc7, setCollapsed: setCc7 }, newVehicleCardContent)), + React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, attachmentContent), + React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, changeHistoryContent), + React.createElement('div', { style: { height: 60 } }), + serviceModalContent, + React.createElement('div', { style: styles.footer }, + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { message.info('取消(原型)'); } }, '取消'), + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function() { message.success('已提交审核,审核通过后生效(原型)'); } }, '提交审核') + ) + ); +}; diff --git a/web端/车辆租赁合同/查看租赁合同.jsx b/web端/车辆租赁合同/查看租赁合同.jsx new file mode 100644 index 0000000..b325197 --- /dev/null +++ b/web端/车辆租赁合同/查看租赁合同.jsx @@ -0,0 +1,448 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 车辆资产管理系统 - 查看租赁合同模块(只读 + 审批状态 + 盖章附件,IE11+ 兼容) + +const Component = function() { + var cc1State = React.useState(false); + var cc1 = cc1State[0]; + var setCc1 = cc1State[1]; + var cc2State = React.useState(false); + var cc2 = cc2State[0]; + var setCc2 = cc2State[1]; + var cc3State = React.useState(false); + var cc3 = cc3State[0]; + var setCc3 = cc3State[1]; + var cc4State = React.useState(false); + var cc4 = cc4State[0]; + var setCc4 = cc4State[1]; + var cc5State = React.useState(false); + var cc5 = cc5State[0]; + var setCc5 = cc5State[1]; + var cc6State = React.useState(false); + var cc6 = cc6State[0]; + var setCc6 = cc6State[1]; + var attachmentHoverState = React.useState(false); + var attachmentHover = attachmentHoverState[0]; + var setAttachmentHover = attachmentHoverState[1]; + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + var servicePopoverRowState = React.useState(null); + var servicePopoverRow = servicePopoverRowState[0]; + var setServicePopoverRow = servicePopoverRowState[1]; + + // 模拟已上传的盖章合同附件 + var uploadedFile = { name: '租赁合同-盖章版.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + + // 合同变更历史记录(变更时间倒序,序号 1.2.3...) + var changeHistoryRaw = [ + { changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' }, + { changeTime: '2026-02-16 10:30', opType: '附加费用', operator: '李四', remark: '添加服务项目"保养费用",费用为"200",生效时间为"2026-03-01"\n添加服务项目"清洗费",费用为"80",生效时间为"2026-03-01"' }, + { changeTime: '2026-02-15 16:20', opType: '添加授权人', operator: '王五', remark: '添加授权人"李四"' }, + { changeTime: '2026-02-14 09:15', opType: '合同续签', operator: '赵六', remark: '原合同编码"JXZL20250210YW101100A"' }, + { changeTime: '2026-02-13 10:00', opType: '终止合同', operator: '周九', remark: '终止原因"客户提前解约"' }, + { changeTime: '2026-02-12 11:00', opType: '撤回合同', operator: '钱七', remark: '-' }, + { changeTime: '2026-02-10 14:30', opType: '转正式合同', operator: '孙八', remark: '原合同编码"JXZL20260210YW101230B"' } + ]; + var changeHistorySorted = changeHistoryRaw.slice().sort(function(a, b) { return b.changeTime.localeCompare(a.changeTime); }); + + var scrollToCard = function(id) { + var el = document.getElementById(id); + if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, + breadcrumb: { marginBottom: 16, color: '#666' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#666', cursor: 'default', borderColor: '#e8e8e8' }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 }, + tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + approvalCard: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + approvalCardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600, color: '#333', textAlign: 'center' }, + approvalCardBody: { padding: '24px 20px', display: 'flex', justifyContent: 'center' }, + stepWrap: { display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', justifyContent: 'center' }, + stepItem: { flex: '1 1 0', minWidth: 140, maxWidth: 220, textAlign: 'center', position: 'relative' }, + stepIcon: { width: 32, height: 32, borderRadius: '50%', margin: '0 auto 8px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, fontWeight: 600 }, + stepIconDone: { backgroundColor: '#52c41a', color: '#fff' }, + stepLine: { position: 'absolute', top: 16, left: '50%', right: '-50%', height: 2, backgroundColor: '#e8e8e8', zIndex: 0 }, + stepLineDone: { backgroundColor: '#52c41a' }, + stepTitle: { fontSize: 13, color: '#333', fontWeight: 500, marginBottom: 4 }, + stepDesc: { fontSize: 12, color: '#666' }, + stepStatus: { fontSize: 12, color: '#52c41a', marginTop: 4 }, + stepTime: { fontSize: 12, color: '#999', marginTop: 2 }, + attachmentRow: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 12 }, + attachmentFile: { display: 'inline-flex', alignItems: 'center', padding: '8px 12px', backgroundColor: '#f5f5f5', borderRadius: 4, border: '1px solid #d9d9d9', fontSize: 14, color: '#1890ff', cursor: 'pointer', textDecoration: 'none', fontFamily: 'inherit', margin: 0, outline: 'none' }, + attachmentFileHover: { color: '#40a9ff', borderColor: '#1890ff', backgroundColor: '#e6f7ff' }, + attachmentMeta: { fontSize: 12, color: '#999', marginLeft: 8 }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 }, + historyLink: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var FormItemReadOnly = function(props) { + var colStyle = props.fullWidth ? styles.formColFull : styles.formCol; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, props.label), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled) }, props.value || '—') + ); + }; + + // 审批流程步骤数据(全部已通过,含审核时间 YYYY-MM-DD HH:MM) + var approvalSteps = [ + { title: '业务部主管', person: '姚守涛', status: '已通过', approveTime: '2026-02-16 09:30' }, + { title: '事业部主管', person: '尚建华', status: '已通过', approveTime: '2026-02-16 10:15' }, + { title: '财务部', person: '宋欣怡 / 吕红', status: '已通过', approveTime: '2026-02-16 11:20' }, + { title: '法务部', person: '高洁 / 彭青松', status: '已通过', approveTime: '2026-02-16 14:00' } + ]; + + var approvalStepEls = approvalSteps.map(function(step, index) { + var isLast = index === approvalSteps.length - 1; + var lineStyle = Object.assign({}, styles.stepLine, step.status === '已通过' ? styles.stepLineDone : {}); + if (isLast) lineStyle.display = 'none'; + return React.createElement('div', { key: index, style: Object.assign({}, styles.stepItem, { zIndex: approvalSteps.length - index }) }, + !isLast ? React.createElement('div', { style: lineStyle }) : null, + React.createElement('div', { style: Object.assign({}, styles.stepIcon, styles.stepIconDone) }, '✓'), + React.createElement('div', { style: styles.stepTitle }, step.title), + React.createElement('div', { style: styles.stepDesc }, step.person), + React.createElement('div', { style: styles.stepStatus }, step.status), + step.approveTime ? React.createElement('div', { style: styles.stepTime }, step.approveTime) : null + ); + }); + + var approvalCardEl = React.createElement('div', { style: styles.approvalCard }, + React.createElement('div', { style: styles.approvalCardHeader }, '审批状态'), + React.createElement('div', { style: styles.approvalCardBody }, + React.createElement('div', { style: styles.stepWrap }, approvalStepEls) + ) + ); + + // 模拟只读数据(与新增合同同结构) + var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }; + var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' }; + var mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', remarks: '' }; + var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }]; + var mockRentalOrders = [ + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }, { project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] }, + { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] } + ]; + var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' }; + var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' }; + var mockFeeTemplate = '标准费用模板A'; + var mockBillingMethod = '按自然月结算'; + var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }]; + var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }]; + var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }]; + var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.project); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.standard); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.serviceFee); + return React.createElement('tr', { key: i }, td1, td2, td3); + }; + var makeFeeRow5 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.category); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.part); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.partName); + var td4 = React.createElement('td', { style: styles.rentalTd }, r.qty); + var td5 = React.createElement('td', { style: styles.rentalTd }, r.feeDetail); + return React.createElement('tr', { key: i }, td1, td2, td3, td4, td5); + }; + var feeCertRows = feeTemplateCertFees.map(makeFeeRow3); + var feePenaltyRows = feeTemplatePenaltyFees.map(makeFeeRow3); + var feeConsumablesRows = feeTemplateConsumables.map(makeFeeRow5); + var feeOtherRows = feeTemplateOtherFees.map(makeFeeRow3); + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeCertRows)); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows)); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows)); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows)); + var feeTemplateBody = React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), + feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), + feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), + feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), + feeOtherTable + ); + + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '客户名称', value: mockCustomer.name }), + React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockCustomer.creditCode }), + React.createElement(FormItemReadOnly, { label: '客户地址', value: mockCustomer.address }), + React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockCustomer.contact }), + React.createElement(FormItemReadOnly, { label: '客户电话', value: mockCustomer.phone }), + React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockCustomer.email }), + React.createElement(FormItemReadOnly, { label: '企业名称', value: mockCustomer.companyName }), + React.createElement(FormItemReadOnly, { label: '企业电话', value: mockCustomer.companyPhone }), + React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockCustomer.mailingAddress }), + React.createElement(FormItemReadOnly, { label: '开户银行', value: mockCustomer.bank }), + React.createElement(FormItemReadOnly, { label: '银行账号', value: mockCustomer.bankAccount }), + React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockCustomer.taxId }), + React.createElement(FormItemReadOnly, { label: '业务部门', value: '业务1部' }), + React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' }) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }), + React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }), + React.createElement(FormItemReadOnly, { label: '合同类型', value: mockContract.contractType }), + React.createElement(FormItemReadOnly, { label: '生效日期', value: mockContract.effectiveDate }), + React.createElement(FormItemReadOnly, { label: '付款方式', value: mockContract.paymentMethod }), + React.createElement(FormItemReadOnly, { label: '主要车型', value: mockContract.mainVehicleModels }), + React.createElement(FormItemReadOnly, { label: '结束日期', value: mockContract.endDate }), + React.createElement(FormItemReadOnly, { label: '付款周期', value: mockContract.paymentPeriod }), + React.createElement(FormItemReadOnly, { label: '签约公司', value: mockContract.signingCompany }), + React.createElement('div', { style: styles.formCol }, + React.createElement(FormItemReadOnly, { label: '交车区域', value: mockContract.deliveryRegion }), + React.createElement('div', null, + React.createElement('label', { style: styles.label }, '合同原件'), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) }, + contractOriginal + ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name), + (contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + ) + : '—' + ) + ) + ), + React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation }) + ); + var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' })); + + var authorizedContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人姓名'), + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人联系电话'), + React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人身份证') + ), + mockAuthorized.map(function(item, index) { + return React.createElement('div', { key: index, style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } }, + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.name), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.phone), + React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.idCard) + ); + }) + ); + + var rentalSummaryEl = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.vehicleCount + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalRentService + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalDeposit + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.hydrogenPrepay + ' 元')) + ); + var Popover = window.antd && window.antd.Popover; + var renderServiceItemsPopover = function(row) { + var items = row.serviceItems || []; + if (items.length === 0) { + return React.createElement('div', { style: { padding: 12, minWidth: 320, fontSize: 13 } }, '暂无服务项明细'); + } + var thead = 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 }, '生效时间') + ) + ); + var tbody = React.createElement('tbody', null, + items.map(function(si, i) { + return React.createElement('tr', { key: i }, + React.createElement('td', { style: styles.rentalTd }, si.project || '—'), + React.createElement('td', { style: styles.rentalTd }, si.fee != null && si.fee !== '' ? si.fee + ' 元' : '—'), + React.createElement('td', { style: styles.rentalTd }, si.effectiveDate || '—') + ); + }) + ); + return React.createElement('div', { style: { padding: 8 } }, + React.createElement('table', { style: styles.rentalTable }, thead, tbody) + ); + }; + var rentalTableBody = mockRentalOrders.map(function(row, idx) { + var servicePopoverContent = renderServiceItemsPopover(row); + var serviceCell = Popover + ? React.createElement(Popover, { content: servicePopoverContent, title: '服务项明细', trigger: 'click' }, + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 } }, '查看')) + : React.createElement('span', { + style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 }, + onClick: function() { setServicePopoverRow(servicePopoverRow === idx ? null : idx); } + }, '查看'); + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTd }, idx + 1), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.brand)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.model)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.plateNo)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vin)), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')), + React.createElement('td', { style: styles.rentalTd }, serviceCell), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—')) + ); + }); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '品牌'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '型号'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '车牌号'), + React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆识别代码'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '车辆月租金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '服务费项目'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'), + React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注') + ) + ), + React.createElement('tbody', null, rentalTableBody) + ); + var hydrogenReadOnly = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItemReadOnly, { label: '氢费承担方', value: mockHydrogen.bearer }), + React.createElement(FormItemReadOnly, { label: '付款方式', value: mockHydrogen.paymentMethod }), + React.createElement(FormItemReadOnly, { label: '氢气预付款', value: mockHydrogen.hydrogenPrepay + ' 元' }), + React.createElement(FormItemReadOnly, { label: '退还车氢气单价', value: mockHydrogen.returnPrice + ' 元' }) + ); + var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly); + + var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody); + + var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod)); + + var attachmentContent = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, + React.createElement('span', { style: styles.cardTitle }, '盖章合同附件') + ), + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.attachmentRow }, + React.createElement('button', { + type: 'button', + style: Object.assign({}, styles.attachmentFile, attachmentHover ? styles.attachmentFileHover : {}), + onMouseEnter: function() { setAttachmentHover(true); }, + onMouseLeave: function() { setAttachmentHover(false); }, + onClick: function() { window.open('#', '_blank'); } + }, '📄 ' + uploadedFile.name, React.createElement('span', { style: styles.attachmentMeta }, uploadedFile.size + ' · ' + uploadedFile.uploadTime)) + ) + ) + ); + + var historyTableRows = changeHistorySorted.map(function(row, index) { + return React.createElement('tr', { key: index }, + React.createElement('td', { style: styles.rentalTd }, index + 1), + React.createElement('td', { style: styles.rentalTd }, row.changeTime), + React.createElement('td', { style: styles.rentalTd }, row.opType), + React.createElement('td', { style: styles.rentalTd }, row.operator), + React.createElement('td', { style: styles.rentalTd }, React.createElement('button', { type: 'button', style: styles.historyLink, onClick: function() { window.open('#', '_blank'); } }, '查看变更前记录')), + React.createElement('td', { style: Object.assign({}, styles.rentalTd, { verticalAlign: 'top', whiteSpace: 'pre-line' }) }, row.remark) + ); + }); + var historyTable = React.createElement('table', { style: styles.rentalTable }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: styles.rentalTh, width: 60 }, '序号'), + React.createElement('th', { style: styles.rentalTh, width: 140 }, '变更时间'), + React.createElement('th', { style: styles.rentalTh, width: 100 }, '操作类型'), + React.createElement('th', { style: styles.rentalTh, width: 90 }, '操作人'), + React.createElement('th', { style: styles.rentalTh, width: 120 }, '原始记录'), + React.createElement('th', { style: styles.rentalTh }, '备注') + ) + ), + React.createElement('tbody', null, historyTableRows) + ); + var changeHistoryContent = React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardHeader }, + React.createElement('span', { style: styles.cardTitle }, '合同变更历史记录') + ), + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: { overflowX: 'auto' } }, historyTable) + ) + ); + + var reqSpecBlock = { marginBottom: 8 }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; + var reqSpecH2First = { marginTop: 0 }; + var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; + var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: Object.assign({}, reqSpecH2, reqSpecH2First) }, '1.其他部分与新增租赁合同相同,只是不可编辑,新增以下几块内容:')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.审批状态卡片:'), React.createElement('div', { style: reqSpecP }, '显示所有审批步骤及当前节点、审批人姓名、已通过/待审批/驳回三种状态;最下方为对应审批节点审批操作时间,格式为 YYYY-MM-DD HH:MM。')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.盖章合同附件:'), React.createElement('div', { style: reqSpecP }, '当法务未完成审核并上传盖章合同时,盖章合同附件显示为暂无附件;已上传盖章合同时,显示合同文件名称、文件大小、上传时间(YYYY-MM-DD HH:MM)。')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.合同变更历史记录:'), React.createElement('div', { style: reqSpecP }, '列表字段为序号、变更时间、操作类型、操作人、原始记录、备注。'), React.createElement('div', { style: reqSpecLi }, '4.1.序号:1.2.3...以此类推;'), React.createElement('div', { style: reqSpecLi }, '4.2.变更时间:YYYY-MM-DD HH:MM 格式,倒序展示数据;'), React.createElement('div', { style: reqSpecLi }, '4.3.操作类型:包括变更内容、合同续签、撤回合同、添加授权人、附加费用、转正式合同、终止合同;'), React.createElement('div', { style: reqSpecLi }, '4.4.操作人:记录对应操作用户名称;'), React.createElement('div', { style: reqSpecLi }, '4.5.原始记录:显示查看变更前记录,点击会新开页查看未修改前合同原始记录;'), React.createElement('div', { style: reqSpecLi }, '4.6.备注:'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.1.操作类型为变更内容时,显示:"字段名"由"xx"修改为"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.2.操作类型为合同续签时,显示:原合同编码"xxxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.3.操作类型为撤回合同时,显示:-;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.4.操作类型为添加授权人时,显示:添加授权人"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.5.操作类型为附加费用时,显示:添加服务项目"xxxxxx",费用为"xxxxxxx",生效时间为"YYYY-MM-DD",如果有多条附加费用,支持多行显示;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.6.操作类型为转正式合同时,显示:原合同编码"xxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.7.操作类型为终止合同时,显示:终止原因"xxxxxx"。')) + ); + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + var servicePopoverContentCustom = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow] + ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setServicePopoverRow(null); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 480 }), onClick: function(e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalHeader }, '服务项明细'), + React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '16px 20px' }) }, renderServiceItemsPopover(mockRentalOrders[servicePopoverRow])), + React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setServicePopoverRow(null); } }, '关闭')) + )) + : null; + + return React.createElement('div', { style: styles.page }, + servicePopoverContentCustom, + React.createElement('div', { style: { marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '查看租赁合同'))), + approvalCardEl, + React.createElement('div', { style: styles.anchorWrap }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录') + ), + React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))), + React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)), + React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)), + React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, attachmentContent), + React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, changeHistoryContent), + React.createElement('div', { style: { height: 60 } }), + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault) }, '返回')) + ); +}; diff --git a/web端/车辆租赁合同/车辆租赁合同.jsx b/web端/车辆租赁合同/车辆租赁合同.jsx new file mode 100644 index 0000000..1ef0f38 --- /dev/null +++ b/web端/车辆租赁合同/车辆租赁合同.jsx @@ -0,0 +1,1064 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 业务管理 - 车辆租赁合同(列表页,租赁合同管理) + +const Component = function() { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Table = antd.Table; + var Space = antd.Space; + var Card = antd.Card; + var DatePicker = antd.DatePicker; + var Popover = antd.Popover; + var Dropdown = antd.Dropdown; + var Modal = antd.Modal; + var message = antd.message; + var App = antd.App; + + var RangePicker = DatePicker.RangePicker; + + // 筛选展开(默认收起,只显示第一行 3 列) + var _filterExpanded = useState(false); + var _contractCode = useState(undefined); + var _projectName = useState(undefined); + var _customerName = useState(undefined); + var _signingCompany = useState(undefined); + var _approvalStatus = useState(['全部']); + var _contractStatus = useState(['全部']); + var _businessDept = useState([]); + var _businessOwner = useState([]); + var _contractType = useState(['全部']); + var _creator = useState([]); + var _endDateRange = useState(null); + + var _appliedFilter = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + signingCompany: undefined, + approvalStatus: ['全部'], + contractStatus: ['全部'], + businessDept: [], + businessOwner: [], + contractType: ['全部'], + creator: [], + endDateRange: null + }); + + var _vehiclePopoverRecord = useState(null); + var _authorizedModalVisible = useState(false); + var _authorizedModalRecord = useState(null); + var _authorizedList = useState([{ name: '', phone: '', idCard: '' }]); + var _extraFeeModalVisible = useState(false); + var _extraFeeModalRecord = useState(null); + var _extraFeeList = useState([]); + var _deleteModalVisible = useState(false); + var _deleteModalRecord = useState(null); + var _withdrawModalVisible = useState(false); + var _withdrawModalRecord = useState(null); + var _requirementModalVisible = useState(false); + + // 模拟选项(与新增租赁合同保持一致) + var contractCodeOptions = [ + { value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' }, + { value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' }, + { value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' }, + { value: 'HT-ZL-2025-004', label: 'HT-ZL-2025-004' }, + { value: 'HT-ZL-2025-005', label: 'HT-ZL-2025-005' }, + { value: 'HT-ZL-2025-006', label: 'HT-ZL-2025-006' }, + { value: 'HT-ZL-2025-007', label: 'HT-ZL-2025-007' }, + { value: 'HT-ZL-2025-008', label: 'HT-ZL-2025-008' }, + { value: 'HT-ZL-2024-009', label: 'HT-ZL-2024-009' }, + { value: 'HT-ZL-2024-010', label: 'HT-ZL-2024-010' } + ]; + var projectNameOptions = [ + { value: 'p1', label: '嘉兴氢能示范项目' }, + { value: 'p2', label: '上海物流租赁项目' }, + { value: 'p3', label: '杭州城配租赁项目' } + ]; + var customerNameOptions = [ + { value: 'c1', label: '嘉兴某某物流有限公司' }, + { value: 'c2', label: '上海某某运输公司' }, + { value: 'c3', label: '杭州某某租赁有限公司' } + ]; + var signingCompanyOptions = [ + { value: '嘉兴羚牛', label: '嘉兴羚牛' }, + { value: '上海羚牛', label: '上海羚牛' }, + { value: '广东羚牛', label: '广东羚牛' } + ]; + var approvalStatusOptions = [ + { value: '全部', label: '全部' }, + { value: '待审批', label: '待审批' }, + { value: '审批中', label: '审批中' }, + { value: '审批通过', label: '审批通过' }, + { value: '审批驳回', label: '审批驳回' }, + { value: '未提交', label: '未提交' }, + { value: '已结束', label: '已结束' } + ]; + var contractStatusOptions = [ + { value: '全部', label: '全部' }, + { value: '草稿', label: '草稿' }, + { value: '变更', label: '变更' }, + { value: '合同进行中', label: '合同进行中' }, + { value: '到期合同', label: '到期合同' }, + { value: '已提交审批', label: '已提交审批' } + ]; + var contractTypeOptions = [ + { value: '全部', label: '全部' }, + { value: '正式合同', label: '正式合同' }, + { value: '试用合同', label: '试用合同' } + ]; + var deptOptions = [ + { value: '业务1部', label: '业务1部' }, + { value: '业务2部', label: '业务2部' }, + { value: '业务3部', label: '业务3部' } + ]; + var userOptions = [ + { value: '张经理', label: '张经理' }, + { value: '李专员', label: '李专员' }, + { value: '王专员', label: '王专员' }, + { value: '赵经理', label: '赵经理' }, + { value: '钱专员', label: '钱专员' } + ]; + + // 模拟列表数据:按审批状态 × 合同状态组合生成样例 + // 审批状态:待审批、审批中、审批通过、审批驳回、未提交、已结束 + // 合同状态:草稿、变更、合同进行中、到期合同、已提交审批 + var rawList = [ + // 1. 未提交 + 草稿(仅保存未提交) + { + id: '1', + contractCode: 'HT-ZL-2025-001', + projectName: '嘉兴氢能示范项目', + vehicleCount: 2, + vehicles: [ + { vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A12345', actualDelivery: '2025-01-10 09:00' }, + { vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '-', actualDelivery: '2025-01-12 14:30' } + ], + approvalStatus: '未提交', + contractStatus: '草稿', + customerName: '嘉兴某某物流有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务1部', + businessOwner: '张经理', + contractType: '正式合同', + contractEndDate: '2026-02-16', + contactName: '张三', + contactPhone: '13800138001', + creator: '张经理', + createTime: '2025-01-05 10:00', + updater: '-', + updateTime: '-', + remark: '草稿待完善' + }, + // 2. 未提交 + 草稿(试用合同) + { + id: '2', + contractCode: 'HT-ZL-2025-002', + projectName: '上海物流租赁项目', + vehicleCount: 1, + vehicles: [ + { vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '沪D66666', actualDelivery: '2025-02-01 11:00' } + ], + approvalStatus: '未提交', + contractStatus: '草稿', + customerName: '上海某某运输公司', + signingCompany: '上海羚牛', + businessDept: '业务2部', + businessOwner: '李专员', + contractType: '试用合同', + contractEndDate: '2025-08-01', + contactName: '李四', + contactPhone: '13800138002', + creator: '李专员', + createTime: '2025-02-10 09:00', + updater: '-', + updateTime: '-', + remark: '试用期 3 个月' + }, + // 3. 待审批 + 已提交审批(初次提交,尚未有任何节点审批) + { + id: '3', + contractCode: 'HT-ZL-2025-003', + projectName: '杭州城配租赁项目', + vehicleCount: 1, + vehicles: [ + { vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '浙B20002', actualDelivery: '2025-02-15 08:30' } + ], + approvalStatus: '待审批', + contractStatus: '已提交审批', + customerName: '杭州某某租赁有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务3部', + businessOwner: '王专员', + contractType: '正式合同', + contractEndDate: '2026-06-30', + contactName: '王五', + contactPhone: '13800138003', + creator: '王专员', + createTime: '2025-02-12 11:00', + updater: '-', + updateTime: '-', + remark: '-' + }, + // 4. 审批中 + 已提交审批(已有节点审批,未走完) + { + id: '4', + contractCode: 'HT-ZL-2025-004', + projectName: '宁波冷链运输项目', + vehicleCount: 2, + vehicles: [ + { vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B2', plateNo: '-', actualDelivery: '2025-02-16 10:00' }, + { vehicleType: '49吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '浙C30003', actualDelivery: '2025-02-18 14:00' } + ], + approvalStatus: '审批中', + contractStatus: '已提交审批', + customerName: '嘉兴某某物流有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务1部', + businessOwner: '张经理', + contractType: '正式合同', + contractEndDate: '2026-03-01', + contactName: '赵六', + contactPhone: '13900139001', + creator: '张经理', + createTime: '2025-02-14 09:00', + updater: '李专员', + updateTime: '2025-02-15 16:00', + remark: '-' + }, + // 5. 审批中 + 变更(已通过审批后做了变更并重新提交) + { + id: '5', + contractCode: 'HT-ZL-2025-005', + projectName: '苏州城配试点项目', + vehicleCount: 1, + vehicles: [ + { vehicleType: '重型平板半挂车-重型平板半挂车', brand: '品牌D', model: '型号D2', plateNo: '苏E50005', actualDelivery: '2025-02-20 09:00' } + ], + approvalStatus: '审批中', + contractStatus: '变更', + customerName: '上海某某运输公司', + signingCompany: '上海羚牛', + businessDept: '业务2部', + businessOwner: '李专员', + contractType: '正式合同', + contractEndDate: '2026-05-31', + contactName: '孙七', + contactPhone: '13900139002', + creator: '李专员', + createTime: '2025-02-18 10:00', + updater: '李专员', + updateTime: '2025-02-22 14:00', + remark: '变更车辆数量' + }, + // 6. 审批通过 + 合同进行中(正式合同) + { + id: '6', + contractCode: 'HT-ZL-2025-006', + projectName: '南京氢能示范项目', + vehicleCount: 3, + vehicles: [ + { vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏A60006', actualDelivery: '2025-01-20 08:00' }, + { vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏A60007', actualDelivery: '2025-01-21 10:00' }, + { vehicleType: '35吨牵引车头-重型半挂牵引车', brand: '品牌D', model: '型号D1', plateNo: '苏A60008', actualDelivery: '2025-01-22 14:00' } + ], + approvalStatus: '审批通过', + contractStatus: '合同进行中', + customerName: '杭州某某租赁有限公司', + signingCompany: '广东羚牛', + businessDept: '业务3部', + businessOwner: '王专员', + contractType: '正式合同', + contractEndDate: '2026-12-31', + contactName: '周八', + contactPhone: '13900139003', + creator: '王专员', + createTime: '2025-01-15 09:00', + updater: '王专员', + updateTime: '2025-01-25 11:00', + remark: '-' + }, + // 7. 审批通过 + 合同进行中(试用合同,可转正式) + { + id: '7', + contractCode: 'HT-ZL-2025-007', + projectName: '无锡试用租赁项目', + vehicleCount: 1, + vehicles: [ + { vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C2', plateNo: '苏B70007', actualDelivery: '2025-02-01 09:30' } + ], + approvalStatus: '审批通过', + contractStatus: '合同进行中', + customerName: '嘉兴某某物流有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务1部', + businessOwner: '张经理', + contractType: '试用合同', + contractEndDate: '2025-05-31', + contactName: '吴九', + contactPhone: '13900139004', + creator: '张经理', + createTime: '2025-01-28 10:00', + updater: '-', + updateTime: '-', + remark: '试用 3 个月' + }, + // 8. 审批驳回 + 已提交审批(可编辑和重新提交) + { + id: '8', + contractCode: 'HT-ZL-2025-008', + projectName: '常州物流合作项目', + vehicleCount: 2, + vehicles: [ + { vehicleType: '重型集装箱半挂车-重型集装箱半挂车', brand: '品牌D', model: '型号D1', plateNo: '-', actualDelivery: '-' }, + { vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '-', actualDelivery: '-' } + ], + approvalStatus: '审批驳回', + contractStatus: '已提交审批', + customerName: '上海某某运输公司', + signingCompany: '上海羚牛', + businessDept: '业务2部', + businessOwner: '李专员', + contractType: '正式合同', + contractEndDate: '2026-08-15', + contactName: '郑十', + contactPhone: '13900139005', + creator: '李专员', + createTime: '2025-02-20 14:00', + updater: '李专员', + updateTime: '2025-02-23 09:00', + remark: '驳回原因:费用条款需调整' + }, + // 9. 已结束 + 到期合同(审批已通过但合同结束日期已过) + { + id: '9', + contractCode: 'HT-ZL-2024-009', + projectName: '南通去年到期项目', + vehicleCount: 2, + vehicles: [ + { vehicleType: '18吨双飞翼货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '苏F90009', actualDelivery: '2024-03-01 09:00' }, + { vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '苏F90010', actualDelivery: '2024-03-02 10:00' } + ], + approvalStatus: '已结束', + contractStatus: '到期合同', + customerName: '杭州某某租赁有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务3部', + businessOwner: '王专员', + contractType: '正式合同', + contractEndDate: '2024-12-31', + contactName: '王五', + contactPhone: '13800138003', + creator: '王专员', + createTime: '2024-02-20 10:00', + updater: '王专员', + updateTime: '2024-12-20 16:00', + remark: '已到期可续签' + }, + // 10. 已结束 + 到期合同(合同日期已过) + { + id: '10', + contractCode: 'HT-ZL-2024-010', + projectName: '镇江到期合同项目', + vehicleCount: 1, + vehicles: [ + { vehicleType: '公务用车/小客车-小型普通客车', brand: '品牌C', model: '型号C1', plateNo: '苏L00100', actualDelivery: '2024-06-01 11:00' } + ], + approvalStatus: '已结束', + contractStatus: '到期合同', + customerName: '嘉兴某某物流有限公司', + signingCompany: '嘉兴羚牛', + businessDept: '业务1部', + businessOwner: '张经理', + contractType: '正式合同', + contractEndDate: '2025-01-15', + contactName: '张三', + contactPhone: '13800138001', + creator: '张经理', + createTime: '2024-05-20 09:00', + updater: '张经理', + updateTime: '2025-01-10 14:00', + remark: '-' + } + ]; + + var appliedFilter = _appliedFilter[0]; + var filteredList = useMemo(function() { + var list = rawList.slice(); + var f = appliedFilter; + if (f.contractCode) { + list = list.filter(function(r) { + return (r.contractCode || '').indexOf(f.contractCode) !== -1; + }); + } + if (f.projectName) { + list = list.filter(function(r) { return r.projectName === f.projectName; }); + } + if (f.customerName) { + list = list.filter(function(r) { return r.customerName === f.customerName; }); + } + if (f.signingCompany) { + list = list.filter(function(r) { return r.signingCompany === f.signingCompany; }); + } + var approval = f.approvalStatus; + if (approval && approval.length > 0 && approval.indexOf('全部') === -1) { + list = list.filter(function(r) { return approval.indexOf(r.approvalStatus) !== -1; }); + } + var status = f.contractStatus; + if (status && status.length > 0 && status.indexOf('全部') === -1) { + list = list.filter(function(r) { return status.indexOf(r.contractStatus) !== -1; }); + } + if (f.businessDept && f.businessDept.length > 0) { + list = list.filter(function(r) { return f.businessDept.indexOf(r.businessDept) !== -1; }); + } + if (f.businessOwner && f.businessOwner.length > 0) { + list = list.filter(function(r) { return f.businessOwner.indexOf(r.businessOwner) !== -1; }); + } + var type = f.contractType; + if (type && type.length > 0 && type.indexOf('全部') === -1) { + list = list.filter(function(r) { return type.indexOf(r.contractType) !== -1; }); + } + if (f.creator && f.creator.length > 0) { + list = list.filter(function(r) { return f.creator.indexOf(r.creator) !== -1; }); + } + if (f.endDateRange && f.endDateRange.length === 2) { + var start = ''; + var end = ''; + if (f.endDateRange[0] && f.endDateRange[0].format) { + start = f.endDateRange[0].format('YYYY-MM-DD'); + } + if (f.endDateRange[1] && f.endDateRange[1].format) { + end = f.endDateRange[1].format('YYYY-MM-DD'); + } + if (start || end) { + list = list.filter(function(r) { + var d = r.contractEndDate || ''; + if (start && d < start) return false; + if (end && d > end) return false; + return true; + }); + } + } + return list; + }, [rawList, appliedFilter]); + + var handleQuery = useCallback(function() { + _appliedFilter[1]({ + contractCode: _contractCode[0], + projectName: _projectName[0], + customerName: _customerName[0], + signingCompany: _signingCompany[0], + approvalStatus: _approvalStatus[0] ? _approvalStatus[0].slice() : ['全部'], + contractStatus: _contractStatus[0] ? _contractStatus[0].slice() : ['全部'], + businessDept: _businessDept[0] ? _businessDept[0].slice() : [], + businessOwner: _businessOwner[0] ? _businessOwner[0].slice() : [], + contractType: _contractType[0] ? _contractType[0].slice() : ['全部'], + creator: _creator[0] ? _creator[0].slice() : [], + endDateRange: _endDateRange[0] + }); + }, []); + + var handleReset = useCallback(function() { + _contractCode[1](undefined); + _projectName[1](undefined); + _customerName[1](undefined); + _signingCompany[1](undefined); + _approvalStatus[1](['全部']); + _contractStatus[1](['全部']); + _businessDept[1]([]); + _businessOwner[1]([]); + _contractType[1](['全部']); + _creator[1]([]); + _endDateRange[1](null); + _appliedFilter[1]({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + signingCompany: undefined, + approvalStatus: ['全部'], + contractStatus: ['全部'], + businessDept: [], + businessOwner: [], + contractType: ['全部'], + creator: [], + endDateRange: null + }); + }, []); + + // 审批状态/合同状态/合同类型:「全部」与其它选项互斥,不能同时多选 + var handleApprovalStatusChange = useCallback(function(v) { + if (!v || v.length === 0) { _approvalStatus[1](['全部']); return; } + if (v.indexOf('全部') !== -1 && v.length > 1) { + var prev = _approvalStatus[0] || []; + var hadAllOnly = prev.length === 1 && prev.indexOf('全部') !== -1; + if (hadAllOnly) { + var next = []; + for (var i = 0; i < v.length; i++) { if (v[i] !== '全部') next.push(v[i]); } + _approvalStatus[1](next); + } else { + _approvalStatus[1](['全部']); + } + return; + } + _approvalStatus[1](v); + }, []); + var handleContractStatusChange = useCallback(function(v) { + if (!v || v.length === 0) { _contractStatus[1](['全部']); return; } + if (v.indexOf('全部') !== -1 && v.length > 1) { + var prev = _contractStatus[0] || []; + var hadAllOnly = prev.length === 1 && prev.indexOf('全部') !== -1; + if (hadAllOnly) { + var next = []; + for (var j = 0; j < v.length; j++) { if (v[j] !== '全部') next.push(v[j]); } + _contractStatus[1](next); + } else { + _contractStatus[1](['全部']); + } + return; + } + _contractStatus[1](v); + }, []); + var handleContractTypeChange = useCallback(function(v) { + if (!v || v.length === 0) { _contractType[1](['全部']); return; } + if (v.indexOf('全部') !== -1 && v.length > 1) { + var prev = _contractType[0] || []; + var hadAllOnly = prev.length === 1 && prev.indexOf('全部') !== -1; + if (hadAllOnly) { + var next = []; + for (var k = 0; k < v.length; k++) { if (v[k] !== '全部') next.push(v[k]); } + _contractType[1](next); + } else { + _contractType[1](['全部']); + } + return; + } + _contractType[1](v); + }, []); + + var addAuthorizedRow = useCallback(function() { + _authorizedList[1](function(prev) { return prev.concat([{ name: '', phone: '', idCard: '' }]); }); + }, []); + var removeAuthorizedRow = useCallback(function(index) { + _authorizedList[1](function(prev) { + var list = prev.slice(); + list.splice(index, 1); + if (list.length === 0) list = [{ name: '', phone: '', idCard: '' }]; + return list; + }); + }, []); + var updateAuthorizedRow = useCallback(function(index, field, value) { + _authorizedList[1](function(prev) { + var list = prev.slice(); + var row = list[index] || { name: '', phone: '', idCard: '' }; + var next = {}; + next[field] = value; + list[index] = Object.assign({}, row, next); + return list; + }); + }, []); + + // 附加费用:服务项目枚举(与需求文档一致) + var extraFeeServiceOptions = [ + { value: '代处理费用', label: '代处理费用' }, { value: '罚款', label: '罚款' }, { value: '违章处理违约金', label: '违章处理违约金' }, { value: '未参加安全培训', label: '未参加安全培训' }, { value: '车辆出险', label: '车辆出险' }, { value: '年检年审违约', label: '年检年审违约' }, { value: '停车费', label: '停车费' }, { value: '设备损坏金(包含易损件)', label: '设备损坏金(包含易损件)' }, { value: '清洗费', label: '清洗费' }, { value: '上门收车人工费', label: '上门收车人工费' }, { value: '上门收车送车行驶费', label: '上门收车送车行驶费' }, { value: '上门收车基础服务费', label: '上门收车基础服务费' }, { value: '保险上浮', label: '保险上浮' }, { value: '保养费用', label: '保养费用' }, { value: '补办驾驶证', label: '补办驾驶证' }, { value: '补办牌照', label: '补办牌照' }, { value: '补办营运证', label: '补办营运证' }, { value: '补办加氢证', label: '补办加氢证' }, { value: '借用备用钥匙', label: '借用备用钥匙' }, { value: '补配钥匙', label: '补配钥匙' }, { value: '租金', label: '租金' }, { value: '氢气费-客', label: '氢气费-客' }, { value: '退还车氢量差', label: '退还车氢量差' }, { value: '能源费补缴', label: '能源费补缴' }, { value: '能源费退款', label: '能源费退款' }, { value: '送车上门人工费', label: '送车上门人工费' }, { value: '送车上门送车行驶费', label: '送车上门送车行驶费' }, { value: '送车上门基础服务费', label: '送车上门基础服务费' }, { value: '保证金', label: '保证金' }, { value: '氢气预付费', label: '氢气预付费' }, { value: '维修费用', label: '维修费用' }, { value: 'ETC-客', label: 'ETC-客' }, { value: 'ETC卡缺损费', label: 'ETC卡缺损费' }, { value: 'ETC设备缺损费', label: 'ETC设备缺损费' }, { value: '电费-客', label: '电费-客' }, { value: '未结算保养费', label: '未结算保养费' }, { value: '未结算维修费', label: '未结算维修费' }, { value: '车损费', label: '车损费' }, { value: '工具损坏或丢失费', label: '工具损坏或丢失费' }, { value: '证件费', label: '证件费' }, { value: '广告损坏费', label: '广告损坏费' }, { value: '送车服务费', label: '送车服务费' }, { value: '接车服务费', label: '接车服务费' }, { value: '补办行驶证', label: '补办行驶证' }, { value: '超赔险', label: '超赔险' }, { value: '轮胎磨损费', label: '轮胎磨损费' }, { value: '无忧包', label: '无忧包' }, { value: '轮胎保', label: '轮胎保' }, { value: '养护保', label: '养护保' }, { value: '尾板', label: '尾板' } + ]; + var updateExtraFeeRow = useCallback(function(index, field, value) { + _extraFeeList[1](function(prev) { + var list = prev.slice(); + var row = list[index] || {}; + var next = {}; + next[field] = value; + list[index] = Object.assign({}, row, next); + return list; + }); + }, []); + + // 附加费用:服务项目枚举(与需求文档一致) + var extraFeeServiceOptions = [ + { value: '代处理费用', label: '代处理费用' }, { value: '罚款', label: '罚款' }, { value: '违章处理违约金', label: '违章处理违约金' }, { value: '未参加安全培训', label: '未参加安全培训' }, { value: '车辆出险', label: '车辆出险' }, { value: '年检年审违约', label: '年检年审违约' }, { value: '停车费', label: '停车费' }, { value: '设备损坏金(包含易损件)', label: '设备损坏金(包含易损件)' }, { value: '清洗费', label: '清洗费' }, { value: '上门收车人工费', label: '上门收车人工费' }, { value: '上门收车送车行驶费', label: '上门收车送车行驶费' }, { value: '上门收车基础服务费', label: '上门收车基础服务费' }, { value: '保险上浮', label: '保险上浮' }, { value: '保养费用', label: '保养费用' }, { value: '补办驾驶证', label: '补办驾驶证' }, { value: '补办牌照', label: '补办牌照' }, { value: '补办营运证', label: '补办营运证' }, { value: '补办加氢证', label: '补办加氢证' }, { value: '借用备用钥匙', label: '借用备用钥匙' }, { value: '补配钥匙', label: '补配钥匙' }, { value: '租金', label: '租金' }, { value: '氢气费-客', label: '氢气费-客' }, { value: '退还车氢量差', label: '退还车氢量差' }, { value: '能源费补缴', label: '能源费补缴' }, { value: '能源费退款', label: '能源费退款' }, { value: '送车上门人工费', label: '送车上门人工费' }, { value: '送车上门送车行驶费', label: '送车上门送车行驶费' }, { value: '送车上门基础服务费', label: '送车上门基础服务费' }, { value: '保证金', label: '保证金' }, { value: '氢气预付费', label: '氢气预付费' }, { value: '维修费用', label: '维修费用' }, { value: 'ETC-客', label: 'ETC-客' }, { value: 'ETC卡缺损费', label: 'ETC卡缺损费' }, { value: 'ETC设备缺损费', label: 'ETC设备缺损费' }, { value: '电费-客', label: '电费-客' }, { value: '未结算保养费', label: '未结算保养费' }, { value: '未结算维修费', label: '未结算维修费' }, { value: '车损费', label: '车损费' }, { value: '工具损坏或丢失费', label: '工具损坏或丢失费' }, { value: '证件费', label: '证件费' }, { value: '广告损坏费', label: '广告损坏费' }, { value: '送车服务费', label: '送车服务费' }, { value: '接车服务费', label: '接车服务费' }, { value: '补办行驶证', label: '补办行驶证' }, { value: '超赔险', label: '超赔险' }, { value: '轮胎磨损费', label: '轮胎磨损费' }, { value: '无忧包', label: '无忧包' }, { value: '轮胎保', label: '轮胎保' }, { value: '养护保', label: '养护保' }, { value: '尾板', label: '尾板' } + ]; + var updateExtraFeeRow = useCallback(function(index, field, value) { + _extraFeeList[1](function(prev) { + var list = prev.slice(); + var row = list[index] || {}; + var next = {}; + next[field] = value; + list[index] = Object.assign({}, row, next); + return list; + }); + }, []); + + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var tableSingleLineStyle = '.contract-list-table .ant-table-thead th,.contract-list-table .ant-table-tbody td{white-space:nowrap;}'; + + var vehicleColumns = [ + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 180 }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '实际交车日期', dataIndex: 'actualDelivery', key: 'actualDelivery', width: 140 } + ]; + + function getMoreMenuItems(record) { + var status = record.contractStatus; + var type = record.contractType; + var approval = record.approvalStatus; + var items = []; + if (status === '草稿') { + items.push({ key: 'edit', label: '编辑合同', onClick: function() { message.info('编辑合同(原型)'); } }); + items.push({ key: 'del', label: '删除合同', danger: true, onClick: function() { _deleteModalRecord[1](record); _deleteModalVisible[1](true); } }); + } + if (status === '合同进行中') { + items.push({ key: 'addVehicle', label: '新增车辆', onClick: function() { message.info('新增车辆(原型)'); } }); + items.push({ key: 'renew', label: '合同续签', onClick: function() { message.info('合同续签(原型)'); } }); + items.push({ key: 'authorized', label: '添加被授权人', onClick: function() { _authorizedModalRecord[1](record); _authorizedList[1]([{ name: '', phone: '', idCard: '' }]); _authorizedModalVisible[1](true); } }); + items.push({ key: 'extraFee', label: '附加费用', onClick: function() { + _extraFeeModalRecord[1](record); + var vehicles = record.vehicles || []; + _extraFeeList[1](vehicles.map(function(v) { + return { + vehicleType: v.vehicleType || '-', + brand: v.brand || '-', + model: v.model || '-', + plateNo: v.plateNo || '-', + serviceItem: undefined, + fee: '', + effectiveDate: null + }; + })); + _extraFeeModalVisible[1](true); + } }); + } + if (status === '到期合同') { + items.push({ key: 'renew2', label: '合同续签', onClick: function() { message.info('合同续签(原型)'); } }); + } + if (status === '已提交审批' && approval !== '审批驳回') { + items.push({ key: 'withdraw', label: '撤回合同', onClick: function() { _withdrawModalRecord[1](record); _withdrawModalVisible[1](true); } }); + } + if (approval === '审批驳回') { + items.push({ key: 'editReject', label: '编辑合同', onClick: function() { message.info('编辑合同(原型)'); } }); + } + if (type === '试用合同') { + items.push({ key: 'toFormal', label: '转正式合同', onClick: function() { message.info('转正式合同(原型)'); } }); + } + return items; + } + + function getOperationButtons(record) { + var menuItems = getMoreMenuItems(record); + var moreDropdown = menuItems.length > 0 + ? React.createElement( + Dropdown, + { + menu: { items: menuItems }, + trigger: ['hover'] + }, + React.createElement(Button, { type: 'link', size: 'small' }, '更多') + ) + : null; + return React.createElement( + Space, + { size: 4, wrap: true }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('查看合同(原型)'); } }, '查看合同'), + moreDropdown + ); + } + + var columns = [ + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 140, fixed: 'left' }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, fixed: 'left' }, + { + title: '车辆数', + key: 'vehicleCount', + width: 90, + render: function(_, record) { + var open = _vehiclePopoverRecord[0] && _vehiclePopoverRecord[0].id === record.id; + var content = React.createElement('div', { style: { padding: 8 } }, + React.createElement(Table, { + size: 'small', + rowKey: function(r, i) { return String(i); }, + columns: vehicleColumns, + dataSource: record.vehicles || [], + pagination: false, + scroll: { x: 600 } + }) + ); + return React.createElement( + Popover, + { + content: content, + title: '车辆明细', + open: open, + onOpenChange: function(visible) { + if (!visible) _vehiclePopoverRecord[1](null); + else _vehiclePopoverRecord[1](record); + }, + trigger: 'click' + }, + React.createElement('a', { style: { cursor: 'pointer', color: '#1890ff', fontWeight: 500 } }, record.vehicleCount) + ); + } + }, + { title: '审批状态', dataIndex: 'approvalStatus', key: 'approvalStatus', width: 100 }, + { title: '合同状态', dataIndex: 'contractStatus', key: 'contractStatus', width: 110 }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140 }, + { title: '签约公司', dataIndex: 'signingCompany', key: 'signingCompany', width: 100 }, + { title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 100 }, + { title: '业务负责人', dataIndex: 'businessOwner', key: 'businessOwner', width: 100 }, + { title: '合同类型', dataIndex: 'contractType', key: 'contractType', width: 100 }, + { title: '合同结束日期', dataIndex: 'contractEndDate', key: 'contractEndDate', width: 120 }, + { title: '客户联系人', dataIndex: 'contactName', key: 'contactName', width: 100 }, + { title: '联系电话', dataIndex: 'contactPhone', key: 'contactPhone', width: 120 }, + { title: '创建人', dataIndex: 'creator', key: 'creator', width: 90 }, + { title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 150 }, + { title: '更新人', dataIndex: 'updater', key: 'updater', width: 90 }, + { title: '最后更新时间', dataIndex: 'updateTime', key: 'updateTime', width: 150 }, + { title: '备注', dataIndex: 'remark', key: 'remark', width: 100, ellipsis: true }, + { title: '操作', key: 'action', width: 140, fixed: 'right', render: function(_, record) { return getOperationButtons(record); } } + ]; + + var filterItems = [ + React.createElement('div', { key: 'contractCode', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同编码'), + React.createElement(Select, { + placeholder: '请选择或输入合同编码', + style: filterControlStyle, + value: _contractCode[0], + onChange: function(v) { _contractCode[1](v); }, + allowClear: true, + showSearch: true, + options: contractCodeOptions, + filterOption: function(input, opt) { + return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; + } + })), + React.createElement('div', { key: 'projectName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '项目名称'), + React.createElement(Select, { + placeholder: '请选择或输入项目名称', + style: filterControlStyle, + value: _projectName[0], + onChange: function(v) { _projectName[1](v); }, + allowClear: true, + showSearch: true, + options: projectNameOptions, + filterOption: function(input, opt) { + return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; + } + })), + React.createElement('div', { key: 'customerName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请选择或输入客户名称', + style: filterControlStyle, + value: _customerName[0], + onChange: function(v) { _customerName[1](v); }, + allowClear: true, + showSearch: true, + options: customerNameOptions, + filterOption: function(input, opt) { + return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; + } + })), + React.createElement('div', { key: 'signingCompany', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '签约公司'), + React.createElement(Select, { + placeholder: '请选择或输入签约公司名称', + style: filterControlStyle, + value: _signingCompany[0], + onChange: function(v) { _signingCompany[1](v); }, + allowClear: true, + options: signingCompanyOptions + })), + React.createElement('div', { key: 'approvalStatus', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '审批状态'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择', + style: filterControlStyle, + value: _approvalStatus[0], + onChange: handleApprovalStatusChange, + options: approvalStatusOptions + })), + React.createElement('div', { key: 'contractStatus', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同状态'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择', + style: filterControlStyle, + value: _contractStatus[0], + onChange: handleContractStatusChange, + options: contractStatusOptions + })), + React.createElement('div', { key: 'businessDept', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务部门'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择或输入业务部门名称', + style: filterControlStyle, + value: _businessDept[0], + onChange: function(v) { _businessDept[1](v); }, + options: deptOptions + })), + React.createElement('div', { key: 'businessOwner', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '业务负责人'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择或输入业务负责人姓名', + style: filterControlStyle, + value: _businessOwner[0], + onChange: function(v) { _businessOwner[1](v); }, + options: userOptions + })), + React.createElement('div', { key: 'contractType', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同类型'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择', + style: filterControlStyle, + value: _contractType[0], + onChange: handleContractTypeChange, + options: contractTypeOptions + })), + React.createElement('div', { key: 'creator', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '创建人'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择或输入创建人姓名', + style: filterControlStyle, + value: _creator[0], + onChange: function(v) { _creator[1](v); }, + options: userOptions + })), + React.createElement('div', { key: 'endDate', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同结束日期'), + React.createElement(RangePicker, { + style: filterControlStyle, + placeholder: ['开始日期', '结束日期'], + value: _endDateRange[0], + onChange: function(v) { _endDateRange[1](v); } + })) + ]; + + var filterCount = _filterExpanded[0] ? 11 : 3; + var filterNodes = []; + for (var i = 0; i < filterCount && i < filterItems.length; i++) { + filterNodes.push(filterItems[i]); + } + + var reqTitleStyle = { fontSize: 18, fontWeight: 600, marginBottom: 16, color: 'rgba(0,0,0,0.85)' }; + var reqSectionStyle = { fontSize: 15, fontWeight: 600, marginTop: 16, marginBottom: 8, color: 'rgba(0,0,0,0.85)' }; + var reqSubStyle = { fontSize: 14, fontWeight: 500, marginLeft: 16, marginTop: 8, marginBottom: 4, color: 'rgba(0,0,0,0.85)' }; + var reqItemStyle = { fontSize: 13, marginLeft: 32, marginTop: 4, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.75)' }; + var reqSubItemStyle = { fontSize: 13, marginLeft: 48, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.7)' }; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '业务管理' }, + { title: '车辆租赁合同' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { _requirementModalVisible[1](true); } }, '查看需求说明') + ), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + flex: 1, + minWidth: 0 + } + }, filterNodes), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function() { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开') + ) + ), + React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 } }, + React.createElement(Button, { onClick: function() { message.info('租赁费用模板(原型)'); } }, '租赁费用模板'), + React.createElement('div', { style: { display: 'flex', gap: 8 } }, + React.createElement(Button, { type: 'primary', onClick: function() { message.info('请在原型菜单列表中点击:业务管理-车辆租赁合同-新建合同 查看具体原型'); } }, '新建'), + React.createElement(Button, { onClick: function() { message.info('根据筛选条件,导出相应记录'); } }, '导出') + ) + ), + React.createElement(Card, null, + React.createElement(React.Fragment, null, + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'contract-list-table' }, + React.createElement(Table, { + rowKey: 'id', + columns: columns, + dataSource: filteredList, + scroll: { x: 2400 }, + size: 'small', + pagination: { showSizeChanger: true, showQuickJumper: true, showTotal: function(t) { return '共 ' + t + ' 条'; } } + }) + ) + ) + ), + React.createElement(Modal, { + title: '是否确认删除该合同草稿', + open: _deleteModalVisible[0], + onCancel: function() { _deleteModalVisible[1](false); _deleteModalRecord[1](null); }, + onOk: function() { + message.success('已删除(原型)'); + _deleteModalVisible[1](false); + _deleteModalRecord[1](null); + }, + okText: '确定', + cancelText: '取消' + }), + React.createElement(Modal, { + title: '是否确认撤回该合同', + open: _withdrawModalVisible[0], + onCancel: function() { _withdrawModalVisible[1](false); _withdrawModalRecord[1](null); }, + onOk: function() { + message.success('已撤回(原型)'); + _withdrawModalVisible[1](false); + _withdrawModalRecord[1](null); + }, + okText: '确定', + cancelText: '取消' + }), + React.createElement(Modal, { + title: '添加被授权人', + open: _authorizedModalVisible[0], + onCancel: function() { _authorizedModalVisible[1](false); _authorizedModalRecord[1](null); }, + width: 640, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: function() { _authorizedModalVisible[1](false); _authorizedModalRecord[1](null); } }, '取消'), + React.createElement(Button, { key: 'ok', type: 'primary', onClick: function() { message.success('已提交审核(原型)'); _authorizedModalVisible[1](false); _authorizedModalRecord[1](null); } }, '提交审核') + ] + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement(Table, { + rowKey: function(_, i) { return String(i); }, + size: 'small', + columns: [ + { title: '被授权人', key: 'name', width: 140, render: function(_, row, index) { return React.createElement(Input, { value: row.name, onChange: function(e) { updateAuthorizedRow(index, 'name', e.target.value); }, placeholder: '请输入' }); } }, + { title: '被授权人联系电话', key: 'phone', width: 160, render: function(_, row, index) { return React.createElement(Input, { value: row.phone, onChange: function(e) { updateAuthorizedRow(index, 'phone', e.target.value); }, placeholder: '请输入' }); } }, + { title: '被授权人身份证', key: 'idCard', width: 200, render: function(_, row, index) { return React.createElement(Input, { value: row.idCard, onChange: function(e) { updateAuthorizedRow(index, 'idCard', e.target.value); }, placeholder: '请输入' }); } }, + { title: '操作', key: 'action', width: 80, render: function(_, row, index) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeAuthorizedRow(index); } }, '删除'); } } + ], + dataSource: _authorizedList[0], + pagination: false + }), + React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addAuthorizedRow }, '添加一行') + )), + React.createElement(Modal, { + title: '附加费用', + open: _extraFeeModalVisible[0], + onCancel: function() { _extraFeeModalVisible[1](false); _extraFeeModalRecord[1](null); _extraFeeList[1]([]); }, + width: 900, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: function() { _extraFeeModalVisible[1](false); _extraFeeModalRecord[1](null); _extraFeeList[1]([]); } }, '取消'), + React.createElement(Button, { key: 'ok', type: 'primary', onClick: function() { message.success('已提交审核(原型)'); _extraFeeModalVisible[1](false); _extraFeeModalRecord[1](null); _extraFeeList[1]([]); } }, '提交审核') + ] + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement(Table, { + rowKey: function(_, i) { return String(i); }, + size: 'small', + columns: [ + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 160, render: function(v) { return v || '-'; } }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 90, render: function(v) { return v || '-'; } }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100, render: function(v) { return v || '-'; } }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100, render: function(v) { return v || '-'; } }, + { title: '服务项目', key: 'serviceItem', width: 180, render: function(_, row, index) { return React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: row.serviceItem, onChange: function(v) { updateExtraFeeRow(index, 'serviceItem', v); }, options: extraFeeServiceOptions, allowClear: true }); } }, + { title: '费用(元)', key: 'fee', width: 140, render: function(_, row, index) { return React.createElement(Input, { value: row.fee, onChange: function(e) { updateExtraFeeRow(index, 'fee', e.target.value); }, placeholder: '请输入', addonAfter: '元' }); } }, + { title: '生效时间', key: 'effectiveDate', width: 140, render: function(_, row, index) { var dayjs = window.dayjs; var val = row.effectiveDate && dayjs ? dayjs(row.effectiveDate) : null; return React.createElement(DatePicker, { style: { width: '100%' }, placeholder: '请选择日期', value: val, onChange: function(date, dateString) { updateExtraFeeRow(index, 'effectiveDate', dateString || null); } }); } } + ], + dataSource: _extraFeeList[0], + pagination: false, + scroll: { x: 920 } + }) + )), + React.createElement(Modal, { + title: '需求说明', + open: _requirementModalVisible[0], + onCancel: function() { _requirementModalVisible[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function() { _requirementModalVisible[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: reqTitleStyle }, '租赁合同管理'), + 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: reqItemStyle }, '2.1.1.合同编码:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), + React.createElement('div', { style: reqItemStyle }, '2.1.2.项目名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), + React.createElement('div', { style: reqItemStyle }, '2.1.3.客户名称:选择器,支持从输入框内输入内容进行模糊搜索,下拉显示匹配选项;'), + React.createElement('div', { style: reqItemStyle }, '2.1.4.签约公司:选择器,显示租赁合同所有签约公司;'), + React.createElement('div', { style: reqItemStyle }, '2.1.5.审批状态:选择器,支持全选或多选,选项为:全部、待审批、审批中、审批通过、审批驳回、未提交、已结束,默认显示全部;'), + React.createElement('div', { style: reqItemStyle }, '2.1.6.合同状态:选择器,支持全选或多选,选项为:全部、草稿、变更、合同进行中、到期合同、已提交审批;'), + React.createElement('div', { style: reqItemStyle }, '2.1.7.业务部门:选择器,支持全选或多选,拉取部门下所有业务相关部门;'), + React.createElement('div', { style: reqItemStyle }, '2.1.8.业务负责人:选择器,支持全选或多选,拉取所有业务相关部门下所有用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '2.1.9.合同类型:选择器,支持全选或多选,选项为:全部、正式合同、试用合同;'), + React.createElement('div', { style: reqItemStyle }, '2.1.10.创建人:选择器,支持全选或多选,拉取所有业务相关部门下所有用户姓名;'), + React.createElement('div', { style: reqItemStyle }, '2.1.11.合同结束日期:日期选择器,支持单输入框内双日历选择开始-结束时间;'), + React.createElement('div', { style: reqSectionStyle }, '3.列表:'), + React.createElement('div', { style: reqSubStyle }, '3.1.列表展示所有租赁合同信息,字段依次为:合同编码、项目名称、车辆数、审批状态、合同状态、客户名称、签约公司、业务部门、业务负责人、合同类型、合同结束日期、客户联系人、联系电话、创建人、创建时间、更新人、最后更新时间、备注、操作;列表右上角为新建、导出;'), + React.createElement('div', { style: reqItemStyle }, '3.1.1.合同编码:显示租赁合同对应合同编码;'), + React.createElement('div', { style: reqItemStyle }, '3.1.2.项目名称:显示租赁合同对应项目名称;'), + React.createElement('div', { style: reqItemStyle }, '3.1.3.车辆数:显示车辆数,点击车辆数,显示气泡卡片,卡片中列表显示:车辆类型、品牌、型号、车牌号、实际交车日期;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.3.1.车辆类型:4.5吨冷链车-轻型厢式货车、18吨双飞翼货车-重型厢式货车、49吨牵引车头-重型半挂牵引车、4.5吨货车-轻型厢式货车、18吨厢式货车-重型厢式货车、重型集装箱半挂车-重型集装箱半挂车、公务用车/小客车-小型普通客车、35吨牵引车头-重型半挂牵引车、重型平板半挂车-重型平板半挂车;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.3.2.品牌:显示租赁合同中对应车辆品牌;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.3.3.型号:显示租赁合同中对应车辆型号;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.3.4.车牌号:显示租赁合同中对应车牌号,无则显示为-,有则显示具体车牌号;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.3.5.实际交车日期:显示租赁合同中对应车辆对应实际交车日期,精确至分钟,格式为YYYY-MM-DD HH:MM;'), + React.createElement('div', { style: reqItemStyle }, '3.1.4.审批状态:显示租赁合同实际审批状态,状态分为:待审批、审批中、审批通过、审批驳回、未提交、已结束;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.1.待审批:发起人已提交,但还没有任何流程节点完成审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.2.审批中:发起人已提交,已有1个以上节点完成审批,但未完成最终节点审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.3.审批通过:发起人已提交,最终节点完成审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.4.审批驳回:发起人已提交,任意流程节点驳回,该状态下操作列支持编辑和重新提交;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.5.未提交:发起人仅保存,但未提交审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.4.6.已结束:发起人已提交,最终节点完成审批,但当前日期超过合同结束日期;'), + React.createElement('div', { style: reqItemStyle }, '3.1.5.合同状态:显示租赁合同状态,状态分为:草稿、变更、合同进行中、到期合同、已提交审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.1.草稿:发起人仅保存,但未提交审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.2.变更:发起人提交后,合同已通过审批的基础上,进行了「变更内容」操作,且已提交审批,但未完成最终节点审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.3.合同进行中:发起人提交后,合同完成最终节点审批;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.4.到期合同:发起人已提交,最终节点完成审批,但当前日期超过合同结束日期;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.5.5.已提交审批:发起人初次提交,但未完成最终节点审批;'), + React.createElement('div', { style: reqItemStyle }, '3.1.6.客户名称:显示租赁合同创建时所选客户名称,客户名称来自「客户管理」-「客户名称」;'), + React.createElement('div', { style: reqItemStyle }, '3.1.7.签约公司:显示租赁合同创建时所选签约公司,签约公司来自组织机构表;'), + 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.合同续签:当「合同状态」为「合同进行中」、「合同到期」时显示,点击后跳转新增租赁合同页面,同时反写所有旧合同内信息,支持修改。此外,合同续签操作产生的合同,合同编码一栏中编码后方显示:(续签自:旧合同编码xxxx),同时查看新合同时下方变更记录中显示续签自哪个合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.5.删除合同:当「合同状态」为「草稿」时显示,点击删除合同时进行二次确认,提示语:是否确认删除该合同草稿;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.6.撤回合同:当「合同状态」为「已提交审核」时显示,点击撤回合同时进行二次确认,提示语:是否确认撤回该合同;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.7.添加被授权人:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中可编辑被授权人、被授权人联系电话、被授权人身份证,同时支持添加一行、删除已有行等操作;添加被授权人触发重新审核流程,审核通过后生效;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.8.附加费用:当「合同状态」为「合同进行中」时显示,点击弹出卡片,卡片中显示该租赁合同内:车辆类型、品牌、型号、车牌号,同时可对服务项目、费用、生效时间进行编辑;'), + React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.1.服务项目:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), + React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.2.费用:输入框,后缀为元,支持2位小数输入;'), + React.createElement('div', { style: { fontSize: 13, marginLeft: 64, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.65)' } }, '3.1.19.8.3.生效时间:日期选择器,精确至日;'), + React.createElement('div', { style: reqSubItemStyle }, '3.1.19.9.转正式合同:当「合同类型」为「试用合同」时显示,点击后跳转新增租赁合同页面,同时反写所有旧合同内信息,支持修改。此外,合同续签操作产生的合同,合同编码一栏中编码后方显示:(续签自:旧合同编码xxxx),同时查看新合同时下方变更记录中显示续签自哪个合同;') + )) + ) + ); +}; diff --git a/web端/车辆租赁合同/转正式合同.jsx b/web端/车辆租赁合同/转正式合同.jsx new file mode 100644 index 0000000..83a78af --- /dev/null +++ b/web端/车辆租赁合同/转正式合同.jsx @@ -0,0 +1,690 @@ +// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型 +// 车辆管理系统 - 转正式合同模块(布局与合同续签一致,合同编码后增加转正式合同自:合同编号xxxxxxx,IE11+ 兼容,采用 antd) + +const Component = function() { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var Modal = antd.Modal; + var message = antd.message; + var Option = Select.Option; + + var customerList = [ + { id: '1', name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' }, + { id: '2', name: '上海某某运输公司', creditCode: '91310000MA2XXXXX2', address: '上海市浦东新区张江高科技园区', contact: '李四', phone: '13800138002', email: 'lisi@example.com', companyName: '上海某某运输公司', companyPhone: '021-66666666', mailingAddress: '上海市浦东新区张江高科技园区', bank: '中国建设银行上海分行', bankAccount: '6217001234567890123', taxId: '91310000MA2XXXXX2' }, + { id: '3', name: '杭州某某租赁有限公司', creditCode: '91330100MA2XXXXX3', address: '浙江省杭州市余杭区未来科技城', contact: '王五', phone: '13800138003', email: 'wangwu@example.com', companyName: '杭州某某租赁有限公司', companyPhone: '0571-99999999', mailingAddress: '浙江省杭州市余杭区未来科技城', bank: '中国农业银行杭州分行', bankAccount: '6228481234567890123', taxId: '91330100MA2XXXXX3' } + ]; + var deptList = [{ id: 'YW1', name: '业务1部', owners: ['张经理', '李专员', '王专员'] }, { id: 'YW2', name: '业务2部', owners: ['赵经理', '钱专员'] }, { id: 'YW3', name: '业务3部', owners: ['孙经理', '周专员'] }]; + var orgList = ['嘉兴羚牛', '上海羚牛', '广东羚牛']; + var regionList = [ + { province: '浙江省', cities: ['杭州市', '宁波市', '嘉兴市', '湖州市'] }, + { province: '上海市', cities: ['上海市'] }, + { province: '广东省', cities: ['广州市', '深圳市', '东莞市'] } + ]; + var feeTemplates = ['标准费用模板A', '标准费用模板B', '定制费用模板C']; + var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }]; + var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }]; + var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }]; + var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }]; + var brandList = ['品牌A', '品牌B', '品牌C', '品牌D']; + var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2', '型号C3'], '品牌D': ['型号D1'] }; + var vehicleList = [{ plateNo: '浙A10001', vin: 'L1234567890ABCDEF' }, { plateNo: '浙B20002', vin: 'L2234567890ABCDEF' }, { plateNo: '沪A30003', vin: 'L3234567890ABCDEF' }, { plateNo: '粤A40004', vin: 'L4234567890ABCDEF' }]; + var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板']; + + var prevContractSample = { + customerId: '1', + businessDept: 'YW1', + businessOwner: '张经理', + contractOriginal: null, + projectName: '嘉兴氢能运输项目', + contractType: '正式合同', + effectiveDate: '2027-02-17', + paymentMethod: '预付', + endDate: '2028-02-17', + paymentPeriod: '1', + signingCompany: '嘉兴羚牛', + deliveryProvince: '浙江省', + deliveryCity: '嘉兴市', + deliveryLocation: '嘉兴市南湖区科技大道1号', + remarks: '转正式合同自原合同 JXZL20260216YW101235A', + authorizedList: [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }], + rentalOrders: [ + { brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }], deposit: '10000', remark: '' }, + { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceItems: [{ project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }], deposit: '10000', remark: '' } + ], + hydrogenBearer: '客户', + hydrogenPaymentMethod: '预付', + hydrogenPrepay: '5000', + returnHydrogenPrice: '80', + feeTemplate: '标准费用模板A', + billingMethod: 'month' + }; + var prevCustomer = customerList.find(function(c) { return c.id === prevContractSample.customerId; }) || null; + + var cs1 = React.useState(prevCustomer ? prevCustomer.name : ''); + var customerSearch = cs1[0]; + var setCustomerSearch = cs1[1]; + var cs2 = React.useState(prevCustomer); + var selectedCustomer = cs2[0]; + var setSelectedCustomer = cs2[1]; + var cs3 = React.useState(false); + var customerDropdownOpen = cs3[0]; + var setCustomerDropdownOpen = cs3[1]; + var cs4 = React.useState(prevContractSample.businessDept); + var businessDept = cs4[0]; + var setBusinessDept = cs4[1]; + var cs5 = React.useState(prevContractSample.businessOwner); + var businessOwner = cs5[0]; + var setBusinessOwner = cs5[1]; + var cs6 = React.useState(''); + var ownerFocusError = cs6[0]; + var setOwnerFocusError = cs6[1]; + var contractOriginalRef = React.useRef(null); + var csContractOriginal = React.useState(null); + var contractOriginal = csContractOriginal[0]; + var setContractOriginal = csContractOriginal[1]; + + var bs1 = React.useState(prevContractSample.projectName); + var projectName = bs1[0]; + var setProjectName = bs1[1]; + var bs2 = React.useState(prevContractSample.contractType); + var contractType = bs2[0]; + var setContractType = bs2[1]; + var bs3 = React.useState(prevContractSample.effectiveDate); + var effectiveDate = bs3[0]; + var setEffectiveDate = bs3[1]; + var bs4 = React.useState(prevContractSample.paymentMethod); + var paymentMethod = bs4[0]; + var setPaymentMethod = bs4[1]; + var bs6 = React.useState(prevContractSample.endDate); + var endDate = bs6[0]; + var setEndDate = bs6[1]; + var bs7 = React.useState(prevContractSample.paymentPeriod); + var paymentPeriod = bs7[0]; + var setPaymentPeriod = bs7[1]; + var bs8 = React.useState(prevContractSample.signingCompany); + var signingCompany = bs8[0]; + var setSigningCompany = bs8[1]; + var bs9 = React.useState(prevContractSample.deliveryProvince); + var deliveryProvince = bs9[0]; + var setDeliveryProvince = bs9[1]; + var bs10 = React.useState(prevContractSample.deliveryCity); + var deliveryCity = bs10[0]; + var setDeliveryCity = bs10[1]; + var bs10b = React.useState(false); + var deliveryRegionOpen = bs10b[0]; + var setDeliveryRegionOpen = bs10b[1]; + var deliveryRegionClickInsideRef = React.useRef(false); + var bs11 = React.useState(prevContractSample.deliveryLocation); + var deliveryLocation = bs11[0]; + var setDeliveryLocation = bs11[1]; + var bs12 = React.useState(prevContractSample.remarks); + var remarks = bs12[0]; + var setRemarks = bs12[1]; + + var as1 = React.useState(prevContractSample.authorizedList.slice()); + var authorizedList = as1[0]; + var setAuthorizedList = as1[1]; + + var emptyRentalRow = { brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }; + var os1 = React.useState(prevContractSample.rentalOrders.map(function(r) { return { brand: r.brand, model: r.model, plateNo: r.plateNo, vin: r.vin, monthRent: r.monthRent, serviceItems: (r.serviceItems || []).map(function(s) { return { project: s.project, fee: s.fee, effectiveDate: s.effectiveDate }; }), deposit: r.deposit, remark: r.remark || '' }; })); + var rentalOrders = os1[0]; + var setRentalOrders = os1[1]; + var os1b = React.useState(null); + var serviceModalRowIndex = os1b[0]; + var setServiceModalRowIndex = os1b[1]; + var os1c = React.useState(prevContractSample.hydrogenBearer); + var hydrogenBearer = os1c[0]; + var setHydrogenBearer = os1c[1]; + var os1d = React.useState(prevContractSample.hydrogenPaymentMethod); + var hydrogenPaymentMethod = os1d[0]; + var setHydrogenPaymentMethod = os1d[1]; + var os1e = React.useState(prevContractSample.hydrogenPrepay); + var hydrogenPrepay = os1e[0]; + var setHydrogenPrepay = os1e[1]; + var os1f = React.useState(prevContractSample.returnHydrogenPrice); + var returnHydrogenPrice = os1f[0]; + var setReturnHydrogenPrice = os1f[1]; + var os1g = React.useState(null); + var plateNoFocusRow = os1g[0]; + var setPlateNoFocusRow = os1g[1]; + var os1h = React.useState(''); + var plateNoSearch = os1h[0]; + var setPlateNoSearch = os1h[1]; + var os1i = React.useState(null); + var serviceItemFocusRow = os1i[0]; + var setServiceItemFocusRow = os1i[1]; + var os1j = React.useState(''); + var serviceItemSearch = os1j[0]; + var setServiceItemSearch = os1j[1]; + var os1k = React.useState(null); + var plateNoDropdownRect = os1k[0]; + var setPlateNoDropdownRect = os1k[1]; + var os2 = React.useState(prevContractSample.feeTemplate); + var feeTemplate = os2[0]; + var setFeeTemplate = os2[1]; + var os3 = React.useState(prevContractSample.billingMethod); + var billingMethod = os3[0]; + var setBillingMethod = os3[1]; + + var cc1State = React.useState(false); + var cc1 = cc1State[0]; + var setCc1 = cc1State[1]; + var cc2State = React.useState(false); + var cc2 = cc2State[0]; + var setCc2 = cc2State[1]; + var cc3State = React.useState(false); + var cc3 = cc3State[0]; + var setCc3 = cc3State[1]; + var cc4State = React.useState(false); + var cc4 = cc4State[0]; + var setCc4 = cc4State[1]; + var cc5State = React.useState(false); + var cc5 = cc5State[0]; + var setCc5 = cc5State[1]; + var cc6State = React.useState(false); + var cc6 = cc6State[0]; + var setCc6 = cc6State[1]; + var reqSpecState = React.useState(false); + var reqSpecOpen = reqSpecState[0]; + var setReqSpecOpen = reqSpecState[1]; + var formErrorsState = React.useState({}); + var formErrors = formErrorsState[0]; + var setFormErrors = formErrorsState[1]; + + var filteredCustomers = customerList.filter(function(c) { return !customerSearch || c.name.indexOf(customerSearch) !== -1; }); + var currentDept = deptList.find(function(d) { return d.id === businessDept; }); + var ownerOptions = currentDept ? currentDept.owners : []; + var contractCode = (function() { + var cityCode = 'JX'; + var typeCode = 'ZL'; + var signCode = contractType === '正式合同' ? 'A' : 'B'; + var dateStr = effectiveDate ? effectiveDate.replace(/-/g, '') : '20260216'; + var deptCode = businessDept || 'YW1'; + return cityCode + typeCode + dateStr + deptCode + '01235' + signCode; + })(); + var originalContractCode = 'JXZL20260216YW101235A'; + var contractCodeDisplay = contractCode + '(转正式合同自:合同编号' + originalContractCode + ')'; + var mainVehicleModelsDisplay = (function() { + var models = []; + var seen = {}; + for (var i = 0; i < rentalOrders.length; i++) { + var m = rentalOrders[i].model; + if (m && !seen[m]) { seen[m] = true; models.push(m); } + } + return models.join('、'); + })(); + var calcRowServiceFee = function(row) { + var sum = 0; + for (var i = 0; i < (row.serviceItems || []).length; i++) { + var fee = parseFloat(row.serviceItems[i].fee); + if (!isNaN(fee)) sum += fee; + } + return sum.toFixed(2); + }; + var rentalTotalVehicles = rentalOrders.length; + var rentalTotalRentService = rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.monthRent) || 0) + (parseFloat(calcRowServiceFee(r)) || 0); }, 0); + var rentalTotalDeposit = rentalOrders.reduce(function(s, r) { return s + (parseFloat(r.deposit) || 0); }, 0); + var rentalTotalHydrogen = (hydrogenPaymentMethod === '预付' && hydrogenBearer === '客户') ? (parseFloat(hydrogenPrepay) || 0) : 0; + + var selectCustomer = function(c) { + setSelectedCustomer(c); + setCustomerSearch(c ? c.name : ''); + setCustomerDropdownOpen(false); + }; + var deliveryRegionDisplay = deliveryProvince && deliveryCity ? deliveryProvince + ' / ' + deliveryCity : ''; + var selectDeliveryRegion = function(province, city) { + setDeliveryProvince(province); + setDeliveryCity(city); + setDeliveryRegionOpen(false); + }; + var scrollToCard = function(id) { + var el = document.getElementById(id); + if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; + var handleOwnerFocus = function() { + if (!businessDept) setOwnerFocusError('请先选择业务部门'); + else setOwnerFocusError(''); + }; + var handleOwnerBlur = function() { setOwnerFocusError(''); }; + var openContractOriginal = function() { + if (contractOriginal && contractOriginal.file) { + var url = URL.createObjectURL(contractOriginal.file); + window.open(url); + } + }; + + var validateSubmitAndReview = function() { + var errs = {}; + if (!selectedCustomer) errs.customer = '请选择客户'; + if (!businessDept) errs.businessDept = '请选择业务部门'; + if (!contractOriginal) errs.contractOriginal = '请上传合同原件'; + if (!businessOwner) errs.businessOwner = '请选择业务负责人'; + if (!projectName || !projectName.trim()) errs.projectName = '请输入项目名称'; + if (!contractType) errs.contractType = '请选择合同类型'; + if (!effectiveDate) errs.effectiveDate = '请选择生效日期'; + if (!paymentMethod) errs.paymentMethod = '请选择付款方式'; + if (!endDate) errs.endDate = '请选择结束日期'; + if (!paymentPeriod) errs.paymentPeriod = '请选择付款周期'; + if (!signingCompany) errs.signingCompany = '请选择签约公司'; + if (!deliveryProvince || !deliveryCity) errs.deliveryRegion = '请选择交车区域'; + var authInvalid = authorizedList.some(function(a) { return !a.name || !a.name.trim() || !a.phone || !a.phone.trim() || !a.idCard || !a.idCard.trim(); }); + if (authInvalid) errs.authorizedList = '请完整填写被授权人姓名、联系电话、身份证'; + var rentalInvalid = rentalOrders.some(function(r) { return !r.brand || !r.model || !(r.monthRent && String(r.monthRent).trim()) || !(r.deposit && String(r.deposit).trim()); }); + if (rentalInvalid) errs.rentalOrders = '请完整填写租赁订单的品牌、型号、车辆月租金、保证金'; + if (!hydrogenBearer) errs.hydrogenBearer = '请选择氢费承担方'; + if (hydrogenBearer === '客户' && !hydrogenPaymentMethod) errs.hydrogenPaymentMethod = '请选择付款方式'; + if (hydrogenBearer === '客户' && hydrogenPaymentMethod === '预付' && (!hydrogenPrepay || !String(hydrogenPrepay).trim())) errs.hydrogenPrepay = '请输入氢气预付款'; + if (!returnHydrogenPrice || !String(returnHydrogenPrice).trim()) errs.returnHydrogenPrice = '请输入退还车氢气单价'; + if (!feeTemplate) errs.feeTemplate = '请选择费用模板'; + if (!billingMethod) errs.billingMethod = '请选择账单计算方式'; + setFormErrors(errs); + if (Object.keys(errs).length > 0) { + var firstId = errs.customer || errs.businessDept || errs.businessOwner ? 'card-customer' : (errs.projectName || errs.contractType || errs.effectiveDate || errs.paymentMethod || errs.endDate || errs.paymentPeriod || errs.signingCompany || errs.deliveryRegion || errs.contractOriginal) ? 'card-contract' : errs.authorizedList ? 'card-authorized' : (errs.rentalOrders || errs.hydrogenBearer || errs.hydrogenPaymentMethod || errs.hydrogenPrepay || errs.returnHydrogenPrice) ? 'card-rental' : errs.feeTemplate ? 'card-fee' : 'card-billing'; + 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; + } + return true; + }; + + var addAuthorized = function() { + setAuthorizedList(authorizedList.concat([{ name: '', phone: '', idCard: '' }])); + }; + var removeAuthorized = function(index) { + var next = authorizedList.slice(0); + next.splice(index, 1); + setAuthorizedList(next.length ? next : [{ name: '', phone: '', idCard: '' }]); + }; + var updateAuthorized = function(index, field, value) { + var next = authorizedList.slice(0); + var cur = next[index] || {}; + var patch = {}; + patch[field] = value; + next[index] = Object.assign({}, cur, patch); + setAuthorizedList(next); + }; + + var addRentalRow = function() { + setRentalOrders(rentalOrders.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }])); + }; + var removeRentalRow = function(index) { + var next = rentalOrders.slice(0); + next.splice(index, 1); + setRentalOrders(next.length ? next : [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); + }; + var updateRentalOrder = function(index, field, value) { + var next = rentalOrders.slice(0); + var cur = next[index] || {}; + var newRow = Object.assign({}, cur); + newRow[field] = value; + if (field === 'plateNo') { + var v = vehicleList.find(function(x) { return x.plateNo === value; }); + newRow.vin = v ? v.vin : ''; + } + if (field === 'brand') newRow.model = ''; + next[index] = newRow; + setRentalOrders(next); + }; + var openServiceModal = function(index) { setServiceModalRowIndex(index); }; + var closeServiceModal = function() { setServiceModalRowIndex(null); }; + var addServiceItem = function() { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]); + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var removeServiceItem = function(siIndex) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + items.splice(siIndex, 1); + if (items.length === 0) items = [{ project: '', fee: '', effectiveDate: '' }]; + var newRow = {}; + for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + var updateServiceItem = function(siIndex, field, value) { + if (serviceModalRowIndex === null) return; + var next = rentalOrders.slice(0); + var row = next[serviceModalRowIndex]; + var items = (row.serviceItems || []).slice(0); + var item = items[siIndex] || {}; + var newItem = {}; + for (var k in item) { if (item.hasOwnProperty(k)) newItem[k] = item[k]; } + newItem[field] = value; + items[siIndex] = newItem; + var newRow = {}; + for (var rk in row) { if (row.hasOwnProperty(rk) && rk !== 'serviceItems') newRow[rk] = row[rk]; } + newRow.serviceItems = items; + next[serviceModalRowIndex] = newRow; + setRentalOrders(next); + }; + + React.useEffect(function() { + if (!deliveryRegionOpen) return; + var handler = function(e) { + if (deliveryRegionClickInsideRef.current) { deliveryRegionClickInsideRef.current = false; return; } + var el = document.getElementById('delivery-region-wrap'); + if (el && !el.contains(e.target)) setDeliveryRegionOpen(false); + }; + document.addEventListener('mousedown', handler); + return function() { document.removeEventListener('mousedown', handler); }; + }, [deliveryRegionOpen]); + + React.useEffect(function() { + if (plateNoFocusRow === null) { setPlateNoDropdownRect(null); return; } + var timer = setTimeout(function() { + var el = document.getElementById('plate-no-input-' + plateNoFocusRow); + if (el && el.getBoundingClientRect) { + var rect = el.getBoundingClientRect(); + setPlateNoDropdownRect({ top: rect.bottom + 2, left: rect.left, width: rect.width }); + } else { setPlateNoDropdownRect(null); } + }, 0); + return function() { clearTimeout(timer); }; + }, [plateNoFocusRow]); + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 }, + breadcrumb: { marginBottom: 16, color: '#666' }, + breadcrumbSep: { margin: '0 8px', color: '#999' }, + anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 }, + anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 }, + card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' }, + cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' }, + cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' }, + cardToggle: { color: '#999', fontSize: 14 }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 }, + formColFull: { flex: '0 0 100%', marginBottom: 8 }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + inputDisabled: { backgroundColor: '#f5f5f5', color: '#999', cursor: 'not-allowed' }, + inputError: { borderColor: '#ff4d4f' }, + select: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 }, + textarea: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, minHeight: 80, resize: 'vertical' }, + errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 }, + summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 }, + summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' }, + summaryListLabel: { flex: '0 0 140px', color: '#666' }, + summaryListValue: { flex: 1, fontWeight: 600, color: '#333' }, + authRow: { display: 'flex', gap: 12, alignItems: 'flex-start', marginBottom: 12 }, + authInput: { flex: 1, padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4 }, + btnDel: { padding: '8px 16px', color: '#ff4d4f', border: '1px solid #ff4d4f', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer' }, + btnAdd: { padding: '8px 16px', color: '#1890ff', border: '1px dashed #1890ff', borderRadius: 4, backgroundColor: '#fff', cursor: 'pointer', marginBottom: 16 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 }, + btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 }, + btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' }, + btnDefault: { backgroundColor: '#fff', color: '#333' }, + tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 }, + regionCascader: { position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4, backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: 4, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', zIndex: 10, display: 'flex', minHeight: 200 }, + regionCascaderCol: { flex: 1, borderRight: '1px solid #f0f0f0', overflowY: 'auto' }, + regionCascaderColLast: { flex: 1 }, + regionCascaderItem: { padding: '10px 12px', cursor: 'pointer' }, + rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, + rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 }, + rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'top' }, + rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }, + rentalInput: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 13 }, + rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#999' }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }, + modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 }, + modalBody: { padding: 20, overflow: 'auto', flex: 1 }, + btnGroup: { display: 'inline-flex', border: '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden' }, + btnGroupItem: { padding: '8px 16px', border: 'none', borderRight: '1px solid #d9d9d9', backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, + btnGroupItemLast: { borderRight: 'none' }, + btnGroupItemActive: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff', borderRightColor: '#1890ff' }, + feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 }, + feeSectionTitleFirst: { marginTop: 0 }, + modalFormInput: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, height: 36, boxSizing: 'border-box' } + }; + + var CardBlock = function(props) { + return React.createElement('div', { id: props.id, style: styles.card }, + React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } }, + React.createElement('span', { style: styles.cardTitle }, props.title), + React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起') + ), + !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null + ); + }; + + var FormItem = function(props) { + var colStyle = props.fullWidth ? styles.formColFull : (props.colStyle ? Object.assign({}, styles.formCol, props.colStyle) : styles.formCol); + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label), + props.children, + props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null + ); + }; + + var customerOptions = customerList.map(function(c) { return React.createElement(Option, { key: c.id, value: c.id }, c.name); }); + var customerFields = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称', required: true, error: formErrors.customer }, React.createElement(Select, { placeholder: '请选择或输入搜索客户', style: { width: '100%' }, value: selectedCustomer ? selectedCustomer.id : undefined, onChange: function(id) { var c = customerList.find(function(x) { return x.id === id; }); selectCustomer(c || null); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, status: formErrors.customer ? 'error' : undefined }, customerOptions)), + React.createElement(FormItem, { label: '客户统一信用代码' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.creditCode : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.address : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户联系人' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.contact : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.phone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '客户电子邮箱' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.email : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业名称' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyName : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '企业电话' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.companyPhone : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.mailingAddress : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '开户银行' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bank : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '银行账号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.bankAccount : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: selectedCustomer ? selectedCustomer.taxId : '', disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '业务部门', required: true, error: formErrors.businessDept }, React.createElement(Select, { placeholder: '请选择业务部门', style: { width: '100%' }, value: businessDept || undefined, onChange: function(v) { setBusinessDept(v || ''); setBusinessOwner(''); }, status: formErrors.businessDept ? 'error' : undefined }, deptList.map(function(d, i) { return React.createElement(Option, { key: i, value: d.id }, d.name); }))), + React.createElement(FormItem, { label: '业务负责人', required: true, error: formErrors.businessOwner || ownerFocusError }, React.createElement(Select, { placeholder: businessDept ? '请选择' : '请先选择业务部门', style: { width: '100%' }, value: businessOwner || undefined, onChange: function(v) { setBusinessOwner(v || ''); setOwnerFocusError(''); }, onFocus: handleOwnerFocus, onBlur: handleOwnerBlur, disabled: !businessDept, status: (formErrors.businessOwner || ownerFocusError) ? 'error' : undefined }, ownerOptions.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))) + ); + + var contractFormRow1 = React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '项目名称', required: true, error: formErrors.projectName }, React.createElement(Input, { placeholder: '请输入项目名称', value: projectName, onChange: function(e) { setProjectName(e.target.value); }, status: formErrors.projectName ? 'error' : undefined, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: contractCodeDisplay, disabled: true, style: { width: '100%' } })), + React.createElement(FormItem, { label: '合同类型', required: true, error: formErrors.contractType }, React.createElement(Select, { placeholder: '请选择合同类型', style: { width: '100%' }, value: contractType || undefined, onChange: function(v) { setContractType(v || ''); }, status: formErrors.contractType ? 'error' : undefined }, React.createElement(Option, { value: '正式合同' }, '正式合同'), React.createElement(Option, { value: '试用合同' }, '试用合同'))), + React.createElement(FormItem, { label: '生效日期', required: true, error: formErrors.effectiveDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效日期', value: effectiveDate && window.moment ? window.moment(effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEffectiveDate(dateStr || ''); }, status: formErrors.effectiveDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.paymentMethod }, React.createElement(Select, { placeholder: '请选择付款方式', style: { width: '100%' }, value: paymentMethod || undefined, onChange: function(v) { setPaymentMethod(v || ''); }, status: formErrors.paymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '后付' }, '后付'))), + React.createElement(FormItem, { label: '主要车型' }, React.createElement('div', { style: { padding: '8px 12px', minHeight: 36, border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' } }, mainVehicleModelsDisplay ? mainVehicleModelsDisplay.split('、').map(function(m, i) { return React.createElement('span', { key: i, style: styles.tag }, m); }) : React.createElement('span', { style: { color: '#999' } }, '根据租赁订单自动反写'))), + React.createElement(FormItem, { label: '结束日期', required: true, error: formErrors.endDate }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择结束日期', value: endDate && window.moment ? window.moment(endDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { setEndDate(dateStr || ''); }, status: formErrors.endDate ? 'error' : undefined })), + React.createElement(FormItem, { label: '付款周期', required: true, error: formErrors.paymentPeriod }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: paymentPeriod || undefined, onChange: function(v) { setPaymentPeriod(v || ''); }, status: formErrors.paymentPeriod ? 'error' : undefined }, [1,2,3,4,5,6,7,8,9,10,11,12].map(function(n) { return React.createElement(Option, { key: n, value: String(n) }, n + '个月'); }))), + React.createElement(FormItem, { label: '签约公司', required: true, error: formErrors.signingCompany }, React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: signingCompany || undefined, onChange: function(v) { setSigningCompany(v || ''); }, status: formErrors.signingCompany ? 'error' : undefined }, orgList.map(function(o, i) { return React.createElement(Option, { key: i, value: o }, o); }))), + React.createElement('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.contractOriginal }, + React.createElement('div', null, + React.createElement('div', { style: { color: '#999', fontSize: 12, marginBottom: 8 } }, '转正式合同时需重新上传合同原件附件'), + contractOriginal + ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, + React.createElement('a', { href: '#', style: { color: '#1890ff' }, onClick: function(e) { e.preventDefault(); openContractOriginal(); } }, contractOriginal.name), + contractOriginal.size || contractOriginal.uploadTime ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size ? contractOriginal.size : '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null + ), + React.createElement(Button, { type: 'button', size: 'small', danger: true, onClick: function() { setContractOriginal(null); } }, '删除') + ) + : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement('input', { ref: contractOriginalRef, type: 'file', style: { display: 'none' }, onChange: function(e) { + var f = e.target.files && e.target.files[0]; + if (f) { + var sizeDisplay = f.size >= 1024 * 1024 ? (f.size / 1024 / 1024).toFixed(1) + ' MB' : (f.size / 1024).toFixed(1) + ' KB'; + var now = window.moment ? window.moment() : new Date(); + var uploadTimeStr = window.moment ? now.format('YYYY-MM-DD HH:mm') : now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + setContractOriginal({ name: f.name, file: f, size: sizeDisplay, uploadTime: uploadTimeStr }); + } + e.target.value = ''; + } }), + React.createElement(Button, { type: 'default', style: { padding: '8px 16px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14 }, onClick: function() { if (contractOriginalRef.current) contractOriginalRef.current.click(); } }, '上传附件') + ) + ) + ) + ), + React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { placeholder: '请输入交车地点', value: deliveryLocation, onChange: function(e) { setDeliveryLocation(e.target.value); }, style: { width: '100%' } })) + ); + var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '备注', fullWidth: true }, React.createElement(Input.TextArea, { placeholder: '请输入备注信息', value: remarks, onChange: function(e) { setRemarks(e.target.value); }, style: styles.textarea, rows: 4 }))); + + var authorizedContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } }, React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人姓名'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人联系电话'), React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, React.createElement('span', { style: { color: '#ff4d4f', marginRight: 4 } }, '*'), '被授权人身份证'), React.createElement('span', { style: { flex: '0 0 80px' } })), + authorizedList.map(function(item, index) { return React.createElement('div', { key: index, style: styles.authRow }, React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人姓名', value: item.name, onChange: function(e) { updateAuthorized(index, 'name', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人联系电话', value: item.phone, onChange: function(e) { updateAuthorized(index, 'phone', e.target.value); } }), React.createElement(Input, { style: Object.assign({}, styles.authInput, formErrors.authorizedList ? { borderColor: '#ff4d4f' } : {}, { flex: 1 }), placeholder: '请输入被授权人身份证号', value: item.idCard, onChange: function(e) { updateAuthorized(index, 'idCard', e.target.value); } }), React.createElement(Button, { type: 'button', danger: true, onClick: function() { removeAuthorized(index); } }, '删除')); }), + formErrors.authorizedList ? React.createElement('div', { style: styles.errMsg }, formErrors.authorizedList) : null, + React.createElement(Button, { type: 'dashed', style: { width: '100%', marginBottom: 16 }, onClick: addAuthorized }, '添加一行') + ); + + var hydrogenFormRow = (function() { + var hydrogenFields = []; + hydrogenFields.push(React.createElement(FormItem, { label: '氢费承担方', required: true, error: formErrors.hydrogenBearer }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenBearer || undefined, onChange: function(v) { setHydrogenBearer(v || ''); setHydrogenPaymentMethod(v === '客户' ? '预付' : ''); }, status: formErrors.hydrogenBearer ? 'error' : undefined }, React.createElement(Option, { value: '我方' }, '我方'), React.createElement(Option, { value: '客户' }, '客户')))); + if (hydrogenBearer === '客户') { hydrogenFields.push(React.createElement(FormItem, { label: '付款方式', required: true, error: formErrors.hydrogenPaymentMethod }, React.createElement(Select, { style: { width: '100%' }, value: hydrogenPaymentMethod || undefined, onChange: function(v) { setHydrogenPaymentMethod(v || ''); }, status: formErrors.hydrogenPaymentMethod ? 'error' : undefined }, React.createElement(Option, { value: '预付' }, '预付'), React.createElement(Option, { value: '月付款' }, '月付款'), React.createElement(Option, { value: '自行结算' }, '自行结算')))); } + var hydrogenPrepayInput = React.createElement(Input, { placeholder: '0.00', value: hydrogenPrepay, onChange: function(e) { setHydrogenPrepay(e.target.value); }, addonAfter: '元', status: formErrors.hydrogenPrepay ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenInput = React.createElement(Input, { placeholder: '0.00', value: returnHydrogenPrice, onChange: function(e) { setReturnHydrogenPrice(e.target.value); }, addonAfter: '元', status: formErrors.returnHydrogenPrice ? 'error' : undefined, style: { width: '100%' } }); + var returnHydrogenColStyle = hydrogenPaymentMethod === '预付' ? { flex: '1 1 0', minWidth: 180 } : { flex: '0 0 calc(50% - 8px)', minWidth: 180 }; + hydrogenFields.push(React.createElement('div', { key: 'hydrogen-amount-row', style: { display: 'flex', gap: 16, flex: '1 1 100%' } }, hydrogenPaymentMethod === '预付' ? React.createElement(FormItem, { label: '氢气预付款', required: true, error: formErrors.hydrogenPrepay, colStyle: { flex: '1 1 0', minWidth: 180 } }, hydrogenPrepayInput) : null, React.createElement(FormItem, { label: '退还车氢气单价', required: true, error: formErrors.returnHydrogenPrice, colStyle: returnHydrogenColStyle }, returnHydrogenInput))); + return React.createElement.apply(React, ['div', { style: Object.assign({}, styles.formRow, { marginTop: 20 }) }].concat(hydrogenFields)); + })(); + + var plateNoOptions = vehicleList.map(function(v) { return React.createElement(Option, { key: v.plateNo, value: v.plateNo }, v.plateNo); }); + var rentalTableBody = rentalOrders.map(function(row, idx) { + var modelOpts = row.brand ? (modelByBrand[row.brand] || []) : []; + return React.createElement('tr', { key: idx }, + React.createElement('td', { style: styles.rentalTdCenter }, idx + 1), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.brand || undefined, onChange: function(v) { updateRentalOrder(idx, 'brand', v || ''); }, placeholder: '请选择' }, brandList.map(function(b, i) { return React.createElement(Option, { key: i, value: b }, b); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%', minWidth: 90 }, value: row.model || undefined, onChange: function(v) { updateRentalOrder(idx, 'model', v || ''); }, disabled: !row.brand, placeholder: '请选择' }, modelOpts.map(function(m, i) { return React.createElement(Option, { key: i, value: m }, m); }))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择或输入搜索', value: row.plateNo || undefined, onChange: function(v) { updateRentalOrder(idx, 'plateNo', v || ''); }, showSearch: true, allowClear: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; } }, plateNoOptions)), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { value: row.vin || '', disabled: true, style: { width: '100%' } })), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.monthRent || '', onChange: function(e) { updateRentalOrder(idx, 'monthRent', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(idx); } }, '管理')), + React.createElement('td', { style: styles.rentalTdCenter }, calcRowServiceFee(row) + ' 元'), + React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: { display: 'flex', alignItems: 'center' } }, React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateRentalOrder(idx, 'deposit', e.target.value); }, style: styles.rentalInput }), React.createElement('span', { style: { marginLeft: 4, whiteSpace: 'nowrap' } }, '元'))), + React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '备注', value: row.remark || '', onChange: function(e) { updateRentalOrder(idx, 'remark', e.target.value); }, style: Object.assign({}, styles.rentalInput, { width: '100%' }) })), + React.createElement('td', { style: styles.rentalTdCenter }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeRentalRow(idx); } }, '删除')) + ); + }); + + var rentalSummary = React.createElement('div', { style: styles.summaryList }, + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalVehicles + ' 辆')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalRentService.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalDeposit.toFixed(2) + ' 元')), + React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, rentalTotalHydrogen.toFixed(2) + ' 元')) + ); + var reqStarStyle = { color: '#ff4d4f', marginRight: 4 }; + var reqStar = React.createElement('span', { style: reqStarStyle }, '*'); + var rentalTh1 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 50, verticalAlign: 'middle' }) }, '序号'); + var rentalTh2 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '品牌'); + var rentalTh3 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '型号'); + var rentalTh4 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, '车牌号'); + var rentalTh5 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 160 }) }, '车辆识别代码'); + var rentalTh6 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 120 }) }, reqStar, '车辆月租金'); + var rentalTh7 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80, verticalAlign: 'middle' }) }, '服务费项目'); + var rentalTh8 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 90, verticalAlign: 'middle' }) }, '服务费'); + var rentalTh9 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 100 }) }, reqStar, '保证金'); + var rentalTh10 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '备注'); + var rentalTh11 = React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 60, verticalAlign: 'middle' }) }, '操作'); + var rentalTableThead = React.createElement('thead', null, React.createElement('tr', null, rentalTh1, rentalTh2, rentalTh3, rentalTh4, rentalTh5, rentalTh6, rentalTh7, rentalTh8, rentalTh9, rentalTh10, rentalTh11)); + var rentalTableTbody = React.createElement('tbody', null, rentalTableBody); + var rentalTableEl = React.createElement('table', { style: styles.rentalTable }, rentalTableThead, rentalTableTbody); + var rentalTableWrap = React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl); + var rentalContent = React.createElement('div', null, rentalSummary, formErrors.rentalOrders ? React.createElement('div', { style: styles.errMsg }, formErrors.rentalOrders) : null, rentalTableWrap, React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRentalRow }, '添加一行'), hydrogenFormRow); + + var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费')); + var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细')); + var makeFeeRow3 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.project); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.standard); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.serviceFee); + return React.createElement('tr', { key: i }, td1, td2, td3); + }; + var makeFeeRow5 = function(r, i) { + var td1 = React.createElement('td', { style: styles.rentalTd }, r.category); + var td2 = React.createElement('td', { style: styles.rentalTd }, r.part); + var td3 = React.createElement('td', { style: styles.rentalTd }, r.partName); + var td4 = React.createElement('td', { style: styles.rentalTd }, r.qty); + var td5 = React.createElement('td', { style: styles.rentalTd }, r.feeDetail); + return React.createElement('tr', { key: i }, td1, td2, td3, td4, td5); + }; + var feeCertRows = feeTemplateCertFees.map(makeFeeRow3); + var feePenaltyRows = feeTemplatePenaltyFees.map(makeFeeRow3); + var feeConsumablesRows = feeTemplateConsumables.map(makeFeeRow5); + var feeOtherRows = feeTemplateOtherFees.map(makeFeeRow3); + var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeCertRows)); + var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows)); + var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows)); + var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows)); + var feeTemplateSelect = React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '选择费用模板', required: true, error: formErrors.feeTemplate }, React.createElement(Select, { placeholder: '请选择费用模板', style: { width: '100%' }, value: feeTemplate || undefined, onChange: function(v) { setFeeTemplate(v || ''); }, status: formErrors.feeTemplate ? 'error' : undefined }, feeTemplates.map(function(f, i) { return React.createElement(Option, { key: i, value: f }, f); })))); + var feeTemplateBody = feeTemplate ? React.createElement('div', null, + React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), + feeCertTable, + React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), + feePenaltyTable, + React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), + feeConsumablesTable, + React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), + feeOtherTable + ) : null; + var feeContent = React.createElement('div', null, feeTemplateSelect, feeTemplateBody); + + var billingBtnBase = { padding: '12px 16px', border: '1px solid #d9d9d9', borderRadius: 0, backgroundColor: '#fff', color: '#333', cursor: 'pointer', fontSize: 14, textAlign: 'left', flex: 1, minWidth: 0, width: 0, height: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'flex-start', overflow: 'visible' }; + var billingContent = React.createElement('div', null, + React.createElement('div', { style: { display: 'flex', border: formErrors.billingMethod ? '1px solid #ff4d4f' : '1px solid #d9d9d9', borderRadius: 4, overflow: 'hidden', alignItems: 'stretch' } }, + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, { borderRight: '1px solid #d9d9d9' }, billingMethod === 'month' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('month'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按自然月结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照第一个月计费日期开始-当月最后一天为第一期,之后按自然月方式形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日。'))), + React.createElement(Button, { type: 'button', style: Object.assign({}, billingBtnBase, billingMethod === 'period' ? { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' } : {}), onClick: function() { setBillingMethod('period'); } }, React.createElement('div', { style: { width: '100%' } }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 6 } }, '按付款周期天数结算'), React.createElement('div', { style: { fontSize: 12, opacity: 0.9, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.5 } }, '账单按照合同基本信息中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单。'))) + ), + formErrors.billingMethod ? React.createElement('div', { style: styles.errMsg }, formErrors.billingMethod) : null + ); + + var serviceModalRows = serviceModalRowIndex !== null && rentalOrders[serviceModalRowIndex] ? rentalOrders[serviceModalRowIndex].serviceItems : []; + var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '服务项目'), React.createElement('div', { style: styles.modalBody }, React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))), React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) { var filteredServiceOpts = serviceItemOptions.filter(function(o) { return !serviceItemSearch || o.indexOf(serviceItemSearch) !== -1; }); return React.createElement('tr', { key: siIdx }, React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))), React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })), React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))); }))), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } }, React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'), React.createElement(Button, { onClick: closeServiceModal }, '关闭')))) : null; + + var reqSpecH1 = { fontSize: 16, fontWeight: 600, marginBottom: 12, color: '#333' }; + var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' }; + var reqSpecH3 = { fontSize: 13, fontWeight: 600, marginTop: 10, marginBottom: 6, color: '#333' }; + var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' }; + var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' }; + var reqSpecBlock = { marginBottom: 8 }; + var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecP }, '1.1.业务管理-车辆租赁合同-转正式合同')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.客户基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '2.1.用于从客户列表中选择客户,并将该合同绑定业务部门及业务负责人;'), React.createElement('div', { style: reqSpecLi }, '2.1.1.客户名称:必选项,选择器,支持从输入框内输入内容进行模糊搜索,从客户信息列表中选择对应客户;'), React.createElement('div', { style: reqSpecLi }, '2.1.2.客户统一信用代码:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「信用代码」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.3.客户地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.4.客户联系人:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户联系人」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.5.客户电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.6.客户电子邮箱:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「客户电子邮箱」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.7.企业名称:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业名称」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.8.企业电话:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「企业电话」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.9.邮寄地址:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「邮寄地址」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.10.开户银行:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「开户银行」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.11.银行账号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「银行账号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.12.纳税人识别号:输入框(禁用状态),根据所选客户自动从客户信息列表中拉取对应客户-「纳税人识别号」字段;'), React.createElement('div', { style: reqSpecLi }, '2.1.13.业务部门:必选项,选择器,从部门表中选择该租赁合同对应部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.14.业务负责人:必选项,选择器,从已选业务部门下拉取对应业务负责人,未选择业务部门时,业务负责人输入框获取焦点时进行错误提示:请先选择业务部门;'), React.createElement('div', { style: reqSpecLi }, '2.1.15.合同原件:必填项,点击上传按钮,上传本地文件,支持doc、docx、pdf等格式。已上传则显示文件名,后方为删除,删除后可重新点击上传附件进行重新上传;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.合同基本信息卡片:'), React.createElement('div', { style: reqSpecP }, '3.1.用于定义租赁合同基本情况和付款方式;'), React.createElement('div', { style: reqSpecLi }, '3.1.1.项目名称:必填项,输入框,用于定义该合同对应项目名称,默认提示信息"请输入项目名称";'), React.createElement('div', { style: reqSpecLi }, '3.1.2.合同编码:按照合同编码规则自动生成;'), React.createElement('div', { style: reqSpecLi }, '合同编码规则:[城市简写][合同类型][签约时间][业务部门代码][顺序流水号][签署状态]'), React.createElement('div', { style: reqSpecLi }, '3.1.2.1.地区简写:如上海为SH,嘉兴为JX;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.2.合同类型:采购合同为CG、租赁合同为ZL、自营合同为ZY;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.3.签约时间:显示合同签约时间,如20260216'), React.createElement('div', { style: reqSpecLi }, '3.1.2.4.业务部门代码:显示合同签约业务部门信息,如YW1代表业务1部,YW2代表业务2部;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.5.顺序流水号:实施5位编号补零规则,如01235,代表是集团第1235份合同;'), React.createElement('div', { style: reqSpecLi }, '3.1.2.6.签署状态:A为正式合同,B为试用合同;'), React.createElement('div', { style: reqSpecLi }, '如编号为:JXZL20260216YW101235A,则代表嘉兴业务1部在2026年2月16日签署的租赁正式合同,在集团中为第1235份;'), React.createElement('div', { style: reqSpecLi }, '3.1.3.合同类型:必选项,选择器,合同类型分为「正式合同」「试用合同」;'), React.createElement('div', { style: reqSpecLi }, '3.1.4.生效日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.付款方式:必选项,付款方式分为「预付」「后付」两种;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.1.如果选择预付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,同时生成时以消息/待办提醒对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.5.2.如果选择后付,则每笔账单以付款时间及账单计算方式,在账单日自动提前生成下月账单,但只在退还车时,才以消息/待办提醒合同对应业务人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.6.主要车型:输入框(禁用状态),根据租赁订单信息中所选车型,自动反写入输入框并以标签形式显示,支持多车型显示;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.结束日期:必选项,日期选择器,格式为YYYY-MM-DD,精确至天;'), React.createElement('div', { style: reqSpecLi }, '3.1.7.1.合同结束日期前30天将以消息提醒方式提醒;'), React.createElement('div', { style: reqSpecLi }, '3.1.8.付款周期:必选项,选择器,支持1个月-12个月 12种付款周期,账单将以此周期进行定时生成;'), React.createElement('div', { style: reqSpecLi }, '3.1.9.签约公司:必选项,选择器,从组织机构表中获取所有根组织机构(如嘉兴羚牛、上海羚牛、广东羚牛等),默认显示录入合同人员所在机构;'), React.createElement('div', { style: reqSpecLi }, '3.1.10.交车区域:必选项,地区选择器,支持省-市2级,选择区域后,该任务生成交车任务时会自动推送至该区域负责运维人员;'), React.createElement('div', { style: reqSpecLi }, '3.1.11.交车地点:输入框,支持自定义输入交车地点;'), React.createElement('div', { style: reqSpecLi }, '3.1.12.备注:文本域,支持自定义输入备注信息;')) + ); + var reqSpecDocPart2 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.被授权人信息卡片:'), React.createElement('div', { style: reqSpecP }, '4.1.用于定义租赁合同相关被授权人相关信息,被授权人交车单完成时需要通过手机短信调取E签宝进行签字确认;'), React.createElement('div', { style: reqSpecLi }, '4.1.1.被授权人:必填项,输入框,用于输入被授权人信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.2.被授权人联系电话:必填项,输入框,用于输入被授权人联系电话,该电话后续需要接收E签宝签字链接;'), React.createElement('div', { style: reqSpecLi }, '4.1.3.被授权人身份证:必填项,输入框,用于输入被授权人身份证信息;'), React.createElement('div', { style: reqSpecLi }, '4.1.4.支持通过新增/删除一行的方式,创建或管理多个授权人,后续交车单完成时可选择多个授权人;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '5.租赁订单信息卡片:'), React.createElement('div', { style: reqSpecP }, '5.1.用于定义租赁订单租赁车辆品牌、型号、月租金、服务费、保证金等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.卡片上方为租赁车辆总计数据,包括租赁车辆数、租金及服务费合计、保证金总额、氢气预付款金额等相关信息;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.1.租赁车辆数:显示下方租赁订单信息包含多少辆车;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.2.租金及服务费合计:显示下方租赁订单信息中车辆租金总额及服务费总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.3.保证金总额:显示下方租赁订单信息中车辆保证金总额;'), React.createElement('div', { style: reqSpecLi }, '5.1.1.4.氢气预付款金额:显示氢气预付款金额,如客户选择自行承担氢费或羚牛承担氢费,则该处为0不计入氢气预付款金额;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.下方为列表,显示序号、品牌、型号、车牌号、车辆识别代码、车辆月租金(元)、服务费项目、服务费、保证金、备注;'), React.createElement('div', { style: reqSpecLi }, '默认显示一行空数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.1.序号:自动按照条数生成,规则为1、2、3....以此类推;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.2.品牌:必选项,选择器,从型号参数库中「品牌」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.3.型号:必选项,选择器,从型号参数库中「型号」字段拉取所有品牌;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.4.车牌号:选填项,选择器(支持从输入框输入车牌号关键字下拉匹配);'), React.createElement('div', { style: reqSpecLi }, '5.1.2.5.车辆识别代码:输入框(禁用),显示该车辆对应车辆识别代码,从车辆表直接拉取;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.6.车辆月租金(元):输入框,支持两位小数,用于输入该车辆月租金金额,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.服务费项目:显示管理按钮,点击弹出卡片,卡片标题为:服务项目,下方列表显示服务项目、费用、生效时间、操作;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.1.服务项目:必选项,选择器(支持输入框输入服务项目关键字进行下拉匹配),选项包含:代处理费用、罚款、违章处理违约金、未参加安全培训、车辆出险、年检年审违约、停车费、设备损坏金(包含易损件)、清洗费、上门收车人工费、上门收车送车行驶费、上门收车基础服务费、保险上浮、保养费用、补办驾驶证、补办牌照、补办营运证、补办加氢证、借用备用钥匙、补配钥匙、租金、氢气费-客、退还车氢量差、能源费补缴、能源费退款、送车上门人工费、送车上门送车行驶费、送车上门基础服务费、保证金、氢气预付费、维修费用、ETC-客、ETC卡缺损费、ETC设备缺损费、电费-客、未结算保养费、未结算维修费、车损费、工具损坏或丢失费、证件费、广告损坏费、送车服务费、接车服务费、补办行驶证、超赔险、轮胎磨损费、无忧包、轮胎保、养护保、尾板;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.2.费用:必填项,输入框,支持2位小数,输入框后缀为元;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.3.生效时间:必选项,日期选择器,格式为YYYY-MM-DD,服务项目会以此时间提前3天进行消息通知;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.4.操作:删除,点击删除直接删除该行数据;'), React.createElement('div', { style: reqSpecLi }, '5.1.2.7.5.新增一行数据:点击添加一行服务项目;'), React.createElement('div', { style: reqSpecLi }, '5.1.3.氢费承担方:必选项,填充按钮组,选项为我方、客户,默认为客户,选择承担方为我方时,无需选择付款方式、输入氢气预付款;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.付款方式:必选项,填充按钮组,选项为预付、月付款、自行结算;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.1.预付:指合同签署时客户就需预先付出的氢费款项;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.2.月付款:指合同签署后,客户按照每月氢费实际账单,进行支付,设置为月付款时,每月账期时会提示对应业务管理中心-能源部完善氢费账单;'), React.createElement('div', { style: reqSpecLi }, '5.1.4.3.自行结算:指合同签署后,所有氢气费用由客户自行承担;'), React.createElement('div', { style: reqSpecLi }, '5.1.5.氢气预付款:必填项,输入框,支持2位小数,当付款方式为预付时,显示该输入框,氢气预付款金额会并入该合同交车前首付款中一并结算,并计入5.1.1.4.氢气预付款金额中;'), React.createElement('div', { style: reqSpecLi }, '5.1.6.退还车氢气单价:必填项,输入框,支持2位小数,后缀为元,该金额主要用于约定退还车时,与交车时氢气差值以此费用进行结算;')) + ); + var reqSpecDocPart3 = React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '6.其他费用信息卡片:'), React.createElement('div', { style: reqSpecP }, '6.1.用于选择对应费用模板,展示证照补办费用、违约金费用、易损件费用、其他费用信息;'), React.createElement('div', { style: reqSpecLi }, '6.1.1.选择费用模板:必选项,通过选择通过费用模板预设好的费用金额明细,自动将该模板所有环节费用显示在合同中;'), React.createElement('div', { style: reqSpecLi }, '6.1.2.证照补办费用:单独标题显示,内容为列表,列表中包括项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.3.违约金费用:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.4.易损件信息:单独标题显示,内容为列表,列表中包含类别、损坏部位、配件、数量、费用明细,选择费用模板后自动反显;'), React.createElement('div', { style: reqSpecLi }, '6.1.5.其他费用信息:单独标题显示,内容为列表,列表中包含项目、收费标准、服务费,选择费用模板后自动反显;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '7.账单计算方式卡片:'), React.createElement('div', { style: reqSpecP }, '7.1.必选项,填充按钮组,默认为按自然月结算,需要在两种账单计算方式二选一,可手动修改,用于定义租赁合同的账单计算方式,分为按付款周期天数结算、按自然月结算两种方式;'), React.createElement('div', { style: reqSpecLi }, '7.1.1.按付款周期天数结算:账单按照合同基本信息卡片中付款周期实际天数形成一期账单,例如付款周期为60天,则该账单账期为60天,每60天会自动生成新一期账单;'), React.createElement('div', { style: reqSpecLi }, '7.1.2.按自然月结算:账单按照第一个月计费日期开始-当月最后一天为第一期,之后按照付款周期设置,每个月第一天到对应月份最后一天的自然月方式,形成每一期账单,例如付款周期为2个月,则第二期账单从2月1日-3月31日,以此类推;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '8.最下方为提交并审核、保存、取消三个按钮;'), React.createElement('div', { style: reqSpecLi }, '8.1.点击提交并审核,toast提示:租赁合同已提交审核。同时该租赁合同进入租赁合同审核列表中;'), React.createElement('div', { style: reqSpecLi }, '8.2.点击保存,会存储租赁订单已填写内容,并加入租赁合同列表中,该条数据只能操作人自己查看并编辑,其他人无法操作;'), React.createElement('div', { style: reqSpecLi }, '8.3.点击取消,如当前页面有已编辑内容时,点击取消会进行二次提示,内容为:取消将会丢失所有已填写内容,是否确认?点击确认返回车辆租赁合同列表页;')), + React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecP }, '所有卡片支持收起/展开功能,通过点击卡片右侧收起/展开实现;并增加锚点功能,锚点固定于页面右上角,点击锚点对应卡片名称,页面自动跳转至该卡片所在区域;')) + ); + var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 640 }), onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc, reqSpecDocPart2, reqSpecDocPart3), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '转正式合同'))), + React.createElement('div', { style: styles.anchorWrap }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'), + React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式') + ), + React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)), + React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))), + React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)), + React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)), + React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)), + React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)), + React.createElement('div', { style: { height: 60 } }), + serviceModalContent, + reqSpecModalContent, + React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: function() { if (validateSubmitAndReview()) { message.success('租赁合同已提交审核。'); } } }, '提交并审核'), React.createElement(Button, { onClick: function() { message.info('保存,加入租赁合同列表(仅操作人可查看编辑)'); } }, '保存'), React.createElement(Button, { onClick: function() { message.info('取消'); } }, '取消')) + ); +}; diff --git a/web端/车辆管理.jsx b/web端/车辆管理.jsx new file mode 100644 index 0000000..ed7c405 --- /dev/null +++ b/web端/车辆管理.jsx @@ -0,0 +1,591 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆资产管理 - 车辆管理模块(中后台 Ant Design 原型) +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + var antd = window.antd; + + var Breadcrumb = antd.Breadcrumb; + var Cascader = antd.Cascader; + var Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Table = antd.Table; + var Space = antd.Space; + var Dropdown = antd.Dropdown; + var Modal = antd.Modal; + var Upload = antd.Upload; + var Card = antd.Card; + var Tabs = antd.Tabs; + var message = antd.message; + var App = antd.App; + + // 筛选项状态 + var _region = useState([]); + var _vehicleType = useState(undefined); + var _brand = useState(undefined); + var _model = useState(undefined); + var _customer = useState(undefined); + var _department = useState(undefined); + var _contractNo = useState(undefined); + var _ownership = useState(undefined); + + var _plateFilter = useState(''); + var _selectedRowKeys = useState([]); + var _uploadModalVisible = useState(false); + var _confirmModalVisible = useState(false); + var _ocrLoadingVisible = useState(false); + var _currentRow = useState(null); + var _plateForm = useState({ vin: '', plateNo: '' }); + var _plateError = useState(''); + var _detailRecord = useState(null); + var _detailCardExpanded = useState(false); + var _detailTab = useState('型号参数'); + var _filterExpanded = useState(false); + var _requirementModalVisible = useState(false); + + // 省-市 地区数据(示例) + var regionOptions = [ + { value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] }, + { value: 'beijing', label: '北京市', children: [{ value: 'beijing', label: '北京市' }] }, + { value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] } + ]; + + // 车辆类型、品牌、型号、客户、部门、合同、登记所有权(模拟下拉数据) + var vehicleTypeOptions = [{ label: '小型轿车', value: 'type1' }, { label: 'SUV', value: 'type2' }, { label: '厢式货车', value: 'type3' }]; + var brandOptions = [{ label: '比亚迪', value: 'byd' }, { label: '特斯拉', value: 'tsl' }, { label: '蔚来', value: 'nio' }]; + var modelOptions = [{ label: '汉EV', value: 'han' }, { label: 'Model 3', value: 'm3' }, { label: 'ET5', value: 'et5' }]; + var customerOptions = [{ label: '无', value: 'none' }, { label: '客户A', value: 'c1' }, { label: '客户B', value: 'c2' }]; + var departmentOptions = [{ label: '无', value: 'none' }, { label: '华南区', value: 'd1' }, { label: '华东区', value: 'd2' }]; + var contractOptions = [{ label: 'HT-2024-001', value: 'HT-2024-001' }, { label: 'HT-2024-002', value: 'HT-2024-002' }]; + var ownershipOptions = [{ label: '某某租赁公司', value: 'o1' }, { label: '某某科技有限公司', value: 'o2' }]; + + // 表格数据(模拟 20 条,状态按产品枚举) + // 运营状态: 待运营、库存、租赁、自营、退出运营 + // 库位状态: 新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车 + // 出库状态: 异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无 + // 预占状态: 331 后迭代,暂用「未预占」 + // 整备状态: 待整备、整备中、正常、无 + // 过户状态: 过户中、内部过户完成、销售过户完成、无 + // 维修状态: 待服务站接单、维修中、正常 + // 证照状态: 正常、异常 + // 报废状态: 报废中、已报废、无 + var rawData = [ + { id: '1', region: '广东省/广州市', vin: 'LGWEF4A59NS123456', plateNo: '粤A12345', vehicleNo: 'V001', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '12580.50', purchaseDate: '2023-06-15', regDate: '2023-07-01', inspectExpire: '2025-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '12000.00', lastReturnTime: '2024-02-01', lastReturnMile: '12580.50', scrapDate: '2038-12-31', contractNo: 'HT-2024-001', location: '广东省广州市天河区天河路100号', gpsTime: '2024-02-12 14:30' }, + { id: '2', region: '广东省/深圳市', vin: 'LGWEF4A59NS789012', plateNo: '粤B67890', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '25600.00', purchaseDate: '2022-08-20', regDate: '2022-09-01', inspectExpire: '2024-09', lastDeliveryTime: '2024-01-05', lastDeliveryMile: '25500.00', lastReturnTime: '2024-01-20', lastReturnMile: '25600.00', scrapDate: '2037-09-30', contractNo: '-', location: '广东省深圳市南山区科技园南路', gpsTime: '2024-02-11 09:00' }, + { id: '3', region: '广东省/广州市', vin: 'LSJA24U70PS001234', plateNo: '粤A88K88', vehicleNo: 'V003', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '灰色', parking: '黄埔停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '8320.00', purchaseDate: '2023-09-01', regDate: '2023-09-20', inspectExpire: '2025-09', lastDeliveryTime: '2024-02-05', lastDeliveryMile: '8100.00', lastReturnTime: '2024-02-10', lastReturnMile: '8320.00', scrapDate: '2039-09-30', contractNo: 'HT-2024-002', location: '广东省广州市黄埔区开泰大道200号', gpsTime: '2024-02-12 10:15' }, + { id: '4', region: '北京市/北京市', vin: 'WVWZZZ3CZWE123456', plateNo: '京C12345', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '朝阳停车场', customer: '无', department: '无', manager: '-', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '整备中', transferStatus: '无', repairStatus: '维修中', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '18900.25', purchaseDate: '2022-11-10', regDate: '2022-12-01', inspectExpire: '2024-12', lastDeliveryTime: '2024-01-15', lastDeliveryMile: '18500.00', lastReturnTime: '2024-01-28', lastReturnMile: '18900.25', scrapDate: '2037-12-31', contractNo: '-', location: '北京市朝阳区望京街88号', gpsTime: '2024-02-10 16:00' }, + { id: '5', region: '上海市/上海市', vin: 'LSVAU2BR3NS567890', plateNo: '沪D66666', vehicleNo: 'V005', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2021', mileage: '45200.80', purchaseDate: '2021-05-20', regDate: '2021-06-15', inspectExpire: '2024-06', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '44800.00', lastReturnTime: '2024-02-08', lastReturnMile: '45200.80', scrapDate: '2036-06-30', contractNo: 'HT-2024-003', location: '上海市浦东新区张江高科路500号', gpsTime: '2024-02-12 09:45' }, + { id: '6', region: '广东省/深圳市', vin: '5YJ3E1EA1NF123456', plateNo: '粤B12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '红色', parking: '福田停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '5600.00', purchaseDate: '2023-03-08', regDate: '2023-04-01', inspectExpire: '2025-04', lastDeliveryTime: '2024-02-02', lastDeliveryMile: '5200.00', lastReturnTime: '2024-02-11', lastReturnMile: '5600.00', scrapDate: '2038-04-30', contractNo: 'HT-2024-004', location: '广东省深圳市福田区福华路188号', gpsTime: '2024-02-12 11:20' }, + { id: '7', region: '广东省/广州市', vin: 'LGWEF4A59NS234567', plateNo: '粤A99A99', vehicleNo: 'V007', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '22100.30', purchaseDate: '2022-07-15', regDate: '2022-08-01', inspectExpire: '2024-08', lastDeliveryTime: '2024-01-20', lastDeliveryMile: '21800.00', lastReturnTime: '2024-02-05', lastReturnMile: '22100.30', scrapDate: '2037-08-31', contractNo: '-', location: '广东省广州市天河区体育西路200号', gpsTime: '2024-02-11 18:30' }, + { id: '8', region: '北京市/北京市', vin: 'WVWZZZ3CZWE789012', plateNo: '京E88888', vehicleNo: '-', vehicleType: 'SUV', brand: '蔚来', model: 'ET5', color: '绿色', parking: '-', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2023', mileage: '11200.00', purchaseDate: '2023-01-12', regDate: '2023-02-01', inspectExpire: '2025-02', lastDeliveryTime: '2024-02-06', lastDeliveryMile: '11000.00', lastReturnTime: '2024-02-12', lastReturnMile: '11200.00', scrapDate: '2038-02-28', contractNo: 'HT-2024-005', location: '北京市海淀区中关村大街1号', gpsTime: '2024-02-12 08:00' }, + { id: '9', region: '上海市/上海市', vin: 'LSVAU2BR3NS111222', plateNo: '沪A12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '银色', parking: '浦东停车场', customer: '无', department: '无', manager: '-', operateStatus: '库存', storageStatus: '库存车-呆滞车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2021', mileage: '38500.60', purchaseDate: '2021-10-05', regDate: '2021-11-01', inspectExpire: '2024-11', lastDeliveryTime: '2024-01-08', lastDeliveryMile: '38000.00', lastReturnTime: '2024-01-25', lastReturnMile: '38500.60', scrapDate: '2036-11-30', contractNo: '-', location: '上海市徐汇区漕溪路250号', gpsTime: '2024-02-09 14:00' }, + { id: '10', region: '广东省/深圳市', vin: '5YJ3E1EA2NF333444', plateNo: '粤B55B55', vehicleNo: 'V010', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '白色', parking: '南山停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '16800.00', purchaseDate: '2022-04-18', regDate: '2022-05-10', inspectExpire: '2024-05', lastDeliveryTime: '2024-01-30', lastDeliveryMile: '16500.00', lastReturnTime: '2024-02-09', lastReturnMile: '16800.00', scrapDate: '2037-05-31', contractNo: 'HT-2024-006', location: '广东省深圳市南山区后海大道300号', gpsTime: '2024-02-12 13:10' }, + { id: '11', region: '广东省/广州市', vin: 'LSJA24U70PS555666', plateNo: '粤A11B22', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '蓝色', parking: '番禺停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-替换交车', outStatus: '替换交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '7200.50', purchaseDate: '2023-07-20', regDate: '2023-08-05', inspectExpire: '2025-08', lastDeliveryTime: '2024-02-03', lastDeliveryMile: '7000.00', lastReturnTime: '2024-02-11', lastReturnMile: '7200.50', scrapDate: '2038-08-31', contractNo: 'HT-2024-007', location: '广东省广州市番禺区市桥街100号', gpsTime: '2024-02-12 12:00' }, + { id: '12', region: '北京市/北京市', vin: 'WVWZZZ3CZWE333555', plateNo: '京F33333', vehicleNo: '-', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '大兴停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2020', mileage: '52100.00', purchaseDate: '2020-09-01', regDate: '2020-10-01', inspectExpire: '2024-10', lastDeliveryTime: '2024-01-12', lastDeliveryMile: '51800.00', lastReturnTime: '2024-01-30', lastReturnMile: '52100.00', scrapDate: '2035-10-31', contractNo: '-', location: '北京市大兴区亦庄经济开发区', gpsTime: '2024-02-08 11:00' }, + { id: '13', region: '上海市/上海市', vin: 'LSVAU2BR3NS777888', plateNo: '沪B99999', vehicleNo: 'V013', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '内部过户完成', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '19800.00', purchaseDate: '2022-12-01', regDate: '2023-01-05', inspectExpire: '2025-01', lastDeliveryTime: '2024-02-07', lastDeliveryMile: '19500.00', lastReturnTime: '2024-02-12', lastReturnMile: '19800.00', scrapDate: '2038-01-31', contractNo: 'HT-2024-008', location: '上海市闵行区莘庄镇莘松路88号', gpsTime: '2024-02-12 15:45' }, + { id: '14', region: '广东省/深圳市', vin: 'LGWEF4A59NS888999', plateNo: '粤B22C33', vehicleNo: '-', vehicleType: 'SUV', brand: '比亚迪', model: '汉EV', color: '白色', parking: '龙岗停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '4200.00', purchaseDate: '2023-10-10', regDate: '2023-11-01', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-04', lastDeliveryMile: '4000.00', lastReturnTime: '2024-02-10', lastReturnMile: '4200.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-009', location: '广东省深圳市龙岗区龙城大道500号', gpsTime: '2024-02-12 10:30' }, + { id: '15', region: '广东省/广州市', vin: 'LSJA24U70PS000111', plateNo: '粤A66D66', vehicleNo: 'V015', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '红色', parking: '天河停车场', customer: '无', department: '无', manager: '-', operateStatus: '待运营', storageStatus: '新车入库-待验车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2021', mileage: '31200.40', purchaseDate: '2021-03-15', regDate: '2021-04-01', inspectExpire: '2024-04', lastDeliveryTime: '2024-01-18', lastDeliveryMile: '30800.00', lastReturnTime: '2024-02-02', lastReturnMile: '31200.40', scrapDate: '2036-04-30', contractNo: '-', location: '广东省广州市越秀区中山五路66号', gpsTime: '2024-02-07 09:00' }, + { id: '16', region: '北京市/北京市', vin: '5YJ3E1EA3NF222333', plateNo: '京G12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '银色', parking: '西城停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '9800.00', purchaseDate: '2023-05-01', regDate: '2023-05-20', inspectExpire: '2025-05', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '9500.00', lastReturnTime: '2024-02-11', lastReturnMile: '9800.00', scrapDate: '2038-05-31', contractNo: 'HT-2024-010', location: '北京市西城区金融街28号', gpsTime: '2024-02-12 14:00' }, + { id: '17', region: '上海市/上海市', vin: 'LGWEF4A59NS444555', plateNo: '沪C11111', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '虹口停车场', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '24500.00', purchaseDate: '2022-02-28', regDate: '2022-03-15', inspectExpire: '2024-03', lastDeliveryTime: '2024-01-22', lastDeliveryMile: '24200.00', lastReturnTime: '2024-02-06', lastReturnMile: '24500.00', scrapDate: '2037-03-31', contractNo: '-', location: '上海市虹口区四川北路1688号', gpsTime: '2024-02-11 17:00' }, + { id: '18', region: '广东省/深圳市', vin: 'LSVAU2BR3NS666777', plateNo: '粤B44E44', vehicleNo: 'V018', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '待服务站接单', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2020', mileage: '67800.25', purchaseDate: '2020-06-10', regDate: '2020-07-01', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-25', lastDeliveryMile: '67500.00', lastReturnTime: '2024-02-09', lastReturnMile: '67800.25', scrapDate: '2035-07-31', contractNo: 'HT-2024-011', location: '广东省深圳市宝安区新安街道创业路', gpsTime: '2024-02-12 08:20' }, + { id: '19', region: '广东省/广州市', vin: '5YJ3E1EA4NF888999', plateNo: '粤A77F77', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '白云停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '3500.00', purchaseDate: '2023-11-05', regDate: '2023-11-25', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-08', lastDeliveryMile: '3200.00', lastReturnTime: '2024-02-12', lastReturnMile: '3500.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-012', location: '广东省广州市白云区白云大道南888号', gpsTime: '2024-02-12 16:00' }, + { id: '20', region: '北京市/北京市', vin: 'LSJA24U70PS999000', plateNo: '京H88888', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '白色', parking: '昌平停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '退出运营', storageStatus: '退出运营-报废车', outStatus: '报废出库', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '报废中', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '15600.00', purchaseDate: '2022-06-20', regDate: '2022-07-10', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '15300.00', lastReturnTime: '2024-01-28', lastReturnMile: '15600.00', scrapDate: '2037-07-31', contractNo: '-', location: '北京市昌平区回龙观西大街100号', gpsTime: '2024-02-10 12:30' } + ]; + + var dataSource = useMemo(function () { + var plate = _plateFilter[0]; + if (!plate || plate.trim() === '') return rawData; + return rawData.filter(function (row) { + return row.plateNo && row.plateNo.indexOf(plate) !== -1; + }); + }, [rawData, _plateFilter[0]]); + + var onPlateFilterChange = useCallback(function (e) { + _plateFilter[1](e.target.value); + }, []); + + var onSelectChange = useCallback(function (keys) { + _selectedRowKeys[1](keys); + }, []); + + var rowSelection = { + selectedRowKeys: _selectedRowKeys[0], + onChange: onSelectChange + }; + + var handleExport = useCallback(function () { + if (_selectedRowKeys[0].length === 0) { + message.warning('请先勾选要导出的记录'); + return; + } + message.success('导出功能(联动多选):已选 ' + _selectedRowKeys[0].length + ' 条'); + }, []); + + var handleBatchImport = useCallback(function () { + message.info('批量导入:请上传文件(原型演示)'); + }, []); + + var goDetail = useCallback(function (record) { + _detailRecord[1](record); + }, []); + + var backToList = useCallback(function () { + _detailRecord[1](null); + }, []); + + var openUploadModal = useCallback(function (record) { + _currentRow[1](record); + _uploadModalVisible[1](true); + _confirmModalVisible[1](false); + _plateForm[1]({ vin: record.vin || '', plateNo: record.plateNo || '' }); + _plateError[1](''); + }, []); + + var closeUploadModal = useCallback(function () { + _uploadModalVisible[1](false); + _currentRow[1](null); + }, []); + + var startOcrThenConfirm = useCallback(function () { + _uploadModalVisible[1](false); + _ocrLoadingVisible[1](true); + setTimeout(function () { + _ocrLoadingVisible[1](false); + _confirmModalVisible[1](true); + }, 1500); + }, []); + + var closeConfirmModal = useCallback(function () { + _confirmModalVisible[1](false); + _currentRow[1](null); + _plateForm[1]({ vin: '', plateNo: '' }); + _plateError[1](''); + }, []); + + var onPlateFormChange = useCallback(function (field, value) { + _plateError[1](''); + _plateForm[1](function (prev) { + var next = {}; + next[field] = value; + return Object.assign({}, prev, next); + }); + }, []); + + var confirmPlate = useCallback(function () { + var row = _currentRow[0]; + var form = _plateForm[0]; + if (!row) return; + var vinMatch = form.vin && row.vin && form.vin.trim() === row.vin.trim(); + if (!form.vin || !form.plateNo) { + _plateError[1]('请填写车辆识别代号与车牌号'); + return; + } + if (!vinMatch) { + _plateError[1]('车辆识别代号与该车辆不匹配'); + return; + } + message.success('上牌信息已更新(原型演示)'); + closeConfirmModal(); + }, []); + + var getMoreMenuItems = function (record) { + return [ + { key: 'plate', label: '车辆上牌', onClick: function () { openUploadModal(record); } }, + { key: 'transfer', label: '车辆过户', onClick: function () { message.info('车辆过户(原型)'); } }, + { key: 'move', label: '车辆异动', onClick: function () { message.info('车辆异动(原型)'); } }, + { key: 'allocate', label: '车辆调拨', onClick: function () { message.info('车辆调拨(原型)'); } }, + { key: 'scrap', label: '车辆报废', onClick: function () { message.info('车辆报废(原型)'); } }, + { key: 'inspect', label: '车辆年审', onClick: function () { message.info('车辆年审(原型)'); } }, + { key: 'sell', label: '车辆出售', onClick: function () { message.info('车辆出售(原型)'); } }, + { key: 'fault', label: '故障提报', onClick: function () { message.info('故障提报(原型)'); } } + ]; + }; + + var columns = [ + { title: '运营城市', dataIndex: 'region', key: 'region', width: 140, fixed: 'left' }, + { title: '车辆识别代号', dataIndex: 'vin', key: 'vin', width: 180, fixed: 'left' }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110, fixed: 'left' }, + { title: '车辆编号', dataIndex: 'vehicleNo', key: 'vehicleNo', width: 100 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 90 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, + { title: '车身颜色', dataIndex: 'color', key: 'color', width: 90 }, + { title: '归属停车场', dataIndex: 'parking', key: 'parking', width: 120 }, + { title: '客户名称', dataIndex: 'customer', key: 'customer', width: 100 }, + { title: '业务部门', dataIndex: 'department', key: 'department', width: 100 }, + { title: '业务负责人', dataIndex: 'manager', key: 'manager', width: 100 }, + { title: '运营状态', dataIndex: 'operateStatus', key: 'operateStatus', width: 90 }, + { title: '库位状态', dataIndex: 'storageStatus', key: 'storageStatus', width: 180 }, + { title: '出库状态', dataIndex: 'outStatus', key: 'outStatus', width: 90 }, + { title: '整备状态', dataIndex: 'prepareStatus', key: 'prepareStatus', width: 90 }, + { title: '过户状态', dataIndex: 'transferStatus', key: 'transferStatus', width: 90 }, + { title: '维修状态', dataIndex: 'repairStatus', key: 'repairStatus', width: 90 }, + { title: '证照状态', dataIndex: 'licenseStatus', key: 'licenseStatus', width: 90 }, + { title: '报废状态', dataIndex: 'scrapStatus', key: 'scrapStatus', width: 90 }, + { title: '登记所有权', dataIndex: 'ownership', key: 'ownership', width: 160 }, + { title: '在线状态', dataIndex: 'onlineStatus', key: 'onlineStatus', width: 90 }, + { title: '出厂年份', dataIndex: 'year', key: 'year', width: 90 }, + { title: '行驶公里数(KM)', dataIndex: 'mileage', key: 'mileage', width: 130 }, + { title: '采购入库时间', dataIndex: 'purchaseDate', key: 'purchaseDate', width: 120 }, + { title: '行驶证注册日期', dataIndex: 'regDate', key: 'regDate', width: 130 }, + { title: '行驶证检验有效期', dataIndex: 'inspectExpire', key: 'inspectExpire', width: 130 }, + { title: '上次交车时间', dataIndex: 'lastDeliveryTime', key: 'lastDeliveryTime', width: 120 }, + { title: '上次交车里程(KM)', dataIndex: 'lastDeliveryMile', key: 'lastDeliveryMile', width: 140 }, + { title: '上次还车时间', dataIndex: 'lastReturnTime', key: 'lastReturnTime', width: 120 }, + { title: '上次还车里程(KM)', dataIndex: 'lastReturnMile', key: 'lastReturnMile', width: 140 }, + { title: '强制报废日期', dataIndex: 'scrapDate', key: 'scrapDate', width: 120 }, + { title: '合同编号', dataIndex: 'contractNo', key: 'contractNo', width: 120 }, + { title: '当前位置', dataIndex: 'location', key: 'location', width: 300 }, + { title: 'GPS最后上传时间', dataIndex: 'gpsTime', key: 'gpsTime', width: 160 }, + { + title: '操作', + key: 'action', + width: 140, + fixed: 'right', + render: function (_, record) { + return React.createElement(Space, null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goDetail(record); } }, '查看'), + React.createElement(Dropdown, { menu: { items: getMoreMenuItems(record) }, trigger: ['click'] }, + React.createElement(Button, { type: 'link', size: 'small' }, '更多') + ) + ); + } + } + ]; + + var filterControlStyle = { width: '100%' }; + var layoutStyle = { padding: '16px 24px', background: '#fff', minHeight: '100vh' }; + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var toolbarStyle = { marginBottom: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }; + var tableSingleLineStyle = '.vehicle-mgmt-table .ant-table-thead th,.vehicle-mgmt-table .ant-table-tbody td{white-space:nowrap;}'; + + // 车辆详情页:卡片标题、默认只显示前三列+收起/展开,下方 Tab+内容卡片 + var detailRecord = _detailRecord[0]; + if (detailRecord) { + var r = detailRecord; + var labelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 100, textAlign: 'right' }; + var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' }; + var linkStyle = { color: '#1890ff', cursor: 'pointer', fontSize: 14 }; + var cellStyle = { marginBottom: 16, minHeight: 22, display: 'flex', alignItems: 'center', gap: 8 }; + var link = function (text, onClick) { return React.createElement('a', { style: linkStyle, onClick: onClick }, text); }; + var detailCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' }; + var gridColStyle = { flex: '1', minWidth: 180 }; + var field = function (label, val) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, val)); }; + var fieldLink = function (label, text, onClick) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, link(text, onClick))); }; + var detailFields = [ + { label: '车架号', node: field('车架号', r.vin || '-') }, + { label: '车辆编号', node: field('车辆编号', r.vehicleNo || '-') }, + { label: '实际所有权', node: field('实际所有权', r.ownership || '-') }, + { label: '合同编号', node: r.contractNo && r.contractNo !== '-' ? fieldLink('合同编号', r.contractNo, function () { message.info('合同详情(原型)'); }) : field('合同编号', r.contractNo || '-') }, + { label: '出库状态', node: field('出库状态', r.outStatus || '-') }, + { label: '过户状态', node: field('过户状态', r.transferStatus || '-') }, + { label: '车身颜色', node: field('车身颜色', r.color || '-') }, + { label: '采购交车日期', node: field('采购交车日期', r.purchaseDate || '-') }, + { label: '登记所有权', node: field('登记所有权', r.ownership || '-') }, + { label: '客户名称', node: field('客户名称', r.customer || '-') }, + { label: '整备状态', node: field('整备状态', r.prepareStatus || '-') }, + { label: '证照状态', node: field('证照状态', r.licenseStatus || '-') }, + { label: '资源分类', node: field('资源分类', 'XXXXXXXXX') }, + { label: '出厂年份', node: field('出厂年份', r.year || '-') }, + { label: '库位状态', node: field('库位状态', r.storageStatus || '-') }, + { label: '业务部门', node: field('业务部门', r.department || '-') }, + { label: '上次整备时间', node: field('上次整备时间', r.lastDeliveryTime || '-') }, + { label: '报废状态', node: field('报废状态', r.scrapStatus || '-') }, + { label: '资产评级', node: field('资产评级', 'XXXXXXXXX') }, + { label: '等评时间', node: field('等评时间', r.regDate || '-') }, + { label: '强制报废期', node: field('强制报废期', r.scrapDate || '-') }, + { label: '停车位置', node: field('停车位置', r.parking || '-') }, + { label: '业务负责人', node: r.manager && r.manager !== '-' ? fieldLink('业务负责人', r.manager, function () { message.info('负责人(原型)'); }) : field('业务负责人', r.manager || '-') }, + { label: '维修状态', node: field('维修状态', r.repairStatus || '-') }, + { label: '运营城市', node: field('运营城市', r.region ? r.region.replace(/\//g, '-') : '-') }, + { label: '下次年检时间', node: field('下次年检时间', r.inspectExpire || '-') }, + { label: '上次维修时间', node: field('上次维修时间', r.lastDeliveryTime || '-') }, + { label: 'GPS最后上传时间', node: field('GPS最后上传时间', r.gpsTime || '-') }, + { label: '车辆当前位置', node: fieldLink('车辆当前位置', r.location || '-', function () { message.info('查看位置(原型)'); }) } + ]; + var rowCount = _detailCardExpanded[0] ? 8 : 3; + var rows = []; + for (var i = 0; i < rowCount; i++) { + var rowCells = []; + for (var j = 0; j < 4; j++) { + var idx = i * 4 + j; + rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, detailFields[idx] ? detailFields[idx].node : null)); + } + rows.push(React.createElement('div', { key: i, style: { display: 'flex', gap: 24, marginBottom: i < rowCount - 1 ? 16 : 0 } }, rowCells)); + } + var tabContentCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 0, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' }; + var tabContent = function (text) { return React.createElement('div', { style: { padding: '8px 0', color: 'rgba(0,0,0,0.65)', fontSize: 14 } }, text); }; + var sectionTitleStyle = { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16, fontSize: 15, fontWeight: 600, color: 'rgba(0,0,0,0.85)' }; + var sectionIconStyle = { width: 6, height: 6, borderRadius: 3, backgroundColor: '#1890ff', flexShrink: 0 }; + var kvLabelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 90, textAlign: 'right' }; + var kvValueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' }; + var kvItem = function (label, value, badge) { + var valNode = value; + if (badge) valNode = React.createElement('span', null, value, React.createElement('span', { style: { marginLeft: 6, padding: '0 6px', fontSize: 12, background: '#faad14', color: '#fff', borderRadius: 2 } }, badge)); + return React.createElement('div', { key: label, style: { display: 'flex', alignItems: 'center', gap: 8 } }, React.createElement('span', { style: kvLabelStyle }, label), React.createElement('span', { style: kvValueStyle }, valNode)); + }; + var modelParamRowStyle = { display: 'flex', gap: 24, marginBottom: 16 }; + var fourColRows = function (nodes) { + var out = []; + for (var i = 0; i < nodes.length; i += 4) { + var rowCells = []; + for (var j = 0; j < 4; j++) { + var idx = i + j; + rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, nodes[idx] || null)); + } + out.push(React.createElement('div', { key: i, style: modelParamRowStyle }, rowCells)); + } + return out; + }; + var maintenanceTableData = [ + { key: '1', no: 1, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' }, + { key: '2', no: 2, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' }, + { key: '3', no: 3, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' }, + { key: '4', no: 4, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' }, + { key: '5', no: 5, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' } + ]; + var maintenanceColumns = [ + { title: '序号', dataIndex: 'no', key: 'no', width: 60 }, + { title: '养护项目', dataIndex: 'item', key: 'item', width: 120 }, + { title: '保养公里周期(km)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140 }, + { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 130 }, + { title: '工时费(元)', dataIndex: 'labor', key: 'labor', width: 100 }, + { title: '材料费(元)', dataIndex: 'material', key: 'material', width: 100 }, + { title: '合计', dataIndex: 'total', key: 'total', width: 80 }, + { title: '上次保养公里数(KM)', dataIndex: 'lastKm', key: 'lastKm', width: 140 } + ]; + var modelParamContent = React.createElement('div', { style: tabContentCardStyle }, + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '型号参数'), + React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([ + kvItem('品牌', r.brand || '苏龙'), + kvItem('型号', r.model || '海格牌KLQ5180XYKFCEV'), + kvItem('车辆类型', r.vehicleType || '18吨双飞翼货车'), + kvItem('燃料种类', '氢'), + kvItem('整车尺寸', '5995mm x 2145mm x 3130mm'), + kvItem('车牌颜色', '绿牌') + ])), + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '轮胎情况'), + React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([ + kvItem('轮胎数量', '8'), + kvItem('轮胎规格', '15/80R22.5') + ])), + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '电气系统'), + React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([ + kvItem('电池类型', '磷酸铁锂'), + kvItem('电池厂家', 'XXXXXXXXXXXXX企业名称'), + kvItem('储电量', '100000 kWh'), + kvItem('续航里程', '200 KM') + ])), + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '供氢系统'), + React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([ + kvItem('氢瓶容量', 'xxx L'), + kvItem('仪表盘模式', 'MPa'), + kvItem('续航里程', '1000 KM'), + kvItem('供氢系统厂家', 'XXXXXXXX企业') + ])), + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '其他系统'), + React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([ + kvItem('冷机生产企业', 'XXXXXXXX企业'), + kvItem('电堆生产企业', 'XXXXXXXX企业') + ])), + React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '保养参数'), + React.createElement(Table, { size: 'small', rowKey: 'key', columns: maintenanceColumns, dataSource: maintenanceTableData, pagination: false }) + ); + var detailTabItems = [ + { key: '型号参数', label: '型号参数', children: modelParamContent }, + { key: '后装设备', label: '后装设备', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('后装设备列表(原型占位)')) }, + { key: '证照信息', label: '证照信息', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('证照信息(原型占位)')) }, + { key: '租赁记录', label: '租赁记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('租赁记录(原型占位)')) }, + { key: '保险记录', label: '保险记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('保险记录(原型占位)')) }, + { key: '维修记录', label: '维修记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('维修记录(原型占位)')) }, + { key: '事故记录', label: '事故记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('事故记录(原型占位)')) }, + { key: '故障记录', label: '故障记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('故障记录(原型占位)')) }, + { key: '违章记录', label: '违章记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('违章记录(原型占位)')) }, + { key: '异动记录', label: '异动记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('异动记录(原型占位)')) }, + { key: '调拨记录', label: '调拨记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('调拨记录(原型占位)')) }, + { key: '整备记录', label: '整备记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('整备记录(原型占位)')) } + ]; + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement(Breadcrumb, { + style: { marginBottom: 16 }, + items: [ + { title: '运维管理' }, + { title: React.createElement('a', { onClick: backToList, style: { color: '#1890ff' } }, '车辆管理') }, + { title: '车辆详情' } + ] + }), + React.createElement('div', { style: detailCardStyle }, + React.createElement('div', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)', marginBottom: 24 } }, '车辆详情'), + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24 } }, + React.createElement('span', { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: r.onlineStatus === '在线' ? '#52c41a' : '#d9d9d9', flexShrink: 0 } }), + React.createElement('span', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)' } }, r.plateNo || '-'), + React.createElement(Button, { type: 'primary', size: 'small' }, r.operateStatus || '租赁') + ), + React.createElement('div', { style: { marginBottom: 24 } }, rows), + React.createElement('div', { style: { textAlign: 'center', paddingTop: 16, marginTop: 8, borderTop: '1px solid #f0f0f0' } }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _detailCardExpanded[1](!_detailCardExpanded[0]); } }, _detailCardExpanded[0] ? '收起' : '展开') + ) + ), + React.createElement('div', { style: Object.assign({}, detailCardStyle, { marginTop: 24 }) }, + React.createElement(Tabs, { + activeKey: _detailTab[0], + onChange: function (key) { _detailTab[1](key); }, + items: detailTabItems + }) + ) + ) + ); + } + + var requirementText = '# 车辆管理\n一个「车辆资产管理」后台的「车辆管理」模块,功能从上到下依次为:\n\n1.面包屑:\n运维管理-车辆管理\n\n2.筛选:\n2.1.运营城市:地区选择器,支持省-市2级地区选择;\n2.2.车辆类型:选择器,根据车辆类型表拉取;\n2.3.品牌:选择器,根据型号参数表拉取品牌字段;\n2.4.型号:选择器,根据型号参数表拉取型号字段;\n2.5.客户名称:选择器,根据租赁合同/自营合同对应客户名称拉取,并包含无;\n2.6.归属业务部门:选择器,根据租赁合同/自营合同对应归属业务部门拉取,并包含无;\n2.7.合同编号:选择器,支持输入框内输入内容模糊匹配选项;\n2.8.登记所有权:选择器,支持输入框内输入内容模糊匹配选项;\n\n3.列表:\n列表左上角为车牌号筛选框,输入车牌号可快速筛选列表内记录;右侧按钮为导出(联动多选)、批量导入,字段依次为运营城市、车辆识别代号、车牌号、车辆编号、车辆类型、品牌、型号、车身颜色、归属停车场、客户名称、业务部门、业务负责人、运营状态、库位状态、出库状态、预占状态、整备状态、过户状态、维修状态、证照状态、报废状态、登记所有权、在线状态、出厂年份、行驶公里数、采购入库时间、行驶证注册日期、行驶证检验有效期、上次交车时间、上次交车里程(KM)、上次还车时间、上次还车里程(KM)、强制报废日期、合同编号、当前位置、GPS最后上传时间、操作;\n3.1.运营城市:列固定在左侧,车辆实际运营城市,通过GPS当前位置获取,只显示至省-市2级;\n3.2.车辆识别代号:列固定在左侧,显示车辆识别代号(VIN码),为唯一值,采购、外部租赁等方式录入的车架号与系统现有车架号不能重复,导入亦然;\n3.3.车牌号:列固定在左侧,显示车牌号,主要通过上牌管理功能录入;\n3.4.车辆编号:显示车辆编号,主要由旧版迁移数据获取(这批车在港口需要单独以编号记录),其他车辆无此需求;\n3.5.车辆类型:显示车辆对应车辆类型,由车辆类型表中获取;\n3.6.品牌:显示车辆对应品牌,由型号参数表-品牌字段获取;\n3.7.型号:显示车辆对应型号,由型号参数表-型号字段获取;\n3.8.车身颜色:显示车辆对应颜色,由用户采购/外部租赁车辆合同时填写车身颜色(旧版迁移数据)字段获取;\n3.9.归属停车场:显示车辆归属停车场,交车出去租赁给客户的车辆显示为-,还车或发生异动时,根据运维记录,记录最后停放的停车场,停车场从停车场表中获取;\n3.10.客户名称:显示车辆租赁/自营合同发生时,该合同对应客户名称;\n3.11.业务部门:显示车辆租赁/自营合同发生时,该合同对应业务部门;\n3.12.业务负责人:显示车辆租赁/自营合同发生时,该合同对应业务负责人;\n3.13.运营状态:分为待运营、库存、租赁、自营、退出运营\n3.14.库位状态:分为新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车;\n3.15.出库状态:分为异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无;\n3.16.预占状态:331前占位,该功能331后迭代;\n3.17.整备状态:分为待整备、整备中、正常、无;\n3.18.过户状态:分为过户中、内部过户完成、销售过户完成、无;\n3.19.维修状态:分为待服务站接单、维修中、正常;\n3.20.证照状态:分为正常、异常;\n3.21.报废状态:分为报废中、已报废、无;\n3.22.登记所有权:行驶证对应所有人;\n3.23.在线状态:分为在线、离线,显示GPS在线状态,根据实际对接设备取状态;\n3.24.出厂年份:格式为YYYY,根据车辆采购合同时记录的出厂年份获取;\n3.25.行驶公里数(KM):精确至2位小数,根据最近一次交车/还车/备车/异动/调拨记录时记录的行驶公里数获取;\n3.26.采购入库时间:格式为YYYY-MM-DD,根据采购合同时,采购完成车辆验车单提交时的时间;\n3.27.行驶证注册日期:格式为YYYY-MM-DD,根据上牌管理中,最后一次上传行驶证时的行驶证注册日期获取;\n3.28.行驶证检验有效期:根据行驶证(副联)中,检验记录中检验有效期获取;\n3.29.上次交车时间:格式为YYYY-MM-DD,根据最近一次交车记录完成时间获取;\n3.30.上次交车里程(KM):精确至2位小数,根据最近一次交车记录中记录的里程获取;\n3.31.上次还车日期:格式为YYYY-MM-DD,根据最近一次还车记录完成时间获取;\n3.32.上次还车里程(KM):精确至2位小数,根据最近一次还车记录中记录的里程获取;\n3.33.强制报废日期:格式为YYYY-MM-DD,根据行驶证(副联)中强制报废期获取;\n3.34.合同编号:显示当前租赁/自营合同合同编号,如无则显示为-;\n3.35.当前位置:根据车辆GPS当前定位获取,格式为xxx省xx市xx区/县xxxxx路xxxxx号xxxxxx\n3.36.GPS最后上传时间:格式为YYYY-MM-DD HH:MM,显示车辆GPS最后一次获取时间\n3.37.操作:操作列固定在右侧,显示查看、更多,悬浮更多时,显示车辆上牌、车辆过户、车辆异动、车辆调拨、车辆报废、车辆年审、车辆出售、故障提报;\n3.37.1.查看,点击跳转车辆详情页;\n3.38.2.车辆上牌,点击后上传照片,OCR识别过程中卡片提示:识别中,请勿关闭页面,之后弹出卡片,标题为确认上牌信息,左侧为行驶证照片,右侧为车辆识别代号(输入框,根据行驶证照片反写,可编辑)与车牌号(输入框,根据行驶证照片反写,可编辑),下侧为取消和确认按钮,点击确认,判断车辆识别代码与车牌号是否一致,如不一致提示车辆识别代号与该车辆不匹配,如一致则更新车牌号和行驶证照片(具体参考车辆业务-上牌管理);\n3.38.3.车辆过户:\n3.38.4.车辆异动:\n3.38.5.车辆调拨:\n3.38.6.车辆报废:\n3.38.7.车辆年审:\n3.38.8.车辆出售;\n3.38.9.故障提报:'; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: [{ title: '运维管理' }, { title: '车辆管理' }] }), + React.createElement(Button, { type: 'link', onClick: function () { _requirementModalVisible[1](true); } }, '查看需求说明') + ), + + React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'flex-start' } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + flex: 1, + minWidth: 0 + } + }, (function () { + var filterItems = [ + React.createElement('div', { key: 'region', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '运营城市'), React.createElement(Cascader, { options: regionOptions, placeholder: '请选择', style: filterControlStyle, value: _region[0].length ? _region[0] : undefined, onChange: function (v) { _region[1](v || []); } })), + React.createElement('div', { key: 'type', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '车辆类型'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: vehicleTypeOptions, value: _vehicleType[0], onChange: _vehicleType[1], allowClear: true })), + React.createElement('div', { key: 'brand', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '品牌'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: brandOptions, value: _brand[0], onChange: _brand[1], allowClear: true })), + React.createElement('div', { key: 'model', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '型号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: modelOptions, value: _model[0], onChange: _model[1], allowClear: true })), + React.createElement('div', { key: 'customer', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '客户名称'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: customerOptions, value: _customer[0], onChange: _customer[1], allowClear: true })), + React.createElement('div', { key: 'dept', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '归属业务部门'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: departmentOptions, value: _department[0], onChange: _department[1], allowClear: true })), + React.createElement('div', { key: 'contract', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '合同编号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: contractOptions, value: _contractNo[0], onChange: _contractNo[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } })), + React.createElement('div', { key: 'ownership', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '登记所有权'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: ownershipOptions, value: _ownership[0], onChange: _ownership[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } })) + ]; + var limit = _filterExpanded[0] ? 8 : 6; + var out = []; + for (var i = 0; i < limit && i < filterItems.length; i++) { + out.push(filterItems[i]); + } + return out; + })()), + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8, flexShrink: 0 } }, + React.createElement(Button, { type: 'primary', onClick: function () { message.info('查询(原型)'); } }, '查询'), + React.createElement(Button, { onClick: function () { + _region[1]([]); + _vehicleType[1](undefined); + _brand[1](undefined); + _model[1](undefined); + _customer[1](undefined); + _department[1](undefined); + _contractNo[1](undefined); + _ownership[1](undefined); + } }, '重置'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开') + ) + ), + + React.createElement('div', { style: toolbarStyle }, + React.createElement(Input.Search, { + placeholder: '输入车牌号筛选', + allowClear: true, + value: _plateFilter[0], + onChange: onPlateFilterChange, + onSearch: onPlateFilterChange, + style: { width: 220 } + }), + React.createElement(Space, null, + React.createElement(Button, { onClick: handleExport }, '导出'), + React.createElement(Button, { onClick: handleBatchImport }, '批量导入') + ) + ), + + React.createElement(React.Fragment, null, + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'vehicle-mgmt-table' }, + React.createElement(Table, { + rowKey: 'id', + columns: columns, + dataSource: dataSource, + rowSelection: rowSelection, + scroll: { x: 3800 }, + size: 'small', + pagination: { showSizeChanger: true, showQuickJumper: true, showTotal: function (t) { return '共 ' + t + ' 条'; } } + }) + ) + ), + + // 车辆上牌:上传/识别中 + React.createElement(Modal, { + title: '上传行驶证', + open: _uploadModalVisible[0], + onCancel: closeUploadModal, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: closeUploadModal }, '取消'), + React.createElement(Button, { key: 'ok', type: 'primary', onClick: startOcrThenConfirm }, '开始识别') + ] + }, React.createElement(Upload.Dragger, { accept: 'image/*', multiple: false }, React.createElement('p', null, '点击或拖拽行驶证照片到此区域上传'))), + + React.createElement(Modal, { + title: '识别中,请勿关闭页面', + open: _ocrLoadingVisible[0], + footer: null, + closable: false, + maskClosable: false + }, React.createElement('div', { style: { textAlign: 'center', padding: '24px 0' } }, '正在识别行驶证信息...')), + + React.createElement(Modal, { + title: '确认上牌信息', + open: _confirmModalVisible[0], + onCancel: closeConfirmModal, + width: 640, + footer: [ + React.createElement(Button, { key: 'cancel', onClick: closeConfirmModal }, '取消'), + React.createElement(Button, { key: 'ok', type: 'primary', onClick: confirmPlate }, '确认') + ] + }, React.createElement('div', { style: { display: 'flex', gap: 24 } }, + React.createElement('div', { style: { flex: '0 0 240px' } }, + React.createElement('div', { style: { marginBottom: 8, color: '#666' } }, '行驶证照片'), + React.createElement('img', { src: 'https://picsum.photos/240/160', alt: '行驶证', style: { width: '100%', borderRadius: 8 } }) + ), + React.createElement('div', { style: { flex: 1 } }, + React.createElement('div', { style: { marginBottom: 12 } }, + React.createElement('span', { style: { marginRight: 8 } }, '车辆识别代号'), + React.createElement(Input, { + value: _plateForm[0].vin, + onChange: function (e) { onPlateFormChange('vin', e.target.value); }, + placeholder: '根据行驶证反写,可编辑', + style: { width: '100%' } + }) + ), + React.createElement('div', { style: { marginBottom: 12 } }, + React.createElement('span', { style: { marginRight: 8 } }, '车牌号'), + React.createElement(Input, { + value: _plateForm[0].plateNo, + onChange: function (e) { onPlateFormChange('plateNo', e.target.value); }, + placeholder: '根据行驶证反写,可编辑', + style: { width: '100%' } + }) + ), + _plateError[0] ? React.createElement('div', { style: { color: '#ff4d4f', fontSize: 12 } }, _plateError[0]) : null + ) + )), + React.createElement(Modal, { + title: '需求说明', + open: _requirementModalVisible[0], + onCancel: function () { _requirementModalVisible[1](false); }, + width: 720, + footer: React.createElement(Button, { onClick: function () { _requirementModalVisible[1](false); } }, '关闭') + }, React.createElement('div', { style: { maxHeight: 560, overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementText)) + ) + ); +}; diff --git a/web端/运维管理/.DS_Store b/web端/运维管理/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..feef74124c98d6b9cef5c3f559a36bc7872005ee GIT binary patch literal 6148 zcmeHKPfrs;6rTrGwxF^=tB5ga?8O8UQL2eChFUO6j6x{EB4FKZr)9%3)9h}kfFwP6 z&;#)Uc<|^qF!AEii&t;PkDym?zWKBK5xE+p^OBk0n>X*h+4*gD-VPxI3eD2DkbZAQ_B8VJrD{QzvcX9mgkhd`y1`Gp#8w29oP3+WmoeW%XFg#TDKvMo!cW3r?^oMsu9|Zmc`*yhmGQJ2k;s?I)ndb`c&uKE=+mg^ZPK#c|BLG^tv=o} z&22Q$C$(pg>FxxSGMR6>RtsrV+NUp+?cT5waNnQdp%;0aEi8q|^HfCFrdW;RitEVn zbVE#q^E3z%55<%goFH&mofbBheaEH#G%N5Z;%g9Tr`m^Ws_fMMYOWk8${8EgXc8rurx(Se=30w5;PECqFOmk==-jd_i2g*bx(Dil$L zg1%ya3P*cJ`{gyZ6{>Io`tkvKWzaVikgH?^}fMMVu1LEHw#TNDdHh#lL z-2bJM=?nvgfq#<$NK6(d3%EC3TMzA0)Y=TTC)k7tzqUddf}JkMQW2$i5StX7%LoNA Vud%HVEhy$kK+<43!@wV9;3uIc&G7&L literal 0 HcmV?d00001 diff --git a/web端/运维管理/基本数据维护/停车场管理.jsx b/web端/运维管理/基本数据维护/停车场管理.jsx new file mode 100644 index 0000000..27836d1 --- /dev/null +++ b/web端/运维管理/基本数据维护/停车场管理.jsx @@ -0,0 +1,1319 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 停车场管理 - 车辆资产管理后台模块 +// 设计变量参考 Arco Design Token: https://arco.design/react/docs/token +var ARCO_TOKEN = { + // 主色 / 品牌色 + primary: '#165DFF', + primaryHover: '#4080FF', + primaryActive: '#0E42D2', + // 功能色 + danger: '#F53F3F', + success: '#00B42A', + warning: '#FF7D00', + link: '#165DFF', + // 中性色 (Neutral) + neutral1: '#FFFFFF', + neutral2: '#F7F8FA', + neutral3: '#F2F3F5', + neutral4: '#E5E6EB', + neutral5: '#C9CDD4', + neutral6: '#86909C', + neutral7: '#4E5969', + neutral8: '#1D2129', + // 边框 + border: '#E5E6EB', + borderSecondary: '#C9CDD4', + // 填充/背景 + fill: '#F2F3F5', + fillSecondary: '#F7F8FA', + // 阴影 + shadowLight: '0 1px 2px rgba(0,0,0,0.05)', + shadowMedium: '0 2px 8px rgba(0,0,0,0.08)', + // 圆角 + radiusSmall: '2px', + radiusMedium: '4px', + radiusLarge: '8px', + // 间距 + spacing4: '4px', + spacing8: '8px', + spacing12: '12px', + spacing16: '16px', + spacing24: '24px', + // 字体 + fontSize14: '14px', + fontSize16: '16px', + fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif' +}; + +const Component = function () { + var _useState = React.useState('list'); + var currentView = _useState[0]; + var setCurrentView = _useState[1]; + + var _useState2 = React.useState([]); + var regionProvince = _useState2[0]; + var setRegionProvince = _useState2[1]; + + var _useState3 = React.useState(''); + var regionCity = _useState3[0]; + var setRegionCity = _useState3[1]; + + var _useState4 = React.useState(''); + var filterParkingName = _useState4[0]; + var setFilterParkingName = _useState4[1]; + + var _useState5 = React.useState(''); + var filterManager = _useState5[0]; + var setFilterManager = _useState5[1]; + + var _useState5b = React.useState(false); + var parkingNameInputFocused = _useState5b[0]; + var setParkingNameInputFocused = _useState5b[1]; + + var _useState5d = React.useState(false); + var managerInputFocused = _useState5d[0]; + var setManagerInputFocused = _useState5d[1]; + + var _useState6 = React.useState(false); + var showColumnSettings = _useState6[0]; + var setShowColumnSettings = _useState6[1]; + + var _useState6b = React.useState(false); + var columnIconHover = _useState6b[0]; + var setColumnIconHover = _useState6b[1]; + + var _useState6c = React.useState(false); + var regionCascaderOpen = _useState6c[0]; + var setRegionCascaderOpen = _useState6c[1]; + + var _useState6d = React.useState(false); + var formRegionCascaderOpen = _useState6d[0]; + var setFormRegionCascaderOpen = _useState6d[1]; + + var _useState6e = React.useState(''); + var regionCascaderHover = _useState6e[0]; + var setRegionCascaderHover = _useState6e[1]; + var _useState6f = React.useState(''); + var formRegionCascaderHover = _useState6f[0]; + var setFormRegionCascaderHover = _useState6f[1]; + + var _useState7 = React.useState(null); + var vehicleDetailParkingId = _useState7[0]; + var setVehicleDetailParkingId = _useState7[1]; + + var _useState8 = React.useState(null); + var viewParking = _useState8[0]; + var setViewParking = _useState8[1]; + + var _useState9 = React.useState(null); + var editParking = _useState9[0]; + var setEditParking = _useState9[1]; + + var _useState10 = React.useState(null); + var deleteConfirmParking = _useState10[0]; + var setDeleteConfirmParking = _useState10[1]; + + var _useState11 = React.useState(''); + var toastMessage = _useState11[0]; + var setToastMessage = _useState11[1]; + + var _useState11b = React.useState(false); + var showRequirementModal = _useState11b[0]; + var setShowRequirementModal = _useState11b[1]; + + // 分页状态 + var _useState12 = React.useState(1); + var currentPage = _useState12[0]; + var setCurrentPage = _useState12[1]; + var _useState13 = React.useState(10); + var pageSize = _useState13[0]; + var setPageSize = _useState13[1]; + + // 新建表单状态 + var _useState12 = React.useState({ + regionProvince: '', + regionCity: '', + parkingName: '', + parkingSpaces: '', + address: '', + manager: '', + managerPhone: '', + contactName: '', + contactPhone: '', + leaseStart: '', + leaseEnd: '', + rentFee: '', + contractFile: null + }); + var formData = _useState12[0]; + var setFormData = _useState12[1]; + + // 列显示配置 + var defaultColumnsVisible = { + region: true, + parkingName: true, + parkedVehicles: true, + parkingSpaces: true, + address: true, + leaseStart: true, + leaseEnd: true, + manager: true, + managerPhone: true, + contactName: true, + contactPhone: true + }; + var _useState13 = React.useState(defaultColumnsVisible); + var columnsVisible = _useState13[0]; + var setColumnsVisible = _useState13[1]; + + // 模拟省-市数据 + var provinceList = [ + { code: 'gd', name: '广东省' }, + { code: 'zj', name: '浙江省' }, + { code: 'bj', name: '北京市' } + ]; + var cityMap = { + gd: [{ code: 'gz', name: '广州市' }, { code: 'sz', name: '深圳市' }], + zj: [{ code: 'hz', name: '杭州市' }, { code: 'nb', name: '宁波市' }], + bj: [{ code: 'bj', name: '北京市' }] + }; + + // 模拟停车场列表数据 + var mockParkingList = [ + { + id: '1', + regionProvince: '广东省', + regionCity: '广州市', + parkingName: '天河智慧停车场', + parkedCount: 12, + parkingSpaces: 50, + address: '广州市天河区体育西路123号', + leaseStart: '2023-01-01', + leaseEnd: '2025-12-31', + manager: '张明', + managerPhone: '13800138001', + contactName: '李华', + contactPhone: '13900139001', + vehicles: [ + { city: '广州', frameNo: 'LGW123456', plateNo: '粤A12345', code: 'V001', brand: '比亚迪', model: '秦', color: '白色', status: '正常', mileage: 12000, location: '天河区体育西路', gpsTime: '2025-02-06 10:30:00' }, + { city: '广州', frameNo: 'LGW789012', plateNo: '粤A67890', code: 'V002', brand: '特斯拉', model: 'Model 3', color: '黑色', status: '正常', mileage: 8000, location: '天河区体育西路', gpsTime: '2025-02-06 10:28:00' } + ] + }, + { + id: '2', + regionProvince: '广东省', + regionCity: '深圳市', + parkingName: '南山科技园停车场', + parkedCount: 0, + parkingSpaces: 80, + address: '深圳市南山区科技园南路88号', + leaseStart: '2022-06-01', + leaseEnd: '2025-05-31', + manager: '王芳', + managerPhone: '13700137001', + contactName: '赵强', + contactPhone: '13600136001', + vehicles: [] + }, + { + id: '3', + regionProvince: '浙江省', + regionCity: '杭州市', + parkingName: '西湖景区停车场', + parkedCount: 5, + parkingSpaces: 30, + address: '杭州市西湖区杨公堤1号', + leaseStart: '2024-01-01', + leaseEnd: '2026-12-31', + manager: '陈静', + managerPhone: '13500135001', + contactName: '刘洋', + contactPhone: '13400134001', + vehicles: [ + { city: '杭州', frameNo: 'HZ111', plateNo: '浙A11111', code: 'V003', brand: '小鹏', model: 'P7', color: '灰色', status: '维修', mileage: 15000, location: '西湖区杨公堤', gpsTime: '2025-02-05 18:00:00' } + ] + } + ]; + + // 获取所有唯一的停车场名称和负责人 + var getAllParkingNames = function () { + var names = []; + var seen = {}; + mockParkingList.forEach(function (item) { + if (item.parkingName && !seen[item.parkingName]) { + seen[item.parkingName] = true; + names.push(item.parkingName); + } + }); + return names.sort(); + }; + var getAllManagers = function () { + var managers = []; + var seen = {}; + mockParkingList.forEach(function (item) { + if (item.manager && !seen[item.manager]) { + seen[item.manager] = true; + managers.push(item.manager); + } + }); + return managers.sort(); + }; + var allParkingNames = getAllParkingNames(); + var allManagers = getAllManagers(); + + var getFilteredList = function () { + var list = mockParkingList; + if (regionProvince && regionProvince.length > 0) { + var p = provinceList.find(function (x) { return x.code === regionProvince; }); + var pName = p ? p.name : ''; + list = list.filter(function (item) { return item.regionProvince === pName; }); + } + if (regionCity) { + var cities = cityMap[regionProvince]; + var c = cities && cities.find(function (x) { return x.code === regionCity; }); + var cName = c ? c.name : ''; + list = list.filter(function (item) { return item.regionCity === cName; }); + } + if (filterParkingName) { + list = list.filter(function (item) { + return item.parkingName && item.parkingName.indexOf(filterParkingName) >= 0; + }); + } + if (filterManager) { + list = list.filter(function (item) { + return item.manager && item.manager.indexOf(filterManager) >= 0; + }); + } + return list; + }; + + var filteredList = getFilteredList(); + var totalItems = filteredList.length; + var totalPages = Math.ceil(totalItems / pageSize) || 1; + var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage); + if (validPage !== currentPage && totalPages > 0) { + setCurrentPage(1); + validPage = 1; + } + var startIndex = (validPage - 1) * pageSize; + var endIndex = startIndex + pageSize; + var paginatedList = filteredList.slice(startIndex, endIndex); + + var handleExport = function () { + setToastMessage('导出功能为原型演示'); + setTimeout(function () { setToastMessage(''); }, 2000); + }; + + var handleDeleteConfirm = function () { + if (!deleteConfirmParking) return; + if (deleteConfirmParking.parkedCount > 0) { + setToastMessage('该停车场存在车辆,无法删除'); + setDeleteConfirmParking(null); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + setToastMessage('删除成功'); + setDeleteConfirmParking(null); + setTimeout(function () { setToastMessage(''); }, 2000); + }; + + var handleFormChange = function (field, value) { + var next = {}; + for (var k in formData) { next[k] = formData[k]; } + next[field] = value; + setFormData(next); + }; + + var handleSubmitNew = function () { + if (!formData.regionProvince || !formData.regionCity) { + setToastMessage('请选择地区'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + if (!formData.parkingName || !formData.parkingName.trim()) { + setToastMessage('请填写停车场名称'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + if (!formData.leaseStart) { + setToastMessage('请选择租赁开始时间'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + if (!formData.leaseEnd) { + setToastMessage('请选择租赁到期时间'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + if (formData.leaseEnd < formData.leaseStart) { + setToastMessage('租赁到期时间不能早于租赁开始时间'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + if (!formData.rentFee || formData.rentFee.trim() === '') { + setToastMessage('请填写租赁费'); + setTimeout(function () { setToastMessage(''); }, 2000); + return; + } + setToastMessage('提交成功'); + setTimeout(function () { setToastMessage(''); }, 1500); + setCurrentView('list'); + setFormData({ + regionProvince: '', + regionCity: '', + parkingName: '', + parkingSpaces: '', + address: '', + manager: '', + managerPhone: '', + contactName: '', + contactPhone: '', + leaseStart: '', + leaseEnd: '', + rentFee: '', + contractFile: null + }); + }; + + var handleCancelNew = function () { + setCurrentView('list'); + setFormData({ + regionProvince: '', + regionCity: '', + parkingName: '', + parkingSpaces: '', + address: '', + manager: '', + managerPhone: '', + contactName: '', + contactPhone: '', + leaseStart: '', + leaseEnd: '', + rentFee: '', + contractFile: null + }); + }; + + var t = ARCO_TOKEN; + var styles = { + page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' }, + breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, + breadcrumbLeft: { display: 'flex', alignItems: 'center' }, + breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: t.spacing8 }, + breadcrumbCurrent: { color: t.neutral8 }, + breadcrumbRight: { display: 'flex', alignItems: 'center' }, + requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' }, + card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing16 }, + filterRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between', gap: t.spacing12 }, + filterRowLeft: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: t.spacing12 }, + filterRowRight: { display: 'flex', alignItems: 'center', gap: t.spacing8, marginLeft: 'auto' }, + label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8 }, + select: { padding: '0 10px', height: '32px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, boxSizing: 'border-box' }, + input: { padding: '0 10px', height: '32px', width: '100%', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, boxSizing: 'border-box' }, + toolbar: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: t.spacing12, marginBottom: t.spacing16 }, + btn: { padding: t.spacing8 + ' ' + t.spacing16, borderRadius: t.radiusMedium, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '6px', boxSizing: 'border-box' }, + btnFixed: { width: '100px', height: '32px', padding: 0, borderRadius: '2px', lineHeight: '1' }, + btnIcon: { width: '14px', height: '14px', flexShrink: 0, display: 'block' }, + btnPrimary: { backgroundColor: t.primary, color: t.neutral1, border: 'none' }, + btnFillBlue: { backgroundColor: t.primary, color: t.neutral1, border: 'none' }, + btnOutlineBlue: { backgroundColor: t.neutral1, color: t.primary, border: '1px solid ' + t.primary }, + btnDefault: { backgroundColor: t.neutral1, color: t.neutral8, border: '1px solid ' + t.border }, + btnDanger: { backgroundColor: t.danger, color: t.neutral1, border: 'none' }, + tableWrap: { overflowX: 'auto', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.neutral4 }, + table: { width: '100%', borderCollapse: 'separate', borderSpacing: 0, fontSize: t.fontSize14 }, + th: { textAlign: 'left', padding: '12px 16px', backgroundColor: t.fillSecondary, borderBottom: '1px solid ' + t.neutral4, fontWeight: 600, color: t.neutral8, fontSize: t.fontSize14, whiteSpace: 'nowrap' }, + td: { padding: '12px 16px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8, fontSize: t.fontSize14 }, + trHover: { backgroundColor: t.fillSecondary }, + pagination: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px', borderTop: '1px solid ' + t.neutral4, backgroundColor: t.neutral1 }, + paginationLeft: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: t.fontSize14, color: t.neutral7 }, + paginationRight: { display: 'flex', alignItems: 'center', gap: '8px' }, + paginationSelect: { padding: '4px 8px', height: '28px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, backgroundColor: t.neutral1 }, + paginationBtn: { minWidth: '28px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, backgroundColor: t.neutral1, color: t.neutral8, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }, + paginationBtnActive: { backgroundColor: t.primary, color: t.neutral1, borderColor: t.primary }, + paginationBtnDisabled: { opacity: 0.5, cursor: 'not-allowed' }, + paginationInput: { width: '50px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, textAlign: 'center' }, + linkBlue: { color: t.link, cursor: 'pointer', textDecoration: 'none' }, + actionLink: { color: t.link, cursor: 'pointer', marginRight: t.spacing12, fontSize: t.fontSize14 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, maxWidth: '90%', maxHeight: '90%', overflow: 'auto', padding: t.spacing24, minWidth: '500px', position: 'relative' }, + modalTitle: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing16, color: t.neutral8 }, + modalHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: t.spacing16 }, + modalCloseBtn: { position: 'absolute', right: t.spacing16, top: t.spacing16, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', borderRadius: t.radiusMedium, backgroundColor: 'transparent', border: 'none', color: t.neutral6, fontSize: '18px', lineHeight: '1', padding: 0 }, + modalCloseBtnHover: { backgroundColor: t.fill, color: t.neutral8 }, + modalContent: { fontSize: t.fontSize14, color: t.neutral8, lineHeight: '1.6', whiteSpace: 'pre-wrap' }, + requirementSection: { marginBottom: t.spacing16 }, + requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: t.spacing8 }, + requirementItem: { marginBottom: t.spacing8, paddingLeft: t.spacing16 }, + requirementSubItem: { marginBottom: t.spacing4, paddingLeft: t.spacing16, fontSize: t.fontSize14, color: t.neutral7 }, + modalFooter: { marginTop: t.spacing24, display: 'flex', justifyContent: 'flex-end', gap: t.spacing8 }, + toast: { position: 'fixed', top: t.spacing24, left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.75)', color: t.neutral1, padding: '10px 20px', borderRadius: t.radiusMedium, zIndex: 2000, fontSize: t.fontSize14 }, + formRow: { marginBottom: t.spacing16 }, + formLabel: { display: 'block', marginBottom: '6px', fontSize: t.fontSize14, color: t.neutral8 }, + formLabelReq: { color: t.danger, marginLeft: '2px' }, + vehicleCard: { border: '1px solid ' + t.neutral4, borderRadius: '6px', padding: t.spacing12, backgroundColor: t.fillSecondary, fontSize: '13px', color: t.neutral8 }, + vehicleCardRow: { marginBottom: t.spacing4 }, + vehicleModalBox: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, maxWidth: '1200px', width: '90%', maxHeight: '80vh', overflow: 'auto', padding: t.spacing24, boxShadow: t.shadowMedium }, + vehicleModalTitle: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing16, color: t.neutral8 }, + columnSettingsBox: { position: 'absolute', right: 0, top: '100%', marginTop: t.spacing4, backgroundColor: t.neutral1, border: '1px solid ' + t.border, borderRadius: t.radiusMedium, padding: 0, minWidth: '220px', boxShadow: t.shadowMedium, zIndex: 100 }, + columnSettingsHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: t.spacing12, borderBottom: '1px solid ' + t.neutral4, fontSize: t.fontSize14, fontWeight: 600, color: t.neutral8 }, + columnSettingsBody: { padding: t.spacing12, maxHeight: '320px', overflowY: 'auto' }, + checkboxRow: { marginBottom: '6px', fontSize: t.fontSize14 }, + iconBtn: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '32px', height: '32px', padding: 0, margin: 0, backgroundColor: 'transparent', border: 'none', borderRadius: t.radiusMedium, cursor: 'pointer', color: t.neutral6, transition: 'background-color 0.2s, color 0.2s' }, + newPageLayout: { display: 'flex', gap: t.spacing24, flexWrap: 'wrap' }, + newPageForm: { flex: '2', minWidth: '400px' }, + newPageMap: { flex: '1', minWidth: '300px', height: '400px', backgroundColor: t.neutral4, borderRadius: t.radiusLarge, display: 'flex', alignItems: 'center', justifyContent: 'center', color: t.neutral6, fontSize: t.fontSize14 }, + cascaderWrap: { position: 'relative', width: '300px' }, + cascaderInput: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', height: '32px', padding: '0 10px', borderRadius: '2px', border: '1px solid ' + t.border, backgroundColor: t.neutral1, fontSize: t.fontSize14, color: t.neutral8, cursor: 'pointer', boxSizing: 'border-box' }, + cascaderInputFocus: { borderColor: t.primary, outline: 'none' }, + cascaderPlaceholder: { color: t.neutral6 }, + cascaderChevron: { marginLeft: '8px', color: t.neutral6, transition: 'transform 0.2s' }, + cascaderPanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, display: 'flex', minWidth: '280px', maxHeight: '280px' }, + cascaderColumn: { minWidth: '120px', maxHeight: '280px', overflowY: 'auto', borderRight: '1px solid ' + t.neutral4 }, + cascaderColumnLast: { borderRight: 'none' }, + cascaderOption: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral8, cursor: 'pointer', whiteSpace: 'nowrap' }, + cascaderOptionHover: { backgroundColor: t.fill }, + autocompleteWrap: { position: 'relative', width: '300px' }, + autocompletePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, minWidth: '100%', maxHeight: '240px', overflowY: 'auto' }, + autocompleteOption: { padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral8, cursor: 'pointer' }, + autocompleteOptionHover: { backgroundColor: t.fill } + }; + + return ( +

+ {/* 面包屑 */} + + + {currentView === 'list' && ( + + {/* 筛选栏 - 单独卡片 */} +
+
+
+ 地区: +
+
+ + {regionProvince && regionCity + ? (function () { + var p = provinceList.find(function (x) { return x.code === regionProvince; }); + var cities = cityMap[regionProvince]; + var c = cities && cities.find(function (x) { return x.code === regionCity; }); + return (p ? p.name : '') + (p && c ? ' - ' : '') + (c ? c.name : ''); + }()) + : '请选择省/市'} + + + + +
+ {regionCascaderOpen && ( + +
+
+
+ {provinceList.map(function (p) { + var isSelected = regionProvince === p.code; + var isHovered = regionCascaderHover === 'p-' + p.code; + return React.createElement('div', { + key: p.code, + style: Object.assign({}, styles.cascaderOption, (isSelected || isHovered) ? styles.cascaderOptionHover : {}), + onClick: function () { + setRegionProvince(p.code); + setRegionCity(''); + }, + onMouseEnter: function () { setRegionCascaderHover('p-' + p.code); }, + onMouseLeave: function () { setRegionCascaderHover(''); } + }, p.name, React.createElement('span', { style: { color: t.neutral6 } }, '>')); + })} +
+
+ {(cityMap[regionProvince] || []).map(function (c) { + var isSelected = regionCity === c.code; + var isHovered = regionCascaderHover === 'c-' + c.code; + return React.createElement('div', { + key: c.code, + style: Object.assign({}, styles.cascaderOption, (isSelected || isHovered) ? styles.cascaderOptionHover : {}), + onClick: function () { + setRegionCity(c.code); + setRegionCascaderOpen(false); + }, + onMouseEnter: function () { setRegionCascaderHover('c-' + c.code); }, + onMouseLeave: function () { setRegionCascaderHover(''); } + }, c.name); + })} +
+
+ + )} +
+ 停车场名称: +
+ + {(parkingNameInputFocused || filterParkingName) && allParkingNames.filter(function (name) { + return !filterParkingName || name.indexOf(filterParkingName) >= 0; + }).length > 0 && ( + +
+
+ {allParkingNames.filter(function (name) { + return !filterParkingName || name.indexOf(filterParkingName) >= 0; + }).map(function (name) { + var isSelected = filterParkingName === name; + return React.createElement('div', { + key: name, + style: Object.assign({}, styles.autocompleteOption, isSelected ? styles.autocompleteOptionHover : {}), + onMouseDown: function (e) { e.preventDefault(); }, + onClick: function () { + setFilterParkingName(name); + setParkingNameInputFocused(false); + }, + onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.fill; }, + onMouseLeave: function (e) { if (!isSelected) e.currentTarget.style.backgroundColor = 'transparent'; } + }, name); + })} +
+ + )} +
+ 负责人: +
+ + {(managerInputFocused || filterManager) && allManagers.filter(function (manager) { + return !filterManager || manager.indexOf(filterManager) >= 0; + }).length > 0 && ( + +
+
+ {allManagers.filter(function (manager) { + return !filterManager || manager.indexOf(filterManager) >= 0; + }).map(function (manager) { + var isSelected = filterManager === manager; + return React.createElement('div', { + key: manager, + style: Object.assign({}, styles.autocompleteOption, isSelected ? styles.autocompleteOptionHover : {}), + onMouseDown: function (e) { e.preventDefault(); }, + onClick: function () { + setFilterManager(manager); + setManagerInputFocused(false); + }, + onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.fill; }, + onMouseLeave: function (e) { if (!isSelected) e.currentTarget.style.backgroundColor = 'transparent'; } + }, manager); + })} +
+ + )} +
+
+
+ + +
+
+
+ + {/* 列表区 - 单独卡片 */} +
+
+ + +
+ + {showColumnSettings && ( + +
+
+
+ 列展示 + +
+
+ {Object.keys(columnsVisible).map(function (key) { + var labels = { + region: '地区', + parkingName: '停车场名称', + parkedVehicles: '停放车辆', + parkingSpaces: '车位数', + address: '地址', + leaseStart: '租赁开始时间', + leaseEnd: '租赁结束时间', + manager: '负责人', + managerPhone: '负责人联系方式', + contactName: '停车场联系人', + contactPhone: '停车场电话' + }; + return React.createElement( + 'div', + { key: key, style: styles.checkboxRow }, + React.createElement('label', { style: { display: 'flex', alignItems: 'center', cursor: 'pointer' }}, + React.createElement('input', { + type: 'checkbox', + checked: columnsVisible[key], + onChange: function () { + var next = {}; + for (var k in columnsVisible) { next[k] = columnsVisible[k]; } + next[key] = !columnsVisible[key]; + setColumnsVisible(next); + } + }), + React.createElement('span', { style: { marginLeft: '8px' } }, labels[key] || key) + ) + ); + })} +
+
+ + )} +
+
+ + {/* 表格 */} +
+
+ + + {columnsVisible.region && React.createElement('th', { style: styles.th }, '地区')} + {columnsVisible.parkingName && React.createElement('th', { style: styles.th }, '停车场名称')} + {columnsVisible.parkedVehicles && React.createElement('th', { style: styles.th }, '停放车辆')} + {columnsVisible.parkingSpaces && React.createElement('th', { style: styles.th }, '车位数')} + {columnsVisible.address && React.createElement('th', { style: styles.th }, '地址')} + {columnsVisible.leaseStart && React.createElement('th', { style: styles.th }, '租赁开始时间')} + {columnsVisible.leaseEnd && React.createElement('th', { style: styles.th }, '租赁结束时间')} + {columnsVisible.manager && React.createElement('th', { style: styles.th }, '负责人')} + {columnsVisible.managerPhone && React.createElement('th', { style: styles.th }, '负责人联系方式')} + {columnsVisible.contactName && React.createElement('th', { style: styles.th }, '停车场联系人')} + {columnsVisible.contactPhone && React.createElement('th', { style: styles.th }, '停车场电话')} + + + + + {paginatedList.length === 0 ? ( + React.createElement('tr', {}, + React.createElement('td', { + colSpan: Object.keys(columnsVisible).filter(function (k) { return columnsVisible[k]; }).length + 1, + style: Object.assign({}, styles.td, { textAlign: 'center', color: t.neutral6, padding: '40px' }) + }, '暂无数据') + ) + ) : paginatedList.map(function (row) { + return React.createElement( + 'tr', + { key: row.id, style: { transition: 'background-color 0.2s' }, onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.fillSecondary; }, onMouseLeave: function (e) { e.currentTarget.style.backgroundColor = 'transparent'; } }, + columnsVisible.region && React.createElement('td', { style: styles.td }, row.regionProvince + '-' + row.regionCity), + columnsVisible.parkingName && React.createElement('td', { style: styles.td }, row.parkingName), + columnsVisible.parkedVehicles && React.createElement('td', { style: styles.td }, + React.createElement('span', { + style: styles.linkBlue, + onClick: function () { + if (row.parkedCount > 0 && row.vehicles && row.vehicles.length > 0) { + setVehicleDetailParkingId(row.id); + } else { + setToastMessage('该停车场暂无车辆'); + setTimeout(function () { setToastMessage(''); }, 2000); + } + } + }, row.parkedCount) + ), + columnsVisible.parkingSpaces && React.createElement('td', { style: styles.td }, row.parkingSpaces), + columnsVisible.address && React.createElement('td', { style: styles.td }, row.address), + columnsVisible.leaseStart && React.createElement('td', { style: styles.td }, row.leaseStart), + columnsVisible.leaseEnd && React.createElement('td', { style: styles.td }, row.leaseEnd), + columnsVisible.manager && React.createElement('td', { style: styles.td }, row.manager), + columnsVisible.managerPhone && React.createElement('td', { style: styles.td }, row.managerPhone), + columnsVisible.contactName && React.createElement('td', { style: styles.td }, row.contactName), + columnsVisible.contactPhone && React.createElement('td', { style: styles.td }, row.contactPhone), + React.createElement('td', { style: styles.td }, + React.createElement('a', { + style: styles.actionLink, + onClick: function (e) { e.preventDefault(); setViewParking(row); } + }, '查看'), + React.createElement('a', { + style: styles.actionLink, + onClick: function (e) { e.preventDefault(); setEditParking(row); } + }, '编辑'), + React.createElement('a', { + style: Object.assign({}, styles.actionLink, { color: t.danger }), + onClick: function (e) { e.preventDefault(); setDeleteConfirmParking(row); } + }, '删除') + ) + ); + })} + +
操作
+ + + {/* 分页 */} + {totalItems > 0 && ( +
+
+ 共 {totalItems} 条 + 每页显示 + + +
+
+ + {(function () { + var pages = []; + var showPages = 5; + var startPage = Math.max(1, currentPage - Math.floor(showPages / 2)); + var endPage = Math.min(totalPages, startPage + showPages - 1); + if (endPage - startPage < showPages - 1) { + startPage = Math.max(1, endPage - showPages + 1); + } + if (startPage > 1) { + pages.push(React.createElement('button', { + key: 1, + style: styles.paginationBtn, + onClick: function () { setCurrentPage(1); }, + onMouseLeave: function (e) { e.currentTarget.style.borderColor = t.border; e.currentTarget.style.color = t.neutral8; } + }, '1')); + if (startPage > 2) { + pages.push(React.createElement('span', { key: 'ellipsis1', style: { padding: '0 4px', color: t.neutral7 } }, '...')); + } + } + for (var i = startPage; i <= endPage; i++) { + var isActive = i === currentPage; + pages.push(React.createElement('button', { + key: i, + style: Object.assign({}, styles.paginationBtn, isActive ? styles.paginationBtnActive : {}), + onClick: function (page) { return function () { setCurrentPage(page); }; }(i), + onMouseLeave: function (e) { if (!isActive) { e.currentTarget.style.borderColor = t.border; e.currentTarget.style.color = t.neutral8; } } + }, i)); + } + if (endPage < totalPages) { + if (endPage < totalPages - 1) { + pages.push(React.createElement('span', { key: 'ellipsis2', style: { padding: '0 4px', color: t.neutral7 } }, '...')); + } + pages.push(React.createElement('button', { + key: totalPages, + style: styles.paginationBtn, + onClick: function () { setCurrentPage(totalPages); }, + onMouseLeave: function (e) { e.currentTarget.style.borderColor = t.border; e.currentTarget.style.color = t.neutral8; } + }, totalPages)); + } + return pages; + })()} + + 跳至 + = 1 && page <= totalPages) { + setCurrentPage(page); + } + }} + onKeyDown={function (e) { + if (e.key === 'Enter') { + var page = parseInt(e.target.value, 10); + if (page >= 1 && page <= totalPages) { + setCurrentPage(page); + } + } + }} + /> + +
+
+ )} + + + + )} + + {/* 新建停车场页面 */} + {currentView === 'new' && ( +
+
新建停车场
+
+
+
+ 地区 * +
+
+ + {formData.regionProvince && formData.regionCity + ? (function () { + var p = provinceList.find(function (x) { return x.code === formData.regionProvince; }); + var cities = cityMap[formData.regionProvince]; + var c = cities && cities.find(function (x) { return x.code === formData.regionCity; }); + return (p ? p.name : '') + (p && c ? ' - ' : '') + (c ? c.name : ''); + }()) + : '请选择省/市'} + + + + +
+ {formRegionCascaderOpen && ( + +
+
+
+ {provinceList.map(function (p) { + var isSelected = formData.regionProvince === p.code; + var isHovered = formRegionCascaderHover === 'p-' + p.code; + return React.createElement('div', { + key: p.code, + style: Object.assign({}, styles.cascaderOption, (isSelected || isHovered) ? styles.cascaderOptionHover : {}), + onClick: function () { + handleFormChange('regionProvince', p.code); + handleFormChange('regionCity', ''); + }, + onMouseEnter: function () { setFormRegionCascaderHover('p-' + p.code); }, + onMouseLeave: function () { setFormRegionCascaderHover(''); } + }, p.name, React.createElement('span', { style: { color: t.neutral6 } }, '>')); + })} +
+
+ {(cityMap[formData.regionProvince] || []).map(function (c) { + var isSelected = formData.regionCity === c.code; + var isHovered = formRegionCascaderHover === 'c-' + c.code; + return React.createElement('div', { + key: c.code, + style: Object.assign({}, styles.cascaderOption, (isSelected || isHovered) ? styles.cascaderOptionHover : {}), + onClick: function () { + handleFormChange('regionCity', c.code); + setFormRegionCascaderOpen(false); + }, + onMouseEnter: function () { setFormRegionCascaderHover('c-' + c.code); }, + onMouseLeave: function () { setFormRegionCascaderHover(''); } + }, c.name); + })} +
+
+ + )} +
+
+
+ 停车场名称 * + +
+
+ 车位数 + +
+
+ 地址 + +
+
+ 负责人 + +
+
+ 负责人电话 + +
+
+ 停车场联系人 + +
+
+ 停车场电话 + +
+
+ 租赁开始时间 * + +
+
+ 租赁到期时间 * + +
+
+ 租赁费 * + + + + +
+
+ 租赁合同 + + 支持上传PDF +
+
+
+ 地图区域(输入地址可定位,拖动反写地址) + {formData.address && React.createElement('div', { style: { marginTop: '8px', fontSize: '12px' } }, '当前地址:' + formData.address)} +
+
+
+ + +
+
+ )} + + {/* 编辑弹窗 - 与新增相同可编辑 */} + {editParking && ( +
+
+
编辑停车场
+
+ 地区 * + {editParking.regionProvince}-{editParking.regionCity} +
+
+ 停车场名称 + +
+
+ 车位数 + +
+
+ 地址 + +
+
+ 负责人 / 负责人电话 + + +
+
+ 停车场联系人 / 停车场电话 + + +
+
+ 租赁开始时间 / 租赁结束时间 + {editParking.leaseStart} / {editParking.leaseEnd} +
+
+ + +
+
+
+ )} + + {/* 查看弹窗 - 与新增相同但只读 */} + {viewParking && ( +
+
+
查看停车场
+
+ 地区 + {viewParking.regionProvince}-{viewParking.regionCity} +
+
+ 停车场名称 + {viewParking.parkingName} +
+
+ 车位数 + {viewParking.parkingSpaces} +
+
+ 地址 + {viewParking.address} +
+
+ 负责人 / 负责人电话 + {viewParking.manager} / {viewParking.managerPhone} +
+
+ 停车场联系人 / 停车场电话 + {viewParking.contactName} / {viewParking.contactPhone} +
+
+ 租赁开始时间 / 租赁结束时间 + {viewParking.leaseStart} / {viewParking.leaseEnd} +
+
+ +
+
+
+ )} + + {/* 删除确认弹窗 */} + {deleteConfirmParking && ( +
+
+
确认删除
+

是否确认删除该停车场?

+
+ + +
+
+
+ )} + + {/* 车辆信息弹框 */} + {vehicleDetailParkingId && (function () { + var row = mockParkingList.find(function (r) { return r.id === vehicleDetailParkingId; }); + if (!row || !row.vehicles || row.vehicles.length === 0) { + return null; + } + return React.createElement( + 'div', + { style: styles.modalMask, onClick: function () { setVehicleDetailParkingId(null); } }, + React.createElement('div', { + style: styles.vehicleModalBox, + onClick: function (e) { e.stopPropagation(); } + }, + React.createElement('div', { style: styles.vehicleModalTitle }, row.parkingName + ' - 车辆明细'), + React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + React.createElement('th', { style: styles.th }, '运营城市'), + React.createElement('th', { style: styles.th }, '车架号'), + React.createElement('th', { style: styles.th }, '车牌号'), + React.createElement('th', { style: styles.th }, '车辆编号'), + React.createElement('th', { style: styles.th }, '品牌'), + React.createElement('th', { style: styles.th }, '型号'), + React.createElement('th', { style: styles.th }, '车身颜色'), + React.createElement('th', { style: styles.th }, '整备状态'), + React.createElement('th', { style: styles.th }, '行驶公里数'), + React.createElement('th', { style: styles.th }, '当前位置'), + React.createElement('th', { style: styles.th }, 'GPS最后上传时间') + ) + ), + React.createElement('tbody', {}, + row.vehicles.map(function (v, i) { + return React.createElement('tr', { key: i }, + React.createElement('td', { style: styles.td }, v.city), + React.createElement('td', { style: styles.td }, v.frameNo), + React.createElement('td', { style: styles.td }, v.plateNo), + React.createElement('td', { style: styles.td }, v.code), + React.createElement('td', { style: styles.td }, v.brand), + React.createElement('td', { style: styles.td }, v.model), + React.createElement('td', { style: styles.td }, v.color), + React.createElement('td', { style: styles.td }, v.status), + React.createElement('td', { style: styles.td }, v.mileage), + React.createElement('td', { style: styles.td }, v.location), + React.createElement('td', { style: styles.td }, v.gpsTime) + ); + }) + ) + ) + ), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { + style: Object.assign({}, styles.btn, styles.btnDefault), + onClick: function () { setVehicleDetailParkingId(null); } + }, '关闭') + ) + ) + ); + })()} + + {/* 需求说明弹窗 */} + {showRequirementModal && ( +
+
+ +
+
+
筛选:
+
包括地区(级联选择器,省-市)、停车场名称(选择器,支持输入框中模糊搜索)、负责人(选择器,支持输入框中模糊搜索)
+
+
+
停车场列表:
+
支持新建、导出、列设置
+
字段显示依次为地区、停车场名称、停放车辆、车位数、地址、租赁开始时间、租赁结束时间、负责人、负责人联系方式、停车场联系人、停车场联系方式
+
1. 地区字段显示精确至省-市即可
+
2. 停车场名称字段显示实际停车场名称
+
3. 停放车辆显示停车场当前停放车辆数,车辆数标注为重点色(如蓝色),点击车辆数以卡片模式展开对应车辆明细,车辆明细显示运营城市、车架号、车牌号、车辆编号、品牌、型号、车身颜色、整备状态、行驶公里数、当前位置、GPS最后上传时间
+
4. 车位数显示停车场实际车位数
+
5. 地址显示停车场实际地址
+
6. 租赁开始时间显示停车场合同实际租赁时间,显示格式为年月日
+
7. 租赁结束时间显示停车场合同实际租赁时间,显示格式为年月日
+
8. 负责人显示停车场实际负责人姓名
+
9. 负责人联系方式显示停车场实际负责人手机号
+
10. 停车场联系人显示停车场联系人姓名
+
11. 停车场电话显示停车场联系人手机号
+
12. 操作列支持查看、编辑、删除
+
查看与新增页面相同,只是所有信息输入为禁用状态
+
点击删除进行二次确认,提示"是否确认删除该停车场",同时判断停车场是否有车辆数据,如有车辆数据则提示"该停车场存在车辆,无法删除",如停车场无车辆,则toast提示删除成功
+
+
+
+
+ )} + + {toastMessage && React.createElement('div', { style: styles.toast }, toastMessage)} +
+ ); +}; +if (typeof window !== 'undefined') { + window.Component = Component; + function mount() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); + } else { + setTimeout(mount, 0); + } +} diff --git a/web端/运维管理/车辆业务/上牌管理.jsx b/web端/运维管理/车辆业务/上牌管理.jsx new file mode 100755 index 0000000..f8d612c --- /dev/null +++ b/web端/运维管理/车辆业务/上牌管理.jsx @@ -0,0 +1,938 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆上牌管理 - 车辆资产管理后台模块 +var ARCO_TOKEN = { + primary: '#165DFF', + primaryHover: '#4080FF', + danger: '#F53F3F', + success: '#00B42A', + neutral1: '#FFFFFF', + neutral2: '#F7F8FA', + neutral3: '#F2F3F5', + neutral4: '#E5E6EB', + neutral5: '#C9CDD4', + neutral6: '#86909C', + neutral7: '#4E5969', + neutral8: '#1D2129', + border: '#E5E6EB', + fill: '#F2F3F5', + fillSecondary: '#F7F8FA', + shadowLight: '0 1px 2px rgba(0,0,0,0.05)', + shadowMedium: '0 2px 8px rgba(0,0,0,0.08)', + radiusSmall: '2px', + radiusMedium: '4px', + radiusLarge: '8px', + spacing8: '8px', + spacing12: '12px', + spacing16: '16px', + spacing24: '24px', + fontSize14: '14px', + fontSize16: '16px', + fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif', + link: '#165DFF' +}; + +const Component = function () { + var antd = window.antd; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var Table = antd.Table; + var Modal = antd.Modal; + var message = antd.message; + var Option = Select.Option; + var Spin = antd.Spin; + + var _useState = React.useState(''); + var filterDateStart = _useState[0]; + var setFilterDateStart = _useState[1]; + + var _useState2 = React.useState(''); + var filterDateEnd = _useState2[0]; + var setFilterDateEnd = _useState2[1]; + + var _useState3 = React.useState(''); + var filterOperator = _useState3[0]; + var setFilterOperator = _useState3[1]; + + var _useState4 = React.useState(''); + var filterPlateNo = _useState4[0]; + var setFilterPlateNo = _useState4[1]; + + var _useState5 = React.useState(''); + var filterVin = _useState5[0]; + var setFilterVin = _useState5[1]; + + var _useState5h = React.useState(''); + var appliedDateStart = _useState5h[0]; + var setAppliedDateStart = _useState5h[1]; + var _useState5i = React.useState(''); + var appliedDateEnd = _useState5i[0]; + var setAppliedDateEnd = _useState5i[1]; + var _useState5j = React.useState(''); + var appliedOperator = _useState5j[0]; + var setAppliedOperator = _useState5j[1]; + var _useState5k = React.useState(''); + var appliedPlateNo = _useState5k[0]; + var setAppliedPlateNo = _useState5k[1]; + var _useState5l = React.useState(''); + var appliedVin = _useState5l[0]; + var setAppliedVin = _useState5l[1]; + + var _useState6 = React.useState(1); + var currentPage = _useState6[0]; + var setCurrentPage = _useState6[1]; + + var _useState7 = React.useState(10); + var pageSize = _useState7[0]; + var setPageSize = _useState7[1]; + + var _useState9 = React.useState(null); + var viewPhotoRecord = _useState9[0]; + var setViewPhotoRecord = _useState9[1]; + + var _useState10 = React.useState(false); + var ocrModalVisible = _useState10[0]; + var setOcrModalVisible = _useState10[1]; + + var _useState11 = React.useState(false); + var confirmModalVisible = _useState11[0]; + var setConfirmModalVisible = _useState11[1]; + + var _useState12 = React.useState(null); + var confirmData = _useState12[0]; + var setConfirmData = _useState12[1]; + + var _useState13 = React.useState([]); + var batchConfirmList = _useState13[0]; + var setBatchConfirmList = _useState13[1]; + + var _useState14 = React.useState(0); + var batchConfirmIndex = _useState14[0]; + var setBatchConfirmIndex = _useState14[1]; + + var _useState15 = React.useState(false); + var isBatchMode = _useState15[0]; + var setIsBatchMode = _useState15[1]; + + var _useState15b = React.useState(0); + var batchTotalCount = _useState15b[0]; + var setBatchTotalCount = _useState15b[1]; + + var _useState16 = React.useState(''); + var confirmVin = _useState16[0]; + var setConfirmVin = _useState16[1]; + + var _useState17 = React.useState(''); + var confirmPlateNo = _useState17[0]; + var setConfirmPlateNo = _useState17[1]; + + var _useState17a = React.useState(''); + var confirmScrapDate = _useState17a[0]; + var setConfirmScrapDate = _useState17a[1]; + + var _useState17b = React.useState(''); + var confirmInspectionExpiry = _useState17b[0]; + var setConfirmInspectionExpiry = _useState17b[1]; + + var _useState18b = React.useState(false); + var showRequirementModal = _useState18b[0]; + var setShowRequirementModal = _useState18b[1]; + + var _useState18c = React.useState(false); + var batchTaskCardVisible = _useState18c[0]; + var setBatchTaskCardVisible = _useState18c[1]; + + var fileInputRef = React.useRef(null); + var batchFullListRef = React.useRef(null); + var batchUploadFromCardRef = React.useRef(false); + var batchFileInputRef = React.useRef(null); + + var _useState18e = React.useState([]); + var batchTaskList = _useState18e[0]; + var setBatchTaskList = _useState18e[1]; + + var mockVehicleList = [ + { id: 'v001', frameNo: 'LGW123456', brand: '比亚迪', model: '秦', vehicleType: '轿车' }, + { id: 'v002', frameNo: 'LGW789012', brand: '特斯拉', model: 'Model 3', vehicleType: '轿车' }, + { id: 'v003', frameNo: 'HZ111222', brand: '小鹏', model: 'P7', vehicleType: '轿车' } + ]; + + var initialRecordList = [ + { + id: 'r001', + plateDate: '2025-02-01', + operator: '张明', + plateNo: '粤A12345', + vin: 'LGW123456', + vehicleType: '轿车', + brand: '比亚迪', + model: '秦', + photoUrl: 'https://picsum.photos/300/200?random=1' + }, + { + id: 'r002', + plateDate: '2025-02-03', + operator: '王芳', + plateNo: '粤A67890', + vin: 'LGW789012', + vehicleType: '轿车', + brand: '特斯拉', + model: 'Model 3', + photoUrl: 'https://picsum.photos/300/200?random=2' + } + ]; + + // 近5条批量上牌任务(时间、照片数量、完成进度);进度100%时有 items 用于「识别」进入确认界面 + var getInitialBatchTaskList = function () { + return [ + { id: 'bt1', createTime: '2025-02-12 10:30', photoCount: 3, progress: 100, items: [ + { photoUrl: 'https://picsum.photos/300/200?random=b1', vin: 'LGW123456', plateNo: '粤A11111', vehicle: mockVehicleList[0] }, + { photoUrl: 'https://picsum.photos/300/200?random=b2', vin: 'LGW789012', plateNo: '粤A22222', vehicle: mockVehicleList[1] }, + { photoUrl: 'https://picsum.photos/300/200?random=b3', vin: 'HZ111222', plateNo: '粤A33333', vehicle: mockVehicleList[2] } + ]}, + { id: 'bt2', createTime: '2025-02-12 09:15', photoCount: 5, progress: 100, items: [ + { photoUrl: 'https://picsum.photos/300/200?random=b4', vin: 'LGW123456', plateNo: '粤A44444', vehicle: mockVehicleList[0] }, + { photoUrl: 'https://picsum.photos/300/200?random=b5', vin: 'LGW789012', plateNo: '粤A55555', vehicle: mockVehicleList[1] } + ]}, + { id: 'bt3', createTime: '2025-02-11 16:20', photoCount: 4, progress: 80, items: null }, + { id: 'bt4', createTime: '2025-02-11 14:00', photoCount: 2, progress: 50, items: null }, + { id: 'bt5', createTime: '2025-02-10 11:30', photoCount: 6, progress: 30, items: null } + ]; + }; + + React.useEffect(function () { + setBatchTaskList(function (prev) { + if (prev.length === 0) return getInitialBatchTaskList(); + return prev; + }); + }, []); + + var _useState18 = React.useState(initialRecordList); + var recordList = _useState18[0]; + var setRecordList = _useState18[1]; + + var getUniqueOperators = function () { + var seen = {}; + var list = []; + recordList.forEach(function (r) { + if (r.operator && !seen[r.operator]) { + seen[r.operator] = true; + list.push(r.operator); + } + }); + return list.sort(); + }; + + var getUniquePlateNos = function () { + var seen = {}; + var list = []; + recordList.forEach(function (r) { + if (r.plateNo && !seen[r.plateNo]) { + seen[r.plateNo] = true; + list.push(r.plateNo); + } + }); + return list.sort(); + }; + + var getUniqueVins = function () { + var seen = {}; + var list = []; + recordList.forEach(function (r) { + if (r.vin && !seen[r.vin]) { + seen[r.vin] = true; + list.push(r.vin); + } + }); + mockVehicleList.forEach(function (v) { + if (v.frameNo && !seen[v.frameNo]) { + seen[v.frameNo] = true; + list.push(v.frameNo); + } + }); + return list.sort(); + }; + + var allOperators = getUniqueOperators(); + var allPlateNos = getUniquePlateNos(); + var allVins = getUniqueVins(); + + var getFilteredList = function () { + var list = recordList; + if (appliedDateStart) { + list = list.filter(function (r) { return r.plateDate >= appliedDateStart; }); + } + if (appliedDateEnd) { + list = list.filter(function (r) { return r.plateDate <= appliedDateEnd; }); + } + if (appliedOperator) { + list = list.filter(function (r) { + return r.operator && r.operator.indexOf(appliedOperator) >= 0; + }); + } + if (appliedPlateNo) { + list = list.filter(function (r) { + return r.plateNo && r.plateNo.indexOf(appliedPlateNo) >= 0; + }); + } + if (appliedVin) { + list = list.filter(function (r) { + return r.vin && r.vin.indexOf(appliedVin) >= 0; + }); + } + return list; + }; + + var filteredList = getFilteredList(); + var totalItems = filteredList.length; + var totalPages = Math.ceil(totalItems / pageSize) || 1; + var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage); + var startIndex = (validPage - 1) * pageSize; + var endIndex = startIndex + pageSize; + var paginatedList = filteredList.slice(startIndex, endIndex); + + var findVehicleByVin = function (vin) { + return mockVehicleList.find(function (v) { return v.frameNo === vin; }); + }; + + var todayStr = function () { + var d = new Date(); + var y = d.getFullYear(); + var m = (d.getMonth() + 1).toString(); + var day = d.getDate().toString(); + if (m.length === 1) { m = '0' + m; } + if (day.length === 1) { day = '0' + day; } + return y + '-' + m + '-' + day; + }; + + var sampleScrapDates = ['2035-12-31', '2030-06-15', '2028-03-20']; + var sampleInspectionExpiries = ['2026-06', '2025-12', '2027-03']; + var getSampleScrapDate = function () { return sampleScrapDates[Math.floor(Math.random() * sampleScrapDates.length)]; }; + var getSampleInspectionExpiry = function () { return sampleInspectionExpiries[Math.floor(Math.random() * sampleInspectionExpiries.length)]; }; + + var processFileAndGetItem = function (file, callback) { + var reader = new FileReader(); + reader.onload = function () { + var photoUrl = reader.result; + var mockVin = mockVehicleList[0].frameNo; + var mockPlate = '粤A' + Math.floor(Math.random() * 90000 + 10000).toString(); + var vehicle = findVehicleByVin(mockVin); + var item = { + photoUrl: photoUrl, + vin: mockVin, + plateNo: mockPlate, + vehicle: vehicle + }; + callback(item); + }; + reader.readAsDataURL(file); + }; + + var simulateOcrAndConfirm = function (items, isMulti) { + setOcrModalVisible(false); + if (isMulti && items.length > 1) { + batchFullListRef.current = items.slice(); + setBatchConfirmList(items); + setBatchConfirmIndex(0); + setBatchTotalCount(items.length); + setConfirmData(items[0]); + setConfirmVin(items[0].vin); + setConfirmPlateNo(items[0].plateNo); + setConfirmScrapDate(getSampleScrapDate()); + setConfirmInspectionExpiry(getSampleInspectionExpiry()); + setConfirmModalVisible(true); + setIsBatchMode(true); + } else if (items.length > 0) { + setConfirmData(items[0]); + setConfirmVin(items[0].vin); + setConfirmPlateNo(items[0].plateNo); + setConfirmScrapDate(getSampleScrapDate()); + setConfirmInspectionExpiry(getSampleInspectionExpiry()); + setConfirmModalVisible(true); + setIsBatchMode(false); + } + }; + + var handleUpload = function (e, isBatch) { + var files = e.target.files; + if (!files || files.length === 0) return; + // 从批量上传弹框内「上传车牌」触发:不进入识别/确认页,只在弹框中新增一条识别任务 + if (batchUploadFromCardRef.current) { + batchUploadFromCardRef.current = false; + var fileCount = files.length; + var collected = []; + var processed = 0; + var checkDone = function () { + processed = processed + 1; + if (processed === fileCount) { + var now = new Date(); + var timeStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + setBatchTaskList(function (prev) { + var next = [{ id: 'bt' + Date.now(), createTime: timeStr, photoCount: collected.length, progress: 100, items: collected }].concat(prev); + return next.slice(0, 5); + }); + } + }; + for (var i = 0; i < fileCount; i++) { + (function (idx) { + processFileAndGetItem(files[idx], function (item) { + collected.push(item); + checkDone(); + }); + })(i); + } + e.target.value = ''; + return; + } + setOcrModalVisible(true); + var fileCount = files.length; + var collected = []; + var processed = 0; + var checkDone = function () { + processed = processed + 1; + if (processed === fileCount) { + setTimeout(function () { + simulateOcrAndConfirm(collected, isBatch && collected.length > 1); + }, 1500); + } + }; + for (var i = 0; i < fileCount; i++) { + (function (idx) { + processFileAndGetItem(files[idx], function (item) { + collected.push(item); + checkDone(); + }); + })(i); + } + e.target.value = ''; + }; + + var handleConfirmSubmit = function () { + var vehicle = confirmData && confirmData.vehicle; + var newRecord = { + id: 'r' + Date.now(), + plateDate: todayStr(), + operator: '当前用户', + plateNo: confirmPlateNo, + vin: confirmVin, + vehicleType: vehicle ? vehicle.vehicleType : '轿车', + brand: vehicle ? vehicle.brand : '-', + model: vehicle ? vehicle.model : '-', + photoUrl: confirmData && confirmData.photoUrl ? confirmData.photoUrl : 'https://picsum.photos/300/200?random=' + Date.now() + }; + var nextList = recordList.slice(); + nextList.unshift(newRecord); + setRecordList(nextList); + message.success('车辆上牌成功'); + if (isBatchMode && batchConfirmList.length > 1) { + var nextBatch = batchConfirmList.slice(1); + var nextItem = nextBatch[0]; + setBatchConfirmList(nextBatch); + setBatchConfirmIndex(batchConfirmIndex + 1); + if (nextItem) { + setConfirmData(nextItem); + setConfirmVin(nextItem.vin); + setConfirmPlateNo(nextItem.plateNo); + setConfirmScrapDate(getSampleScrapDate()); + setConfirmInspectionExpiry(getSampleInspectionExpiry()); + } else { + var fullList = batchFullListRef.current; + if (fullList && fullList.length > 0) { + var now = new Date(); + var timeStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + setBatchTaskList(function (prev) { + var next = [{ id: 'bt' + Date.now(), createTime: timeStr, photoCount: fullList.length, progress: 100, items: fullList }].concat(prev); + return next.slice(0, 5); + }); + batchFullListRef.current = null; + } + setConfirmModalVisible(false); + setConfirmData(null); + setConfirmVin(''); + setConfirmPlateNo(''); + setConfirmScrapDate(''); + setConfirmInspectionExpiry(''); + setBatchConfirmList([]); + setBatchTotalCount(0); + setIsBatchMode(false); + } + } else { + setConfirmModalVisible(false); + setConfirmData(null); + setConfirmVin(''); + setConfirmPlateNo(''); + setConfirmScrapDate(''); + setConfirmInspectionExpiry(''); + setBatchConfirmList([]); + setBatchTotalCount(0); + setIsBatchMode(false); + } + }; + + var handleConfirmCancel = function () { + setConfirmModalVisible(false); + setConfirmData(null); + setConfirmVin(''); + setConfirmPlateNo(''); + setConfirmScrapDate(''); + setConfirmInspectionExpiry(''); + setBatchConfirmList([]); + setBatchConfirmIndex(0); + setBatchTotalCount(0); + setIsBatchMode(false); + }; + + var t = ARCO_TOKEN; + var styles = { + page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' }, + breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, + breadcrumbLeft: { display: 'flex', alignItems: 'center' }, + breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: t.spacing8 }, + breadcrumbCurrent: { color: t.neutral8 }, + breadcrumbRight: { display: 'flex', alignItems: 'center' }, + requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' }, + card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing16 }, + filterRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: t.spacing12 }, + filterRowRight: { display: 'flex', alignItems: 'center', gap: t.spacing8, marginLeft: 'auto' }, + label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap' }, + input: { padding: '0 10px', height: '32px', width: '180px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, boxSizing: 'border-box' }, + btn: { padding: t.spacing8 + ' ' + t.spacing16, borderRadius: t.radiusMedium, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '6px', boxSizing: 'border-box' }, + btnFixed: { width: '82px', height: '32px', padding: 0, borderRadius: '2px', lineHeight: '1' }, + btnIcon: { width: '14px', height: '14px', flexShrink: 0, display: 'block' }, + btnFillBlue: { backgroundColor: t.primary, color: t.neutral1, border: 'none' }, + btnOutlineBlue: { backgroundColor: t.neutral1, color: t.primary, border: '1px solid ' + t.primary }, + btnDefault: { backgroundColor: t.neutral1, color: t.neutral8, border: '1px solid ' + t.border }, + btnSize82: { width: '82px', height: '32px', padding: 0, lineHeight: '1', boxSizing: 'border-box' }, + toolbar: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: t.spacing12, marginBottom: t.spacing16 }, + tableWrap: { overflowX: 'auto', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.neutral4 }, + table: { width: '100%', borderCollapse: 'separate', borderSpacing: 0, fontSize: t.fontSize14 }, + th: { textAlign: 'left', padding: '12px 16px', backgroundColor: t.fillSecondary, borderBottom: '1px solid ' + t.neutral4, fontWeight: 600, color: t.neutral8, fontSize: t.fontSize14, whiteSpace: 'nowrap' }, + td: { padding: '12px 16px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8, fontSize: t.fontSize14 }, + pagination: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px', borderTop: '1px solid ' + t.neutral4, backgroundColor: t.neutral1 }, + paginationLeft: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: t.fontSize14, color: t.neutral7 }, + paginationRight: { display: 'flex', alignItems: 'center', gap: '8px' }, + paginationSelect: { padding: '4px 8px', height: '28px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, backgroundColor: t.neutral1 }, + paginationBtn: { minWidth: '28px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, backgroundColor: t.neutral1, color: t.neutral8, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }, + paginationBtnActive: { backgroundColor: t.primary, color: t.neutral1, borderColor: t.primary }, + paginationBtnDisabled: { opacity: 0.5, cursor: 'not-allowed' }, + paginationInput: { width: '50px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, textAlign: 'center' }, + actionLink: { color: t.link, cursor: 'pointer', marginRight: t.spacing12, fontSize: t.fontSize14 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, maxWidth: '90%', maxHeight: '90%', overflow: 'auto', padding: t.spacing24, minWidth: '500px', position: 'relative' }, + modalTitle: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing16, color: t.neutral8 }, + modalFooter: { marginTop: t.spacing24, display: 'flex', justifyContent: 'flex-end', gap: t.spacing8 }, + toast: { position: 'fixed', top: t.spacing24, left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.75)', color: t.neutral1, padding: '10px 20px', borderRadius: t.radiusMedium, zIndex: 2000, fontSize: t.fontSize14 }, + autocompleteWrap: { position: 'relative', width: '180px', display: 'inline-block' }, + autocompletePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, minWidth: '100%', maxHeight: '200px', overflowY: 'auto' }, + autocompleteOption: { padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral8, cursor: 'pointer' }, + autocompleteOptionHover: { backgroundColor: t.fill }, + confirmCard: { display: 'flex', gap: t.spacing24, marginTop: t.spacing16 }, + confirmPhoto: { flex: '0 0 300px', height: '200px', borderRadius: t.radiusMedium, overflow: 'hidden', backgroundColor: t.neutral3 }, + confirmPhotoImg: { width: '100%', height: '100%', objectFit: 'cover' }, + confirmForm: { flex: 1, display: 'flex', flexDirection: 'column', gap: t.spacing16 }, + formLabel: { display: 'block', marginBottom: '6px', fontSize: t.fontSize14, color: t.neutral8 }, + formInput: { padding: '8px 12px', height: '36px', borderRadius: t.radiusMedium, border: '1px solid ' + t.border, fontSize: t.fontSize14, width: '100%', boxSizing: 'border-box' }, + photoViewModal: { maxWidth: '800px', textAlign: 'center' }, + photoViewImg: { maxWidth: '100%', maxHeight: '70vh', borderRadius: t.radiusMedium }, + modalCloseBtn: { position: 'absolute', right: t.spacing16, top: t.spacing16, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', borderRadius: t.radiusMedium, backgroundColor: 'transparent', border: 'none', color: t.neutral6, fontSize: '18px', lineHeight: '1', padding: 0 }, + modalContent: { fontSize: t.fontSize14, color: t.neutral8, lineHeight: '1.6' }, + requirementSection: { marginBottom: t.spacing16 }, + requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: t.spacing8 }, + flowWrap: { marginTop: t.spacing8, marginBottom: t.spacing16 }, + flowCol: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0 }, + flowArrow: { width: '2px', height: '20px', backgroundColor: t.neutral5 }, + flowNodeOval: { padding: '8px 20px', borderRadius: '20px', border: '1px solid ' + t.neutral5, backgroundColor: t.neutral2, fontSize: t.fontSize14, color: t.neutral8 }, + flowNodeRect: { padding: '8px 20px', border: '1px solid ' + t.neutral5, backgroundColor: t.neutral1, fontSize: t.fontSize14, color: t.neutral8 }, + flowNodeDiamond: { padding: '10px 16px', border: '1px solid ' + t.neutral5, backgroundColor: t.fillSecondary, fontSize: t.fontSize14, color: t.neutral8, transform: 'rotate(0deg)', width: '140px', textAlign: 'center', boxSizing: 'border-box', clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)' }, + flowNodeDiamondWrap: { padding: '8px 16px', border: '1px solid ' + t.neutral5, backgroundColor: t.fillSecondary, fontSize: t.fontSize14, color: t.neutral8, minWidth: '120px', textAlign: 'center' }, + flowNodeToast: { padding: '8px 16px', borderRadius: '8px', border: '1px solid ' + t.neutral4, backgroundColor: t.neutral2, fontSize: t.fontSize14, color: t.neutral7 }, + flowRow: { display: 'flex', alignItems: 'flex-start', justifyContent: 'center', gap: '24px', flexWrap: 'wrap' }, + flowBranch: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0 }, + requirementItem: { marginBottom: t.spacing8, paddingLeft: t.spacing16 }, + requirementSubItem: { marginBottom: t.spacing4, paddingLeft: t.spacing16, fontSize: t.fontSize14, color: t.neutral7 }, + batchTaskCardHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: t.spacing16 }, + batchTaskTable: { width: '100%', borderCollapse: 'collapse', fontSize: t.fontSize14 }, + batchTaskTh: { textAlign: 'left', padding: '10px 12px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral7, fontWeight: 500 }, + batchTaskTd: { padding: '10px 12px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8 }, + progressWrap: { width: '120px', height: '8px', backgroundColor: t.neutral4, borderRadius: '4px', overflow: 'hidden' }, + progressBar: { height: '100%', backgroundColor: t.primary, borderRadius: '4px', transition: 'width 0.2s' }, + recognizeLink: { color: t.link, cursor: 'pointer', fontSize: t.fontSize14 }, + dateRangeWrap: { position: 'relative', display: 'inline-block' }, + dateRangeTrigger: { display: 'flex', alignItems: 'center', height: '32px', padding: '0 12px', border: '1px solid ' + t.border, borderRadius: '2px', backgroundColor: t.neutral1, cursor: 'pointer', minWidth: '280px', boxSizing: 'border-box' }, + dateRangeTriggerFocused: { borderColor: t.primary, outline: 'none' }, + dateRangeLabel: { display: 'flex', alignItems: 'center', gap: '6px', padding: '0 8px', height: '100%', fontSize: t.fontSize14, color: t.neutral8 }, + dateRangeLabelActive: { backgroundColor: 'rgba(22,93,255,0.1)', color: t.primary, borderBottom: '2px solid ' + t.primary }, + dateRangeLabelText: { whiteSpace: 'nowrap' }, + dateRangeDash: { color: t.neutral5, margin: '0 4px', fontSize: t.fontSize14 }, + dateRangeIcon: { marginLeft: 'auto', width: '16px', height: '16px', color: t.neutral6, flexShrink: 0 }, + dateRangePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, padding: '16px', display: 'flex', gap: '24px' }, + dateRangeCalendar: { width: '280px' }, + dateRangeCalendarHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px', padding: '0 4px' }, + dateRangeCalendarTitle: { fontSize: t.fontSize14, fontWeight: 500, color: t.neutral8 }, + dateRangeCalendarNav: { display: 'flex', alignItems: 'center', gap: '4px' }, + dateRangeNavBtn: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none', backgroundColor: 'transparent', color: t.neutral6, cursor: 'pointer', borderRadius: t.radiusSmall }, + dateRangeNavBtnHover: { color: t.primary, backgroundColor: t.fill }, + dateRangeWeekRow: { display: 'flex', marginBottom: '4px' }, + dateRangeWeekCell: { width: '36px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', color: t.neutral6 }, + dateRangeDayRow: { display: 'flex' }, + dateRangeDayCell: { width: '36px', height: '36px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontSize: t.fontSize14, cursor: 'pointer', borderRadius: t.radiusSmall, color: t.neutral8 }, + dateRangeDayCellOther: { color: t.neutral5 }, + dateRangeDayCellSelected: { color: t.primary }, + dateRangeDayCellInRange: { backgroundColor: 'rgba(22,93,255,0.08)', color: t.primary }, + dateRangeDayDot: { width: '4px', height: '4px', borderRadius: '50%', backgroundColor: t.primary, marginTop: '2px' } + }; + + var renderFilterSelect = function (value, setValue, options, placeholder) { + var opts = (options || []).map(function (o) { return React.createElement(Option, { key: o, value: o }, o); }); + return React.createElement(Select, { + placeholder: placeholder || '请选择或输入搜索', + style: { width: 180 }, + value: value || undefined, + onChange: function (v) { setValue(v || ''); }, + showSearch: true, + allowClear: true, + filterOption: function (input, opt) { + var c = opt && opt.children; + return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; + } + }, opts); + }; + + return React.createElement( + 'div', + { style: styles.page }, + React.createElement( + 'div', + { style: styles.breadcrumb }, + React.createElement( + 'div', + { style: styles.breadcrumbLeft }, + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('span', { style: styles.breadcrumbCurrent }, '上牌管理') + ), + React.createElement( + 'div', + { style: styles.breadcrumbRight }, + React.createElement('a', { href: '#', style: styles.requirementLink, onClick: function (e) { e.preventDefault(); setShowRequirementModal(true); } }, '查看需求说明') + ) + ), + React.createElement( + 'div', + { style: styles.card }, + React.createElement( + 'div', + { style: styles.filterRow }, + React.createElement('span', { style: styles.label }, '上牌日期:'), + React.createElement(DatePicker, { + style: { width: 180 }, + format: 'YYYY-MM-DD', + placeholder: '开始日期', + value: filterDateStart && window.moment ? window.moment(filterDateStart, 'YYYY-MM-DD') : null, + onChange: function (d, dateStr) { setFilterDateStart(dateStr || ''); } + }), + React.createElement('span', { style: { color: t.neutral6, margin: '0 4px' } }, '至'), + React.createElement(DatePicker, { + style: { width: 180 }, + format: 'YYYY-MM-DD', + placeholder: '结束日期', + value: filterDateEnd && window.moment ? window.moment(filterDateEnd, 'YYYY-MM-DD') : null, + onChange: function (d, dateStr) { setFilterDateEnd(dateStr || ''); } + }), + React.createElement('span', { style: styles.label }, '操作人:'), + renderFilterSelect(filterOperator, setFilterOperator, allOperators, '请选择操作人'), + React.createElement('span', { style: styles.label }, '车牌号:'), + renderFilterSelect(filterPlateNo, setFilterPlateNo, allPlateNos, '请选择车牌号'), + React.createElement('span', { style: styles.label }, '车辆识别代码:'), + renderFilterSelect(filterVin, setFilterVin, allVins, '请选择车辆识别代码'), + React.createElement( + 'div', + { style: styles.filterRowRight }, + React.createElement(Button, { + type: 'primary', + onClick: function () { + setAppliedDateStart(filterDateStart); + setAppliedDateEnd(filterDateEnd); + setAppliedOperator(filterOperator); + setAppliedPlateNo(filterPlateNo); + setAppliedVin(filterVin); + setCurrentPage(1); + message.success('查询成功'); + } + }, '查询'), + React.createElement(Button, { + onClick: function () { + setFilterDateStart(''); + setFilterDateEnd(''); + setAppliedDateStart(''); + setAppliedDateEnd(''); + setFilterOperator(''); + setFilterPlateNo(''); + setFilterVin(''); + setAppliedOperator(''); + setAppliedPlateNo(''); + setAppliedVin(''); + setCurrentPage(1); + } + }, '重置') + ) + ) + ), + React.createElement( + 'div', + { style: styles.card }, + React.createElement( + 'div', + { style: styles.toolbar }, + React.createElement('input', { + type: 'file', + ref: fileInputRef, + accept: 'image/*', + style: { display: 'none' }, + onChange: function (e) { handleUpload(e, false); } + }), + React.createElement('input', { + type: 'file', + ref: batchFileInputRef, + accept: 'image/*', + multiple: true, + style: { display: 'none' }, + onChange: function (e) { handleUpload(e, true); } + }), + React.createElement(Button, { + type: 'primary', + onClick: function () { if (fileInputRef.current) fileInputRef.current.click(); } + }, '新增'), + React.createElement(Button, { + onClick: function () { setBatchTaskCardVisible(true); } + }, '批量上传') + ), + React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: [ + { title: '上牌日期', dataIndex: 'plateDate', key: 'plateDate', width: 120 }, + { title: '操作人', dataIndex: 'operator', key: 'operator', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 140 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 100 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 120 }, + { title: '操作', key: 'action', width: 80, render: function (_, row) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setViewPhotoRecord(row); } }, '查看'); } } + ], + dataSource: paginatedList, + pagination: { + current: validPage, + pageSize: pageSize, + total: totalItems, + showSizeChanger: true, + showQuickJumper: true, + pageSizeOptions: ['10', '20', '50', '100'], + showTotal: function (total) { return '共 ' + total + ' 条'; }, + onChange: function (page, size) { + setCurrentPage(page); + if (size !== pageSize) setPageSize(size); + } + } + }) + ), + React.createElement(Modal, { + title: '识别中,请勿关闭页面', + visible: ocrModalVisible, + footer: null, + closable: false, + maskClosable: false, + children: React.createElement('div', { style: { padding: '24px 0', textAlign: 'center' } }, + React.createElement(Spin, { size: 'large' }), + React.createElement('p', { style: { marginTop: 12, color: t.neutral6 } }, '正在识别行驶证信息...') + ) + }), + React.createElement(Modal, { + title: isBatchMode && batchTotalCount > 1 ? '确认上牌信息(' + (batchTotalCount - batchConfirmList.length + 1) + '/' + batchTotalCount + ')' : '确认上牌信息', + visible: confirmModalVisible && !!confirmData, + onCancel: handleConfirmCancel, + onOk: handleConfirmSubmit, + okText: '确认', + cancelText: '取消', + width: 560, + children: confirmData ? React.createElement( + 'div', + { style: styles.confirmCard }, + React.createElement('div', { style: styles.confirmPhoto }, + React.createElement('img', { src: confirmData.photoUrl, alt: '行驶证', style: styles.confirmPhotoImg }) + ), + React.createElement('div', { style: styles.confirmForm }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('label', { style: styles.formLabel }, '车辆识别代号'), + React.createElement(Input, { + style: { width: '100%' }, + value: confirmVin, + onChange: function (e) { setConfirmVin(e.target.value); } + }) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('label', { style: styles.formLabel }, '车牌号'), + React.createElement(Input, { + style: { width: '100%' }, + value: confirmPlateNo, + onChange: function (e) { setConfirmPlateNo(e.target.value); } + }) + ), + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement('label', { style: styles.formLabel }, '强制报废日期'), + React.createElement(Input, { + style: { width: '100%' }, + placeholder: 'YYYY-MM-DD', + value: confirmScrapDate, + onChange: function (e) { setConfirmScrapDate(e.target.value); } + }) + ), + React.createElement('div', null, + React.createElement('label', { style: styles.formLabel }, '检验有效期'), + React.createElement(Input, { + style: { width: '100%' }, + placeholder: 'YYYY-MM', + value: confirmInspectionExpiry, + onChange: function (e) { setConfirmInspectionExpiry(e.target.value); } + }) + ) + ) + ) : null + }), + React.createElement(Modal, { + title: '行驶证照片', + visible: !!viewPhotoRecord, + footer: React.createElement(Button, { onClick: function () { setViewPhotoRecord(null); } }, '关闭'), + onCancel: function () { setViewPhotoRecord(null); }, + width: 640, + children: viewPhotoRecord ? React.createElement('img', { src: viewPhotoRecord.photoUrl, alt: '行驶证', style: Object.assign({}, styles.photoViewImg, { width: '100%' }) }) : null + }), + React.createElement(Modal, { + title: '批量上牌任务', + visible: batchTaskCardVisible, + onCancel: function () { setBatchTaskCardVisible(false); }, + footer: React.createElement(Button, { onClick: function () { setBatchTaskCardVisible(false); } }, '关闭'), + width: 560, + children: React.createElement(React.Fragment, null, + React.createElement('div', { style: Object.assign({}, styles.batchTaskCardHeader, { marginBottom: 16 }) }, + React.createElement(Button, { + type: 'primary', + onClick: function () { + batchUploadFromCardRef.current = true; + if (batchFileInputRef.current) batchFileInputRef.current.click(); + } + }, '批量上传') + ), + React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: [ + { title: '任务时间', dataIndex: 'createTime', key: 'createTime', width: 160 }, + { title: '照片数量', dataIndex: 'photoCount', key: 'photoCount', width: 100, render: function (c) { return c + ' 张'; } }, + { title: '完成进度', key: 'progress', width: 160, render: function (_, task) { + return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement('div', { style: styles.progressWrap }, + React.createElement('div', { style: Object.assign({}, styles.progressBar, { width: (task.progress || 0) + '%' }) }) + ), + React.createElement('span', { style: { fontSize: t.fontSize14, color: t.neutral7, minWidth: 36 } }, (task.progress || 0) + '%') + ); + } }, + { title: '操作', key: 'action', width: 80, render: function (_, task) { + return task.progress === 100 && task.items && task.items.length > 0 + ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { + setBatchConfirmList(task.items); + setBatchConfirmIndex(0); + setBatchTotalCount(task.items.length); + setConfirmData(task.items[0]); + setConfirmVin(task.items[0].vin); + setConfirmPlateNo(task.items[0].plateNo); + setConfirmScrapDate(getSampleScrapDate()); + setConfirmInspectionExpiry(getSampleInspectionExpiry()); + setConfirmModalVisible(true); + setIsBatchMode(true); + setBatchTaskCardVisible(false); + } }, '识别') + : React.createElement('span', { style: { color: t.neutral5, fontSize: t.fontSize14 } }, '-'); + } } + ], + dataSource: batchTaskList.slice(0, 5), + pagination: false + }) + ) + }), + React.createElement(Modal, { + title: '需求说明', + visible: showRequirementModal, + onCancel: function () { setShowRequirementModal(false); }, + footer: React.createElement(Button, { onClick: function () { setShowRequirementModal(false); } }, '关闭'), + width: 720, + children: React.createElement('div', { style: styles.modalContent }, + React.createElement('div', { style: Object.assign({}, styles.requirementSection, { marginBottom: t.spacing24 }) }, + React.createElement('div', { style: Object.assign({}, styles.requirementSectionTitle, { fontSize: '18px', marginBottom: t.spacing12 }) }, '上牌管理'), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: 0, color: t.neutral7 }) }, '用以识别行驶证正反面,识别车架号、车牌号、强制报废日期(YYYY-MM-DD)、检验有效期(YYYY-MM)') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '1.面包屑:'), + React.createElement('div', { style: styles.requirementItem }, '运维管理-车辆业务-上牌管理') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '2.筛选:'), + React.createElement('div', { style: styles.requirementSubItem }, '2.1.上牌日期:双日历日期选择器,支持选择开始-结束时间,支持手动修改日期,但检验格式及判断结束日期不得早于开始日期;'), + React.createElement('div', { style: styles.requirementSubItem }, '2.2.操作人:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择操作人;'), + React.createElement('div', { style: styles.requirementSubItem }, '2.3.车牌号:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择车牌号;'), + React.createElement('div', { style: styles.requirementSubItem }, '2.4.车辆识别代码:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择车辆识别代码;') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '3.列表:'), + React.createElement('div', { style: styles.requirementItem }, '列表右侧按钮为新增、批量上传,字段依次为上牌日期、操作人、车牌号、车辆识别代码、车辆类型、品牌、型号、操作;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.上牌日期:车辆上牌操作完成日期,格式为YYYY-MM-DD'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.操作人:车辆上牌记录操作用户姓名;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.3.车牌号:显示上牌车牌号;通过新增按钮上传行驶证照片后,通过OCR识别技术,自动识别出该车辆识别代号对应车牌号,确认无误提交后自动反写'), + React.createElement('div', { style: styles.requirementSubItem }, '3.4.车辆识别代码:显示上牌车辆车辆识别代码;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.5.车辆类型:显示该车辆识别代码对应车辆类型;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.6.品牌:显示该车辆识别代码对应品牌;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.7.型号:显示该车辆识别代码对应型号;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.8.操作:行驶证,点击放大预览行驶证照片;'), + React.createElement('div', { style: styles.requirementItem }, '下方增加分页功能,支持选择单页数据条数;') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '4.新增:'), + React.createElement('div', { style: styles.requirementSubItem }, '4.1.点击新增按钮,上传行驶证照片附件,上传后OCR识别过程中弹出卡片提示:识别中,请勿关闭页面;如果照片识别失败,则提示:识别失败,请重新尝试;'), + React.createElement('div', { style: styles.requirementSubItem }, '4.2.上传成功后确认卡片中显示照片、车辆识别代码、车牌号、强制报废日期、检验有效期,可通过二次编辑进行校正;'), + React.createElement('div', { style: styles.requirementSubItem }, '4.3.点击卡片页面底部"确认"按钮,确认根据车辆识别代码判断是否存在该车辆,如果是则toast提示"上牌成功",并在列表中生成操作,如果否则toast提示:该车辆不存在。') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '5.批量上传:'), + React.createElement('div', { style: styles.requirementItem }, '5.1.点击批量上牌按钮,弹出批量上牌任务卡片,再点击右上角上传车牌,上传多张行驶证照片附件,上传后弹出批量上牌任务卡片:卡片中记录最近5条批量上牌任务,任务字段为任务时间、照片数量、识别错误、完成进度、操作;'), + React.createElement('div', { style: styles.requirementSubItem }, '5.1.1.任务时间:上传批量照片的时间,精确至分钟,格式为YYYY-MM-DD HH:MM;'), + React.createElement('div', { style: styles.requirementSubItem }, '5.1.2.照片数量:显示这一批照片总数;'), + React.createElement('div', { style: styles.requirementSubItem }, '5.1.3.识别错误:显示识别失败的照片总数,hover时气泡卡片显示识别失败的照片名称,以;分隔;'), + React.createElement('div', { style: styles.requirementSubItem }, '5.1.4.完成进度:显示ocr识别进度条和已完成百分比;'), + React.createElement('div', { style: styles.requirementSubItem }, '5.1.5.操作:已完成任务操作栏显示识别,未完成任务操作栏显示-;'), + React.createElement('div', { style: styles.requirementItem }, '5.2.识别:点击识别确认卡片中当前张数、显示照片、车辆识别代码、车牌号、强制报废日期、检验有效期,可通过二次编辑进行校正;当前张数显示在确认上牌信息标题右侧,格式为(当前/总计),依次操作直到完成所有记录提交完成,全部完成后toast提示:批量上传成功;'), + React.createElement('div', { style: styles.requirementItem }, '5.3.在识别确认卡片中点击确认后将立刻上传该照片,可再次通过点击批量上传拉取上传任务框的方式,自动跳转至最后一条上传记录进行操作;'), + React.createElement('div', { style: styles.requirementItem }, '5.4.批量上传任务中照片全部识别完成后,不再显示识别按钮;'), + React.createElement('div', { style: styles.requirementItem }, '5.5.具体识别流程请参考axure菜单目录中上牌管理下的流程图;') + ) + ) + }) + ); +}; +if (typeof window !== 'undefined') { + window.Component = Component; + function mount() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); + } else { + setTimeout(mount, 0); + } +} diff --git a/web端/运维管理/车辆业务/交车管理.jsx b/web端/运维管理/车辆业务/交车管理.jsx new file mode 100644 index 0000000..db935ce --- /dev/null +++ b/web端/运维管理/车辆业务/交车管理.jsx @@ -0,0 +1,538 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆业务 - 交车管理(ONEOS运管平台,布局参照新增租赁合同) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var DatePicker = antd.DatePicker; + var Select = antd.Select; + var Button = antd.Button; + var Tabs = antd.Tabs; + var Table = antd.Table; + var Popover = antd.Popover; + var Cascader = antd.Cascader; + var Modal = antd.Modal; + var message = antd.message; + + var RangePicker = DatePicker.RangePicker; + + var filterState = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + deliveryRegion: undefined, + dateStart: '', + dateEnd: '', + deliveryPerson: undefined + }); + var filters = filterState[0]; + var setFilters = filterState[1]; + var appliedFilterState = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + deliveryRegion: undefined, + dateStart: '', + dateEnd: '', + deliveryPerson: undefined + }); + var appliedFilters = appliedFilterState[0]; + var setAppliedFilters = appliedFilterState[1]; + + var activeTabState = useState('pending'); + var activeTab = activeTabState[0]; + var setActiveTab = activeTabState[1]; + var pageState = useState(1); + var pageSizeState = useState(10); + var requirementModalOpen = useState(false); + var setRequirementModalOpen = requirementModalOpen[1]; + + var requirementDocContent = '交车管理\n\n1.面包屑:\n1.1.运维管理-车辆业务-交车管理\n\n2.筛选:\n2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.4.交车区域:地区选择器,支持省-市2级筛选;\n3.5.交车时间:日期选择器,默认提示信息为:请选择交车开始时间 请选择交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;\n3.6.交车人:选择器,默认为所有交车人;提示信息为:请输入或选择交车人姓名,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.7.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;\n3.8.重置:点击清空查询条件至默认;\n\n3.列表:\n分为两个tab:待处理、历史记录\n3.1.待处理:显示以下字段:\n3.1.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.1.2.任务发布时间:显示交车任务单生成时间,格式为:YYYY-MM-DD HH:MM;\n3.1.3.合同编码:显示租赁/自营合同编码;\n3.1.4.项目名称:显示租赁/自营合同项目名称;\n3.1.5.客户名称:显示租赁/自营合同客户名称;\n3.1.6.交车数量:显示交车数量,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号;\n 3.1.6.1.车辆类型:显示车辆类型;\n 3.1.6.2.品牌:显示车辆品牌;\n 3.1.6.3.型号:显示车辆型号;\n 3.1.6.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n3.1.7.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.1.8.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.1.9.操作:查看、交车;\n 3.1.8.1.交车:点击跳转交车单页面;\n\n3.2.历史记录:显示以下字段:\n3.2.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.2.2.任务发布时间:显示交车任务单生成时间,格式为:YYYY-MM-DD HH:MM;\n3.2.3.交车完成时间:显示交车单完成时间,格式为:YYYY-MM-DD HH:MM;\n3.2.4.交车人:显示交车单最终提交用户;\n3.2.5.合同编码:显示租赁/自营合同编码;\n3.2.6.项目名称:显示租赁/自营合同项目名称;\n3.2.7.客户名称:显示租赁/自营合同客户名称;\n3.2.8.交车数量:显示交车数,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、实际交车日期、交车人;\n 3.2.8.1.车辆类型:显示车辆类型;\n 3.2.8.2.品牌:显示车辆品牌;\n 3.2.8.3.型号:显示车辆型号;\n 3.2.8.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n 3.2.8.5.实际交车日期:显示实际交车日期,格式为YYYY-MM-DD,如该车还未交车则显示为-;\n 3.2.8.6.交车人:显示实际交车人用户姓名,如该车还未还车则显示为-;\n3.2.9.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.2.10.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.2.11.操作:查看;\n 3.2.11.1.查看:点击跳转查看交车单页面;'; + + // 交车区域:省-市 二级 + var regionOptions = [ + { value: 'zhejiang', label: '浙江省', children: [{ value: 'hangzhou', label: '杭州市' }, { value: 'jiaxing', label: '嘉兴市' }, { value: 'ningbo', label: '宁波市' }] }, + { value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] }, + { value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] } + ]; + + var contractCodeOptions = [ + { value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' }, + { value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' }, + { value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' } + ]; + var projectNameOptions = [ + { value: 'p1', label: '嘉兴氢能示范项目' }, + { value: 'p2', label: '上海物流租赁项目' }, + { value: 'p3', label: '杭州城配租赁项目' } + ]; + var customerNameOptions = [ + { value: 'c1', label: '嘉兴某某物流有限公司' }, + { value: 'c2', label: '上海某某运输公司' }, + { value: 'c3', label: '杭州某某租赁有限公司' } + ]; + var deliveryPersonOptions = [ + { value: '张三', label: '张三' }, + { value: '李四', label: '李四' }, + { value: '王五', label: '王五' } + ]; + + // 待处理 mock(10条样例) + var pendingListState = useState([ + { id: 'd1', expectedDate: '2025-02-28', taskPublishTime: '2025-02-20 09:00', contractCode: 'HT-ZL-2025-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '京A12345' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区科技大道1号' }, + { id: 'd2', expectedDate: '2025-03-01 至 2025-03-05', taskPublishTime: '2025-02-21 10:30', contractCode: 'HT-ZL-2025-002', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '浦东新区张江高科技园区' }, + { id: 'd3', expectedDate: '2025-02-25', taskPublishTime: '2025-02-18 14:00', contractCode: 'HT-ZL-2025-003', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, vehicleList: [{ vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '浙A10001' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '-' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '余杭区未来科技城' }, + { id: 'd4', expectedDate: '2025-03-08', taskPublishTime: '2025-02-25 08:15', contractCode: 'HT-ZL-2025-004', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1160', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市秀洲区洪兴西路288号' }, + { id: 'd5', expectedDate: '2025-03-10 至 2025-03-12', taskPublishTime: '2025-02-26 11:20', contractCode: 'HT-ZL-2025-005', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '-' }, { vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '闵行区莘庄工业区申富路669号' }, + { id: 'd6', expectedDate: '2025-03-15', taskPublishTime: '2025-03-01 09:30', contractCode: 'HT-ZL-2025-006', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 4, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '-' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '-' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '-' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '萧山区市心北路108号' }, + { id: 'd7', expectedDate: '2025-03-18', taskPublishTime: '2025-03-03 14:00', contractCode: 'HT-ZL-2025-007', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙F80088' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市经开区昌盛路1号' }, + { id: 'd8', expectedDate: '2025-03-20 至 2025-03-22', taskPublishTime: '2025-03-05 10:00', contractCode: 'HT-ZL-2025-008', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 3, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1190', plateNo: '-' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1190', plateNo: '-' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '宝山区沪太路5008号' }, + { id: 'd9', expectedDate: '2025-03-25', taskPublishTime: '2025-03-08 08:45', contractCode: 'HT-ZL-2025-009', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙A20002' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '滨江区网商路699号' }, + { id: 'd10', expectedDate: '2025-03-28', taskPublishTime: '2025-03-10 16:20', contractCode: 'HT-ZL-2025-010', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1180', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区广益路与由拳路交叉口' } + ]); + var pendingList = pendingListState[0]; + + // 历史记录 mock(10条样例,含交车完成时间、交车人,vehicleList 含实际交车日期、交车人) + var historyListState = useState([ + { id: 'h1', expectedDate: '2025-02-15', taskPublishTime: '2025-02-10 09:00', completeTime: '2025-02-15 14:30', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '京A12345', actualDate: '2025-02-15', deliveryPerson: '张三' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '京C11111', actualDate: '2025-02-15', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区科技大道1号' }, + { id: 'h2', expectedDate: '2025-02-10 至 2025-02-12', taskPublishTime: '2025-02-05 10:00', completeTime: '2025-02-12 11:00', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-002', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '沪A20002', actualDate: '2025-02-11', deliveryPerson: '王五' }], deliveryRegion: '上海市-上海市', deliveryAddress: '浦东新区张江路100号' }, + { id: 'h3', expectedDate: '2025-02-18', taskPublishTime: '2025-02-12 08:30', completeTime: '2025-02-18 15:00', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-003', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, vehicleList: [{ vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '浙A10001', actualDate: '2025-02-18', deliveryPerson: '张三' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '浙A10002', actualDate: '2025-02-18', deliveryPerson: '李四' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '浙A10003', actualDate: '2025-02-18', deliveryPerson: '王五' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '余杭区未来科技城' }, + { id: 'h4', expectedDate: '2025-02-20', taskPublishTime: '2025-02-14 11:00', completeTime: '2025-02-20 10:15', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-004', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '浙F60001', actualDate: '2025-02-20', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市秀洲区洪兴西路288号' }, + { id: 'h5', expectedDate: '2025-02-22 至 2025-02-24', taskPublishTime: '2025-02-16 09:45', completeTime: '2025-02-24 16:30', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-005', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '沪B30001', actualDate: '2025-02-23', deliveryPerson: '王五' }, { vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '沪B30002', actualDate: '2025-02-24', deliveryPerson: '张三' }], deliveryRegion: '上海市-上海市', deliveryAddress: '闵行区莘庄工业区申富路669号' }, + { id: 'h6', expectedDate: '2025-02-25', taskPublishTime: '2025-02-18 14:20', completeTime: '2025-02-25 11:00', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-006', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1160', plateNo: '浙A40001', actualDate: '2025-02-25', deliveryPerson: '张三' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '萧山区市心北路108号' }, + { id: 'h7', expectedDate: '2025-02-28', taskPublishTime: '2025-02-20 10:10', completeTime: '2025-02-28 14:45', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-007', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '浙F70001', actualDate: '2025-02-28', deliveryPerson: '李四' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙F70002', actualDate: '2025-02-28', deliveryPerson: '王五' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市经开区昌盛路1号' }, + { id: 'h8', expectedDate: '2025-03-02', taskPublishTime: '2025-02-22 08:00', completeTime: '2025-03-02 09:30', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-008', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '栏板货车', brand: '江淮', model: 'HFC1160', plateNo: '沪A50001', actualDate: '2025-03-02', deliveryPerson: '王五' }], deliveryRegion: '上海市-上海市', deliveryAddress: '宝山区沪太路5008号' }, + { id: 'h9', expectedDate: '2025-03-05 至 2025-03-07', taskPublishTime: '2025-02-25 15:30', completeTime: '2025-03-07 13:20', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-009', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1190', plateNo: '浙A60001', actualDate: '2025-03-06', deliveryPerson: '张三' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1190', plateNo: '浙A60002', actualDate: '2025-03-07', deliveryPerson: '李四' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '滨江区网商路699号' }, + { id: 'h10', expectedDate: '2025-03-10', taskPublishTime: '2025-03-01 11:00', completeTime: '2025-03-10 10:00', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-010', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '浙F90001', actualDate: '2025-03-10', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区广益路与由拳路交叉口' } + ]); + var historyList = historyListState[0]; + + var filteredPending = useMemo(function () { + var list = pendingList.slice(); + if (appliedFilters.contractCode) list = list.filter(function (r) { return (r.contractCode || '').indexOf(appliedFilters.contractCode) !== -1; }); + if (appliedFilters.projectName) list = list.filter(function (r) { return r.projectName === appliedFilters.projectName; }); + if (appliedFilters.customerName) list = list.filter(function (r) { return r.customerName === appliedFilters.customerName; }); + if (appliedFilters.deliveryRegion) list = list.filter(function (r) { return (r.deliveryRegion || '').indexOf(appliedFilters.deliveryRegion) !== -1; }); + if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.expectedDate || '').slice(0, 10); return d >= appliedFilters.dateStart; }); + if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.expectedDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); + return list; + }, [pendingList, appliedFilters]); + + var filteredHistory = useMemo(function () { + var list = historyList.slice(); + if (appliedFilters.contractCode) list = list.filter(function (r) { return (r.contractCode || '').indexOf(appliedFilters.contractCode) !== -1; }); + if (appliedFilters.projectName) list = list.filter(function (r) { return r.projectName === appliedFilters.projectName; }); + if (appliedFilters.customerName) list = list.filter(function (r) { return r.customerName === appliedFilters.customerName; }); + if (appliedFilters.deliveryRegion) list = list.filter(function (r) { return (r.deliveryRegion || '').indexOf(appliedFilters.deliveryRegion) !== -1; }); + if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.completeTime || '').slice(0, 10); return d >= appliedFilters.dateStart; }); + if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.completeTime || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); + if (appliedFilters.deliveryPerson) list = list.filter(function (r) { return (r.deliveryPerson || '').indexOf(appliedFilters.deliveryPerson) !== -1; }); + return list; + }, [historyList, appliedFilters]); + + var page = pageState[0]; + var setPage = pageState[1]; + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + var totalCount = activeTab === 'pending' ? filteredPending.length : filteredHistory.length; + var displayPending = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredPending.slice(start, start + pageSize); + }, [filteredPending, page, pageSize]); + var displayHistory = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredHistory.slice(start, start + pageSize); + }, [filteredHistory, page, pageSize]); + + var handleQuery = useCallback(function () { + var regionVal = filters.deliveryRegion; + if (Array.isArray(regionVal) && regionVal.length >= 2) { + var prov = regionOptions.find(function (r) { return r.value === regionVal[0]; }); + var city = prov && prov.children && prov.children.find(function (c) { return c.value === regionVal[1]; }); + regionVal = prov && city ? prov.label + '-' + city.label : undefined; + } else if (Array.isArray(regionVal) && regionVal.length === 1) { + var p = regionOptions.find(function (r) { return r.value === regionVal[0]; }); + regionVal = p ? p.label : undefined; + } + setAppliedFilters({ + contractCode: filters.contractCode, + projectName: filters.projectName, + customerName: filters.customerName, + deliveryRegion: regionVal, + dateStart: filters.dateStart, + dateEnd: filters.dateEnd, + deliveryPerson: filters.deliveryPerson + }); + setPage(1); + }, [filters]); + + var handleReset = useCallback(function () { + var empty = { contractCode: undefined, projectName: undefined, customerName: undefined, deliveryRegion: undefined, dateStart: '', dateEnd: '', deliveryPerson: undefined }; + setFilters(empty); + setAppliedFilters(empty); + setPage(1); + }, []); + + var handleExport = useCallback(function () { + var rows = activeTab === 'pending' ? filteredPending : filteredHistory; + if (!rows || rows.length === 0) { + message.warning('当前无数据可导出'); + return; + } + var escapeCsv = function (v) { + var s = v == null ? '' : String(v); + if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1) return '"' + s.replace(/"/g, '""') + '"'; + return s; + }; + var headers; + var rowToCells = function (r) { + if (activeTab === 'pending') { + return [r.expectedDate, r.taskPublishTime, r.contractCode, r.projectName, r.customerName, r.deliveryCount, r.deliveryRegion, r.deliveryAddress]; + } + return [r.expectedDate, r.taskPublishTime, r.completeTime, r.deliveryPerson, r.contractCode, r.projectName, r.customerName, r.deliveryCount, r.deliveryRegion, r.deliveryAddress]; + }; + if (activeTab === 'pending') { + headers = ['预计交车时间', '任务发布时间', '合同编码', '项目名称', '客户名称', '交车数量', '交车区域', '交车地点']; + } else { + headers = ['预计交车时间', '任务发布时间', '交车完成时间', '交车人', '合同编码', '项目名称', '客户名称', '交车数量', '交车区域', '交车地点']; + } + var csv = headers.map(escapeCsv).join(',') + '\n'; + rows.forEach(function (r) { + csv += rowToCells(r).map(escapeCsv).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 = '交车管理_' + (activeTab === 'pending' ? '待处理' : '历史记录') + '_' + new Date().getTime() + '.csv'; + a.click(); + URL.revokeObjectURL(url); + message.success('导出成功'); + }, [activeTab, filteredPending, filteredHistory]); + + var dateRangeValue = useMemo(function () { + if (!filters.dateStart && !filters.dateEnd) return null; + try { + if (typeof window !== 'undefined' && window.dayjs && filters.dateStart && filters.dateEnd) { + return [window.dayjs(filters.dateStart), window.dayjs(filters.dateEnd)]; + } + } catch (e) {} + return null; + }, [filters.dateStart, filters.dateEnd]); + + var onDateRangeChange = useCallback(function (dates, dateStrings) { + setFilters(function (f) { + var g = {}; for (var k in f) g[k] = f[k]; + g.dateStart = (dateStrings && dateStrings[0]) || ''; + g.dateEnd = (dateStrings && dateStrings[1]) || ''; + return g; + }); + }, []); + + // 交车区域 Cascader 的 value:用 appliedFilters 反推或存 value 数组。筛选用字符串比较,表单用 Cascader 选省-市 + var deliveryRegionValue = useMemo(function () { + var s = filters.deliveryRegion; + if (!s || typeof s !== 'string') return undefined; + var parts = s.split('-'); + if (parts.length < 2) return undefined; + for (var i = 0; i < regionOptions.length; i++) { + var prov = regionOptions[i]; + if (prov.label !== parts[0]) continue; + for (var j = 0; j < (prov.children || []).length; j++) { + if (prov.children[j].label === parts[1]) return [prov.value, prov.children[j].value]; + } + } + return undefined; + }, [filters.deliveryRegion]); + + // 列表列:待处理 + var popoverTableStyle = { width: '100%', borderCollapse: 'collapse', fontSize: 12 }; + var popoverThStyle = { padding: '6px 8px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', backgroundColor: '#fafafa', fontWeight: 600 }; + var popoverTdStyle = { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' }; + + function renderPendingQuantity(record) { + var list = record.vehicleList || []; + var content = React.createElement('div', { style: { padding: 8, minWidth: 320 } }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600 } }, '车辆明细'), + React.createElement('table', { style: popoverTableStyle }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: popoverThStyle }, '车辆类型'), + React.createElement('th', { style: popoverThStyle }, '品牌'), + React.createElement('th', { style: popoverThStyle }, '型号'), + React.createElement('th', { style: popoverThStyle }, '车牌号') + ) + ), + React.createElement('tbody', null, + list.map(function (v, i) { + return React.createElement('tr', { key: i }, + React.createElement('td', { style: popoverTdStyle }, v.vehicleType || '-'), + React.createElement('td', { style: popoverTdStyle }, v.brand || '-'), + React.createElement('td', { style: popoverTdStyle }, v.model || '-'), + React.createElement('td', { style: popoverTdStyle }, v.plateNo || '-') + ); + }) + ) + ) + ); + return React.createElement(Popover, { content: content, title: null }, + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 600 } }, record.deliveryCount + ' 辆') + ); + } + + function renderHistoryQuantity(record) { + var list = record.vehicleList || []; + var content = React.createElement('div', { style: { padding: 8, minWidth: 420 } }, + React.createElement('div', { style: { marginBottom: 8, fontWeight: 600 } }, '车辆明细'), + React.createElement('table', { style: popoverTableStyle }, + React.createElement('thead', null, + React.createElement('tr', null, + React.createElement('th', { style: popoverThStyle }, '车辆类型'), + React.createElement('th', { style: popoverThStyle }, '品牌'), + React.createElement('th', { style: popoverThStyle }, '型号'), + React.createElement('th', { style: popoverThStyle }, '车牌号'), + React.createElement('th', { style: popoverThStyle }, '实际交车日期'), + React.createElement('th', { style: popoverThStyle }, '交车人') + ) + ), + React.createElement('tbody', null, + list.map(function (v, i) { + return React.createElement('tr', { key: i }, + React.createElement('td', { style: popoverTdStyle }, v.vehicleType || '-'), + React.createElement('td', { style: popoverTdStyle }, v.brand || '-'), + React.createElement('td', { style: popoverTdStyle }, v.model || '-'), + React.createElement('td', { style: popoverTdStyle }, v.plateNo || '-'), + React.createElement('td', { style: popoverTdStyle }, v.actualDate || '-'), + React.createElement('td', { style: popoverTdStyle }, v.deliveryPerson || '-') + ); + }) + ) + ) + ); + return React.createElement(Popover, { content: content, title: null }, + React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 600 } }, record.deliveryCount + ' 辆') + ); + } + + var pendingColumns = [ + { title: '预计交车时间', dataIndex: 'expectedDate', key: 'expectedDate', width: 260, ellipsis: true }, + { title: '任务发布时间', dataIndex: 'taskPublishTime', key: 'taskPublishTime', width: 160, ellipsis: true }, + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 160, ellipsis: true }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true }, + { title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, r) { return renderPendingQuantity(r); } }, + { title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 130, ellipsis: true }, + { title: '交车地点', dataIndex: 'deliveryAddress', key: 'deliveryAddress', width: 220, ellipsis: true }, + { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转查看交车单'); } }, '查看'); + } } + ]; + + var historyColumns = [ + { title: '预计交车时间', dataIndex: 'expectedDate', key: 'expectedDate', width: 260, ellipsis: true }, + { title: '任务发布时间', dataIndex: 'taskPublishTime', key: 'taskPublishTime', width: 160, ellipsis: true }, + { title: '交车完成时间', dataIndex: 'completeTime', key: 'completeTime', width: 160, ellipsis: true }, + { title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 100, ellipsis: true }, + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 160, ellipsis: true }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true }, + { title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, r) { return renderHistoryQuantity(r); } }, + { title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 130, ellipsis: true }, + { title: '交车地点', dataIndex: 'deliveryAddress', key: 'deliveryAddress', width: 220, ellipsis: true }, + { title: '操作', key: 'action', width: 80, fixed: 'right', render: function () { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转查看交车单页面'); } }, '查看'); + } } + ]; + + var tablePagination = useMemo(function () { + return { + current: page, + pageSize: pageSize, + total: totalCount, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50'], + onChange: function (p, size) { setPage(p); if (size !== pageSize) setPageSize(size); } + }; + }, [page, pageSize, totalCount]); + + var styles = { + page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', 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' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' }, + formItem: { marginBottom: 4 }, + formLabel: { display: 'block', marginBottom: 4, color: '#333' } + }; + + var breadcrumbItems = [ + { title: '运维管理' }, + { title: '车辆业务' }, + { title: '交车管理' } + ]; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: breadcrumbItems }), + React.createElement(Button, { type: 'link', onClick: function () { setRequirementModalOpen(true); } }, '查看需求说明') + ), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.formRow }, + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '合同编码'), + React.createElement(Select, { + placeholder: '请输入或选择合同编码', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.contractCode, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.contractCode = v; return g; }); }, + style: { width: '100%' }, + options: contractCodeOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '项目名称'), + React.createElement(Select, { + placeholder: '请输入或选择项目名称', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.projectName, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.projectName = v; return g; }); }, + style: { width: '100%' }, + options: projectNameOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '客户名称'), + React.createElement(Select, { + placeholder: '请输入或选择客户名称', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.customerName, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.customerName = v; return g; }); }, + style: { width: '100%' }, + options: customerNameOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '交车区域'), + React.createElement(Cascader, { + options: regionOptions, + placeholder: '请选择省-市', + allowClear: true, + style: { width: '100%' }, + value: deliveryRegionValue, + onChange: function (value) { + var s; + if (value && value.length >= 2) { + var prov = regionOptions.find(function (r) { return r.value === value[0]; }); + var city = prov && prov.children && prov.children.find(function (c) { return c.value === value[1]; }); + s = prov && city ? prov.label + '-' + city.label : undefined; + } else { s = undefined; } + setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.deliveryRegion = s; return g; }); + }, + displayRender: function (labels) { return labels && labels.length ? labels.join(' / ') : ''; } + }) + ), + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '交车时间'), + React.createElement(RangePicker, { + style: { width: '100%' }, + placeholder: ['请选择交车开始时间', '请选择交车结束时间'], + value: dateRangeValue, + onChange: onDateRangeChange + }) + ), + React.createElement('div', null, + React.createElement('div', { style: styles.formLabel }, '交车人'), + React.createElement(Select, { + placeholder: '请输入或选择交车人姓名', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.deliveryPerson, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.deliveryPerson = v; return g; }); }, + style: { width: '100%' }, + options: deliveryPersonOptions + }) + ) + ), + 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, null, + React.createElement(Tabs, { + activeKey: activeTab, + onChange: function (k) { setActiveTab(k); setPage(1); }, + tabBarExtraContent: React.createElement(Button, { onClick: handleExport }, '导出'), + items: [ + { + key: 'pending', + label: '待处理', + children: React.createElement(Table, { + columns: pendingColumns, + dataSource: displayPending, + rowKey: 'id', + pagination: tablePagination, + scroll: { x: 1490 }, + size: 'middle' + }) + }, + { + key: 'history', + label: '历史记录', + children: React.createElement(Table, { + columns: historyColumns, + dataSource: displayHistory, + rowKey: 'id', + pagination: tablePagination, + scroll: { x: 1650 }, + size: 'middle' + }) + } + ] + }) + ), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalOpen[0], + onCancel: function () { setRequirementModalOpen(false); }, + footer: React.createElement(Button, { onClick: function () { setRequirementModalOpen(false); } }, '关闭'), + width: 640, + destroyOnClose: true + }, React.createElement('div', { style: { maxHeight: 560, overflowY: 'auto', whiteSpace: 'pre-wrap', lineHeight: 1.6, fontSize: 13 } }, requirementDocContent)) + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function () { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/运维管理/车辆业务/后装设备.jsx b/web端/运维管理/车辆业务/后装设备.jsx new file mode 100644 index 0000000..209b767 --- /dev/null +++ b/web端/运维管理/车辆业务/后装设备.jsx @@ -0,0 +1,481 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 后装设备 - 运维管理-车辆业务 +var ARCO_TOKEN = { + primary: '#165DFF', + primaryHover: '#4080FF', + danger: '#F53F3F', + success: '#00B42A', + neutral1: '#FFFFFF', + neutral2: '#F7F8FA', + neutral3: '#F2F3F5', + neutral4: '#E5E6EB', + neutral5: '#C9CDD4', + neutral6: '#86909C', + neutral7: '#4E5969', + neutral8: '#1D2129', + border: '#E5E6EB', + fill: '#F2F3F5', + fillSecondary: '#F7F8FA', + shadowLight: '0 1px 2px rgba(0,0,0,0.05)', + radiusMedium: '4px', + radiusLarge: '8px', + spacing8: '8px', + spacing12: '12px', + spacing16: '16px', + spacing24: '24px', + fontSize14: '14px', + fontSize16: '16px', + fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif', + link: '#165DFF' +}; + +var DEVICE_TYPES = ['GPS', '尾板', '车身广告', 'G7安全套件', 'G7普通设备', 'G7温控设备', '备胎']; + +var MOCK_VEHICLE_OPTIONS = ['粤A12345', '粤A67890', '粤B11111', '粤B22222', '粤C33333', '京A88888']; + +var MOCK_LIST = [ + { id: '1', deviceType: 'GPS', plateNo: '粤A12345', supplier: '深圳智联', lastOpType: '安装', createTime: '2025-02-20 10:30', removeTime: '', createOperator: '张明', removeOperator: '' }, + { id: '2', deviceType: '尾板', plateNo: '粤A67890', supplier: '广州机械', lastOpType: '拆除', createTime: '2025-02-01 09:00', removeTime: '2025-02-18 14:20', createOperator: '李强', removeOperator: '王芳' }, + { id: '3', deviceType: '车身广告', plateNo: '粤B11111', supplier: '东莞广告', lastOpType: '安装', createTime: '2025-02-15 09:00', removeTime: '', createOperator: '李强', removeOperator: '' }, + { id: '4', deviceType: 'G7安全套件', plateNo: '粤A12345', supplier: 'G7科技', lastOpType: '安装', createTime: '2025-02-10 16:45', removeTime: '', createOperator: '张明', removeOperator: '' }, + { id: '5', deviceType: '备胎', plateNo: '粤C33333', supplier: '华南轮胎', lastOpType: '拆除', createTime: '2025-01-20 08:00', removeTime: '2025-02-08 11:20', createOperator: '赵六', removeOperator: '赵六' } +]; + +const Component = function () { + var antd = window.antd; + var Select = antd.Select; + var Button = antd.Button; + var DatePicker = antd.DatePicker; + var Table = antd.Table; + var Modal = antd.Modal; + var Input = antd.Input; + var message = antd.message; + var Option = Select.Option; + var RangePicker = DatePicker.RangePicker; + var Tabs = antd.Tabs; + + var _useStateTab = React.useState('installed'); + var activeTab = _useStateTab[0]; + var setActiveTab = _useStateTab[1]; + + var _useState = React.useState(''); + var filterDeviceType = _useState[0]; + var setFilterDeviceType = _useState[1]; + + var _useState2 = React.useState(''); + var filterVehicle = _useState2[0]; + var setFilterVehicle = _useState2[1]; + + var _useState3 = React.useState([]); + var filterDateRange = _useState3[0]; + var setFilterDateRange = _useState3[1]; + + var _useState4 = React.useState({ deviceType: '', vehicle: '', dateStart: '', dateEnd: '' }); + var appliedFilter = _useState4[0]; + var setAppliedFilter = _useState4[1]; + + var _useState5 = React.useState(1); + var currentPage = _useState5[0]; + var setCurrentPage = _useState5[1]; + + var _useState6 = React.useState(10); + var pageSize = _useState6[0]; + var setPageSize = _useState6[1]; + + var _useState7 = React.useState(MOCK_LIST); + var dataList = _useState7[0]; + var setDataList = _useState7[1]; + + var _useState8 = React.useState(null); + var viewRecord = _useState8[0]; + var setViewRecord = _useState8[1]; + + var _useState9 = React.useState(null); + var editRecord = _useState9[0]; + var setEditRecord = _useState9[1]; + + var _useState10 = React.useState(null); + var removeConfirmRecord = _useState10[0]; + var setRemoveConfirmRecord = _useState10[1]; + + var _useState11 = React.useState(false); + var addModalVisible = _useState11[0]; + var setAddModalVisible = _useState11[1]; + + var _useState12 = React.useState(false); + var showRequirementModal = _useState12[0]; + var setShowRequirementModal = _useState12[1]; + + var applyBaseFilter = function (list) { + if (appliedFilter.deviceType) { + list = list.filter(function (r) { return r.deviceType === appliedFilter.deviceType; }); + } + if (appliedFilter.vehicle) { + list = list.filter(function (r) { return r.plateNo && r.plateNo.indexOf(appliedFilter.vehicle) >= 0; }); + } + return list; + }; + + var installedList = dataList.filter(function (r) { return !r.removeTime || r.removeTime === ''; }); + var removedList = dataList.filter(function (r) { return r.removeTime && r.removeTime !== ''; }); + + var installedFiltered = applyBaseFilter(installedList).filter(function (r) { + if (appliedFilter.dateStart && r.createTime) { if (r.createTime.slice(0, 10) < appliedFilter.dateStart) return false; } + if (appliedFilter.dateEnd && r.createTime) { if (r.createTime.slice(0, 10) > appliedFilter.dateEnd) return false; } + return true; + }); + var removedFiltered = applyBaseFilter(removedList).filter(function (r) { + if (appliedFilter.dateStart && r.removeTime) { if (r.removeTime.slice(0, 10) < appliedFilter.dateStart) return false; } + if (appliedFilter.dateEnd && r.removeTime) { if (r.removeTime.slice(0, 10) > appliedFilter.dateEnd) return false; } + return true; + }); + + var filteredList = activeTab === 'installed' ? installedFiltered : removedFiltered; + var totalItems = filteredList.length; + var totalPages = Math.ceil(totalItems / pageSize) || 1; + var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage); + var startIndex = (validPage - 1) * pageSize; + var paginatedList = filteredList.slice(startIndex, startIndex + pageSize); + + var handleQuery = function () { + var dateStart = ''; + var dateEnd = ''; + if (filterDateRange && filterDateRange.length >= 2 && filterDateRange[0] && filterDateRange[1]) { + if (filterDateRange[0].format) { + dateStart = filterDateRange[0].format('YYYY-MM-DD'); + dateEnd = filterDateRange[1].format('YYYY-MM-DD'); + } else if (window.dayjs) { + dateStart = window.dayjs(filterDateRange[0]).format('YYYY-MM-DD'); + dateEnd = window.dayjs(filterDateRange[1]).format('YYYY-MM-DD'); + } + } + setAppliedFilter({ + deviceType: filterDeviceType, + vehicle: filterVehicle, + dateStart: dateStart, + dateEnd: dateEnd + }); + setCurrentPage(1); + message.success('查询成功'); + }; + + var handleReset = function () { + setFilterDeviceType(''); + setFilterVehicle(''); + setFilterDateRange([]); + setAppliedFilter({ deviceType: '', vehicle: '', dateStart: '', dateEnd: '' }); + setCurrentPage(1); + }; + + var handleRemoveConfirm = function (row) { + var nowStr = '2025-02-24 15:00'; + if (window.dayjs) { nowStr = window.dayjs().format('YYYY-MM-DD HH:mm'); } + setDataList(dataList.map(function (r) { + if (r.id === row.id) { + return { id: r.id, deviceType: r.deviceType, plateNo: r.plateNo, supplier: r.supplier, lastOpType: '拆除', createTime: r.createTime, removeTime: nowStr, createOperator: r.createOperator, removeOperator: '当前用户' }; + } + return r; + })); + setRemoveConfirmRecord(null); + message.success('拆除成功'); + }; + + var t = ARCO_TOKEN; + var styles = { + page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' }, + breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, + breadcrumbLeft: { display: 'flex', alignItems: 'center' }, + breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: '8px' }, + breadcrumbCurrent: { color: t.neutral8 }, + requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' }, + modalContent: { fontSize: t.fontSize14, color: t.neutral8, lineHeight: 1.6 }, + requirementSection: { marginBottom: t.spacing16 }, + requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: 8 }, + requirementItem: { marginBottom: 8, paddingLeft: 16 }, + requirementSubItem: { marginBottom: 4, paddingLeft: 16, color: t.neutral7 }, + card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing16 }, + filterRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: t.spacing12 }, + label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap' }, + filterRight: { marginLeft: 'auto', display: 'flex', gap: t.spacing8 }, + toolbar: { display: 'flex', justifyContent: 'flex-end', gap: t.spacing12, marginBottom: t.spacing8 } + }; + + var deviceTypeOptions = DEVICE_TYPES.map(function (type) { + return React.createElement(Option, { key: type, value: type }, type); + }); + + var vehicleOptions = MOCK_VEHICLE_OPTIONS.map(function (v) { + return React.createElement(Option, { key: v, value: v }, v); + }); + + return React.createElement( + 'div', + { style: styles.page }, + React.createElement( + 'div', + { style: styles.breadcrumb }, + React.createElement('div', { style: styles.breadcrumbLeft }, + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('span', { style: styles.breadcrumbCurrent }, '后装设备') + ), + React.createElement('a', { href: '#', style: styles.requirementLink, onClick: function (e) { e.preventDefault(); setShowRequirementModal(true); } }, '查看需求说明') + ), + React.createElement( + 'div', + { style: styles.card }, + React.createElement( + 'div', + { style: styles.filterRow }, + React.createElement('span', { style: styles.label }, '设备类型:'), + React.createElement(Select, { + placeholder: '请选择设备类型', + style: { width: 160 }, + value: filterDeviceType || undefined, + onChange: function (v) { setFilterDeviceType(v || ''); }, + allowClear: true + }, deviceTypeOptions), + React.createElement('span', { style: styles.label }, '使用车辆:'), + React.createElement(Select, { + placeholder: '请选择或输入搜索', + style: { width: 180 }, + value: filterVehicle || undefined, + onChange: function (v) { setFilterVehicle(v || ''); }, + showSearch: true, + allowClear: true, + filterOption: function (input, opt) { + var c = opt && opt.children; + return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; + } + }, vehicleOptions), + React.createElement('span', { style: styles.label }, '安装时间:'), + React.createElement(RangePicker, { + style: { width: 260 }, + format: 'YYYY-MM-DD', + placeholder: ['开始日期', '结束日期'], + value: filterDateRange, + onChange: function (dates) { setFilterDateRange(dates || []); } + }), + React.createElement('div', { style: styles.filterRight }, + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { onClick: handleReset }, '重置') + ) + ) + ), + React.createElement( + 'div', + { style: styles.card }, + React.createElement( + 'div', + { style: styles.toolbar }, + activeTab === 'installed' && React.createElement(Button, { type: 'primary', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-新增后装设备'); } }, '新增'), + React.createElement(Button, { onClick: function () { message.info('根据筛选条件导出excel'); } }, '导出') + ), + React.createElement(Tabs, { + activeKey: activeTab, + onChange: function (key) { setActiveTab(key); setCurrentPage(1); }, + items: [ + { + key: 'installed', + label: '已安装', + children: React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: [ + { title: '设备类型', dataIndex: 'deviceType', key: 'deviceType', width: 120 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 }, + { title: '供应商', dataIndex: 'supplier', key: 'supplier', width: 120 }, + { title: '安装时间', dataIndex: 'createTime', key: 'createTime', width: 160 }, + { title: '安装人', dataIndex: 'createOperator', key: 'createOperator', width: 100 }, + { + title: '操作', + key: 'action', + width: 160, + render: function (_, row) { + return React.createElement(React.Fragment, null, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-查看'); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-编辑'); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { setRemoveConfirmRecord(row); } }, '拆除') + ); + } + } + ], + dataSource: paginatedList, + pagination: { + current: validPage, + pageSize: pageSize, + total: totalItems, + showSizeChanger: true, + showQuickJumper: true, + pageSizeOptions: ['10', '20', '50', '100'], + showTotal: function (total) { return '共 ' + total + ' 条'; }, + onChange: function (page, size) { + setCurrentPage(page); + if (size !== pageSize) setPageSize(size); + } + } + }) + }, + { + key: 'removed', + label: '拆除记录', + children: React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: [ + { title: '设备类型', dataIndex: 'deviceType', key: 'deviceType', width: 120 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 }, + { title: '供应商', dataIndex: 'supplier', key: 'supplier', width: 120 }, + { title: '拆除时间', dataIndex: 'removeTime', key: 'removeTime', width: 160 }, + { title: '拆除人', dataIndex: 'removeOperator', key: 'removeOperator', width: 100 }, + { + title: '操作', + key: 'action', + width: 80, + render: function (_, row) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-查看'); } }, '查看'); + } + } + ], + dataSource: paginatedList, + pagination: { + current: validPage, + pageSize: pageSize, + total: totalItems, + showSizeChanger: true, + showQuickJumper: true, + pageSizeOptions: ['10', '20', '50', '100'], + showTotal: function (total) { return '共 ' + total + ' 条'; }, + onChange: function (page, size) { + setCurrentPage(page); + if (size !== pageSize) setPageSize(size); + } + } + }) + } + ] + }) + ), + viewRecord && React.createElement(Modal, { + title: '查看后装设备', + open: !!viewRecord, + onCancel: function () { setViewRecord(null); }, + footer: React.createElement(Button, { onClick: function () { setViewRecord(null); } }, '关闭'), + width: 520, + children: viewRecord ? React.createElement('div', { style: { lineHeight: '2' } }, + React.createElement('div', null, '设备类型:', viewRecord.deviceType), + React.createElement('div', null, '车牌号:', viewRecord.plateNo), + React.createElement('div', null, '供应商:', viewRecord.supplier), + React.createElement('div', null, '安装时间:', viewRecord.createTime || '-'), + React.createElement('div', null, '安装人:', viewRecord.createOperator || '-'), + React.createElement('div', null, '拆除时间:', viewRecord.removeTime || '-'), + React.createElement('div', null, '拆除人:', viewRecord.removeOperator || '-') + ) : null + }), + editRecord && React.createElement(Modal, { + title: '编辑后装设备', + open: !!editRecord, + onCancel: function () { setEditRecord(null); }, + onOk: function () { + setEditRecord(null); + message.success('保存成功'); + }, + okText: '保存', + cancelText: '取消', + width: 520, + children: editRecord ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 16 } }, + React.createElement('div', null, React.createElement('span', { style: styles.label }, '设备类型'), React.createElement(Select, { style: { width: '100%' }, value: editRecord.deviceType, options: DEVICE_TYPES.map(function (d) { return { label: d, value: d }; }) })), + React.createElement('div', null, React.createElement('span', { style: styles.label }, '车牌号'), React.createElement(Input, { style: { width: '100%' }, value: editRecord.plateNo, readOnly: true })), + React.createElement('div', null, React.createElement('span', { style: styles.label }, '供应商'), React.createElement(Input, { style: { width: '100%' }, defaultValue: editRecord.supplier })) + ) : null + }), + removeConfirmRecord && React.createElement(Modal, { + title: '确认拆除', + open: !!removeConfirmRecord, + onCancel: function () { setRemoveConfirmRecord(null); }, + onOk: function () { handleRemoveConfirm(removeConfirmRecord); }, + okText: '确认拆除', + cancelText: '取消', + okButtonProps: { danger: true }, + children: removeConfirmRecord ? React.createElement('div', null, '确定要拆除该车辆「', removeConfirmRecord.deviceType, '」后装设备吗?') : null + }), + React.createElement(Modal, { + title: '需求说明', + open: showRequirementModal, + onCancel: function () { setShowRequirementModal(false); }, + footer: React.createElement(Button, { onClick: function () { setShowRequirementModal(false); } }, '关闭'), + width: 720, + children: React.createElement('div', { style: styles.modalContent }, + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '1. 面包屑:'), + React.createElement('div', { style: styles.requirementItem }, '1.1. 运维管理-车辆业务-后装设备') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '2. 筛选:'), + React.createElement('div', { style: styles.requirementItem }, '2.1. 支持通过设备类型、使用车辆、安装日期进行管理,点击查询后,筛选条件与列表内容联动。点击重置会回到默认筛选条件并在列表展示结果:'), + React.createElement('div', { style: styles.requirementSubItem }, '2.1.1. 设备类型:选择器,包括GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'), + React.createElement('div', { style: styles.requirementSubItem }, '2.1.2. 使用车辆:选择器,支持通过输入框对输入内容模糊搜索,并下拉对应选项;'), + React.createElement('div', { style: styles.requirementSubItem }, '2.1.3. 安装时间:日期选择器,支持单输入框内双日历选择开始-结束时间;') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '3. 列表:'), + React.createElement('div', { style: styles.requirementItem }, '列表分为2个tab,已安装/拆除记录;'), + React.createElement('div', { style: styles.requirementItem }, '当已安装列表对应车辆存在车身广告/尾板时,在备车时会自动拉取车身广告/尾板为有的状态;'), + React.createElement('div', { style: styles.requirementItem }, '当已安装列表不存在车身广告/尾板时,在备车时车身广告/尾板为无的状态;'), + React.createElement('div', { style: styles.requirementItem }, '如果在备车/交车时手动取消或勾选车身广告尾板,并完成提交,后装设备列表也会新增一条该车辆的车身广告/尾板安装数据;'), + React.createElement('div', { style: styles.requirementSectionTitle }, '3.1. 已安装:'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1. 列表展示所有后装设备信息,字段依次为:设备类型、车牌号、供应商、安装时间、安装人、操作;列表右上角为新增、导出;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.1. 设备类型:显示后装设备类型,包括:GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.2. 车牌号:显示后装设备对应车牌号信息;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.3. 供应商:显示后装设备供应商信息;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.4. 安装时间:显示后装设备安装时间,格式为YYYY-MM-DD HH:MM;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.5. 安装人:显示后装设备安装用户名称;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.6. 操作:支持查看、编辑、拆除;'), + React.createElement('div', { style: styles.requirementSectionTitle }, '3.2. 拆除记录:'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1. 列表展示所有后装设备拆除记录,字段依次为:设备类型、车牌号、供应商、拆除时间、拆除人、操作;列表右上角为新增、导出;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.1. 设备类型:显示后装设备类型,包括:GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.2. 车牌号:显示后装设备对应车牌号信息;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.3. 供应商:显示后装设备供应商信息;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.4. 拆除时间:显示后装设备拆除时间,格式为YYYY-MM-DD HH:MM;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.5. 拆除人:显示后装设备拆除用户名称;'), + React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.6. 操作:支持查看;') + ) + ) + }), + addModalVisible && React.createElement(Modal, { + title: '新增后装设备', + open: addModalVisible, + onCancel: function () { setAddModalVisible(false); }, + onOk: function () { + setDataList([{ id: String(Date.now()), deviceType: 'GPS', plateNo: '粤A12345', supplier: '新供应商', lastOpType: '安装', createTime: '2025-02-24 12:00', removeTime: '', createOperator: '当前用户', removeOperator: '' }].concat(dataList)); + setAddModalVisible(false); + message.success('新增成功'); + }, + okText: '确定', + cancelText: '取消', + width: 520, + children: React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 16 } }, + React.createElement('div', null, React.createElement('span', { style: styles.label }, '设备类型'), React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择', options: DEVICE_TYPES.map(function (d) { return { label: d, value: d }; }) })), + React.createElement('div', null, React.createElement('span', { style: styles.label }, '使用车辆'), React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择或搜索', showSearch: true, options: MOCK_VEHICLE_OPTIONS.map(function (v) { return { label: v, value: v }; }) })), + React.createElement('div', null, React.createElement('span', { style: styles.label }, '供应商'), React.createElement(Input, { style: { width: '100%' }, placeholder: '请输入供应商' })) + ) + }) + ); +}; +if (typeof window !== 'undefined') { + window.Component = Component; + function mount() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); + } else { + setTimeout(mount, 0); + } +} diff --git a/web端/运维管理/车辆业务/备车管理.jsx b/web端/运维管理/车辆业务/备车管理.jsx new file mode 100755 index 0000000..f2adfdb --- /dev/null +++ b/web端/运维管理/车辆业务/备车管理.jsx @@ -0,0 +1,652 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 备车管理 - 车辆资产管理后台(按 antd 规范) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var DatePicker = antd.DatePicker; + var Select = antd.Select; + var Button = antd.Button; + var Tabs = antd.Tabs; + var Table = antd.Table; + var Modal = antd.Modal; + var Steps = antd.Steps; + var Input = antd.Input; + var Radio = antd.Radio; + var Space = antd.Space; + var Row = antd.Row; + var Col = antd.Col; + var message = antd.message; + + var RangePicker = DatePicker.RangePicker; + + // 当前视图:'list' | 'add' + var viewState = useState('list'); + var currentView = viewState[0]; + var setCurrentView = viewState[1]; + + var filterState = useState({ + dateStart: '', + dateEnd: '', + operator: undefined, + plateNo: undefined, + vehicleType: undefined, + parkingLot: undefined + }); + var filters = filterState[0]; + var setFilters = filterState[1]; + // 实际参与列表筛选的条件(点击查询后与 filters 同步) + var appliedFilterState = useState({ + dateStart: '', + dateEnd: '', + operator: undefined, + plateNo: undefined, + vehicleType: undefined, + parkingLot: undefined + }); + var appliedFilters = appliedFilterState[0]; + var setAppliedFilters = appliedFilterState[1]; + + var activeTabState = useState('completed'); + var activeTab = activeTabState[0]; + var setActiveTab = activeTabState[1]; + + var defaultInspection = [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '正常', treadDepth: '', remark: '' }]; + var completedListState = useState([ + { id: '1', completeDate: '2025-02-10 09:30', operator: '张三', vehicleType: '厢式货车', plateNo: '京A12345', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=1', 'https://picsum.photos/200/150?random=2', 'https://picsum.photos/200/150?random=3'], tailboard: '有', hasTrailer: '有', trailerPlate: '京B67890', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=4', 'https://picsum.photos/200/150?random=5'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '异常', treadDepth: '', remark: '有划痕' }] }, + { id: '2', completeDate: '2025-02-09 10:15', operator: '李四', vehicleType: '平板货车', plateNo: '京C11111', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=6'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '异常', treadDepth: '3.1mm', remark: '需更换' }] }, + { id: '3', completeDate: '2025-02-08 11:00', operator: '王五', vehicleType: '厢式货车', plateNo: '京D22222', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=7', 'https://picsum.photos/200/150?random=8'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, + { id: '4', completeDate: '2025-02-07 13:45', operator: '赵六', vehicleType: '栏板货车', plateNo: '京E33333', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京E33334', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=9'], inspectionList: defaultInspection }, + { id: '5', completeDate: '2025-02-06 08:20', operator: '张三', vehicleType: '平板货车', plateNo: '京A23456', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M6666666', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=10'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, + { id: '6', completeDate: '2025-02-05 14:30', operator: '李四', vehicleType: '厢式货车', plateNo: '京B34567', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M5555555', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=11', 'https://picsum.photos/200/150?random=12'], inspectionList: defaultInspection }, + { id: '7', completeDate: '2025-02-04 16:00', operator: '王五', vehicleType: '栏板货车', plateNo: '京C45678', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M4444444', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=13', 'https://picsum.photos/200/150?random=14'], tailboard: '有', hasTrailer: '有', trailerPlate: '京C45679', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, + { id: '8', completeDate: '2025-02-03 09:00', operator: '赵六', vehicleType: '厢式货车', plateNo: '京D56789', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M3333333', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=15'], inspectionList: defaultInspection }, + { id: '9', completeDate: '2025-02-02 10:30', operator: '张三', vehicleType: '平板货车', plateNo: '京E67890', brand: '东风', model: 'DFH1200', vin: 'LGHXCAE28M2222222', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=16', 'https://picsum.photos/200/150?random=17', 'https://picsum.photos/200/150?random=18'], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, + { id: '10', completeDate: '2025-02-01 15:45', operator: '李四', vehicleType: '厢式货车', plateNo: '京A78901', brand: '福田', model: 'BJ1200', vin: 'LGHXCAE28M1111111', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '有', trailerPlate: '京A78902', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection } + ]); + var completedList = completedListState[0]; + var setCompletedList = completedListState[1]; + + var pendingListState = useState([ + { id: 'p1', operateDate: '2025-02-10 14:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10001', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=19', 'https://picsum.photos/200/150?random=20'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p2', operateDate: '2025-02-09 09:15', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10002', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M9990002', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=21'] }, + { id: 'p3', operateDate: '2025-02-08 11:45', operator: '张三', vehicleType: '栏板货车', plateNo: '京F10003', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M9990003', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=22'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10004', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p4', operateDate: '2025-02-07 08:00', operator: '李四', vehicleType: '厢式货车', plateNo: '京F10005', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M9990004', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p5', operateDate: '2025-02-06 13:20', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10006', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M9990005', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=23', 'https://picsum.photos/200/150?random=24', 'https://picsum.photos/200/150?random=25'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=26', 'https://picsum.photos/200/150?random=27'] }, + { id: 'p6', operateDate: '2025-02-05 15:30', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10007', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M9990006', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京F10008', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p7', operateDate: '2025-02-04 10:00', operator: '张三', vehicleType: '厢式货车', plateNo: '京F10009', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M9990007', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=28', 'https://picsum.photos/200/150?random=29'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p8', operateDate: '2025-02-03 16:45', operator: '李四', vehicleType: '栏板货车', plateNo: '京F10010', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M9990008', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=30'] }, + { id: 'p9', operateDate: '2025-02-02 09:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10011', brand: '江淮', model: 'HFC1200', vin: 'LGHXCAE28M9990009', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=31'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10012', flawPhotoCount: 0, flawPhotos: [] }, + { id: 'p10', operateDate: '2025-02-01 14:00', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10013', brand: '重汽', model: 'ZZ1200', vin: 'LGHXCAE28M9990010', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] } + ]); + var pendingList = pendingListState[0]; + var setPendingList = pendingListState[1]; + + var pageState = useState(1); + var page = pageState[0]; + var setPage = pageState[1]; + var pageSizeState = useState(10); + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + var operatorOptions = useMemo(function () { return [{ value: '张三', label: '张三' }, { value: '李四', label: '李四' }, { value: '王五', label: '王五' }, { value: '赵六', label: '赵六' }]; }, []); + var plateOptions = useMemo(function () { return [{ value: '京A12345', label: '京A12345' }, { value: '京C11111', label: '京C11111' }, { value: '京D22222', label: '京D22222' }, { value: '京E33333', label: '京E33333' }]; }, []); + var vehicleTypeOptions = useMemo(function () { return [{ value: '厢式货车', label: '厢式货车' }, { value: '平板货车', label: '平板货车' }, { value: '栏板货车', label: '栏板货车' }]; }, []); + var parkingOptions = useMemo(function () { return [{ value: '朝阳停车场', label: '朝阳停车场' }, { value: '海淀停车场', label: '海淀停车场' }, { value: '丰台停车场', label: '丰台停车场' }, { value: '西城停车场', label: '西城停车场' }]; }, []); + + var reqDocOpenState = useState(false); + var photoModalState = useState({ open: false, photos: [], currentIndex: 0 }); + + var addStepState = useState(1); + var addFormState = useState({ + plateNo: '', + vehicleType: '', + brand: '', + vin: '', + bodyAd: '无', + adPhotos: [], + tailboard: '无', + hasTrailer: '', + trailerPlate: '', + flawPhotos: [], + inspectionList: [ + { checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, + { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, + { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '', remark: '' }, + { checkType: '车身', checkItem: '车身外观', result: '正常', treadDepth: '', remark: '' } + ] + }); + + var filteredCompleted = useMemo(function () { + var list = completedList.slice(); + if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; }); + if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; }); + if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; }); + if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; }); + if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d >= appliedFilters.dateStart; }); + if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); + return list.sort(function (a, b) { return (b.completeDate || '').localeCompare(a.completeDate || ''); }); + }, [completedList, appliedFilters]); + + var filteredPending = useMemo(function () { + var list = pendingList.slice(); + if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; }); + if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; }); + if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; }); + if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; }); + if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d >= appliedFilters.dateStart; }); + if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); + return list.sort(function (a, b) { return (b.operateDate || '').localeCompare(a.operateDate || ''); }); + }, [pendingList, appliedFilters]); + + var displayCompleted = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredCompleted.slice(start, start + pageSize); + }, [filteredCompleted, page, pageSize]); + + var displayPending = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredPending.slice(start, start + pageSize); + }, [filteredPending, page, pageSize]); + + var totalCompleted = filteredCompleted.length; + var totalPending = filteredPending.length; + var totalCount = activeTab === 'completed' ? totalCompleted : totalPending; + + var handleQuery = useCallback(function () { + setAppliedFilters({ dateStart: filters.dateStart, dateEnd: filters.dateEnd, operator: filters.operator, plateNo: filters.plateNo, vehicleType: filters.vehicleType, parkingLot: filters.parkingLot }); + setPage(1); + }, [filters.dateStart, filters.dateEnd, filters.operator, filters.plateNo, filters.vehicleType, filters.parkingLot]); + var handleReset = useCallback(function () { + var empty = { dateStart: '', dateEnd: '', operator: undefined, plateNo: undefined, vehicleType: undefined, parkingLot: undefined }; + setFilters(empty); + setAppliedFilters(empty); + setPage(1); + }, []); + + var handleExport = useCallback(function () { + if (activeTab !== 'completed') { + message.warning('请在「已完成」Tab 下导出'); + return; + } + var rows = filteredCompleted; + var headers = ['备车时间', '备车人', '车辆类型', '车牌号', '品牌', '型号', '车辆识别代码', '停车场', '车身广告', '广告照片', '尾板', '是否有挂', '挂车牌号', '瑕疵照片']; + var csv = headers.join(',') + '\n'; + rows.forEach(function (r) { + csv += [r.completeDate, r.operator, r.vehicleType, r.plateNo, r.brand, r.model, r.vin, r.parkingLot, r.bodyAd, (r.adPhotoCount || 0) + '张', r.tailboard, r.hasTrailer, r.trailerPlate || '', (r.flawPhotoCount || 0) + '张'].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 = '备车管理导出_' + new Date().getTime() + '.csv'; + a.click(); + URL.revokeObjectURL(url); + message.success('导出成功'); + }, [activeTab, filteredCompleted]); + + var openPhotoModal = useCallback(function (photos, index) { + if (!photos || photos.length === 0) return; + photoModalState[1]({ open: true, photos: photos, currentIndex: index || 0 }); + }, []); + + var addForm = addFormState[0]; + var setAddForm = addFormState[1]; + var addStep = addStepState[0]; + var setAddStep = addStepState[1]; + + var handleAddSave = useCallback(function () { + var d = new Date(); + var pad = function (n) { return String(n).padStart(2, '0'); }; + var dateTimeStr = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); + var item = { + id: 'p' + Date.now(), + operateDate: dateTimeStr, + operator: '当前用户', + vehicleType: addForm.vehicleType || '厢式货车', + plateNo: addForm.plateNo, + brand: addForm.brand || '-', + model: addForm.model || '-', + vin: addForm.vin, + parkingLot: addForm.parkingLot || '朝阳停车场', + bodyAd: addForm.bodyAd, + adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0, + adPhotos: addForm.adPhotos || [], + tailboard: addForm.tailboard, + hasTrailer: addForm.trailerPlate ? '有' : '无', + trailerPlate: addForm.trailerPlate || '', + flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0, + flawPhotos: addForm.flawPhotos || [] + }; + setPendingList(pendingList.concat([item])); + setCurrentView('list'); + setActiveTab('pending'); + setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList }); + setAddStep(1); + message.success('已保存至待提交列表'); + }, [addForm, pendingList]); + + var handleAddNext = useCallback(function () { + if (!addForm.plateNo) { + message.warning('请选择车牌号'); + return; + } + setAddStep(2); + }, [addForm.plateNo]); + + var handleAddSubmit = useCallback(function () { + var d = new Date(); + var pad = function (n) { return String(n).padStart(2, '0'); }; + var dateTimeStr = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); + var item = { + id: String(Date.now()), + completeDate: dateTimeStr, + operator: '当前用户', + vehicleType: addForm.vehicleType || '厢式货车', + plateNo: addForm.plateNo, + brand: addForm.brand || '-', + model: addForm.model || '-', + vin: addForm.vin, + parkingLot: addForm.parkingLot || '朝阳停车场', + bodyAd: addForm.bodyAd, + adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0, + adPhotos: addForm.adPhotos || [], + tailboard: addForm.tailboard, + hasTrailer: addForm.trailerPlate ? '有' : '无', + trailerPlate: addForm.trailerPlate || '', + flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0, + flawPhotos: addForm.flawPhotos || [], + inspectionList: addForm.inspectionList || [] + }; + setCompletedList(completedList.concat([item])); + setCurrentView('list'); + setActiveTab('completed'); + setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList }); + setAddStep(1); + message.success('提交成功'); + }, [addForm, completedList]); + + var onPlateSelect = useCallback(function (plateNo) { + setAddForm(function (prev) { + var next = {}; + for (var k in prev) next[k] = prev[k]; + next.plateNo = plateNo; + next.vehicleType = '厢式货车'; + next.brand = '东风'; + next.vin = 'LGHXCAE28M' + Math.floor(Math.random() * 10000000); + return next; + }); + }, []); + + var requirementDocContent = '#.面包屑:运维管理-车辆业务-备车管理,支持点击对应模块跳转\n\n#筛选:支持通过备车日期、备车人、车牌号、车辆类型、停车场等方式进行筛选;\n1.备车日期:日期选择器,点击显示两个日历控件,支持选择开始和结束时间,时间格式为YYYY-MM-DD;\n2.备车人:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n3.车牌号:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n4.车辆类型:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n5.停车场:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n6.右侧显示查询、重置按钮,点击查询按钮按筛选条件显示列表内容,点击重置清除筛选条件并按默认条件显示列表内容;\n\n#列表:\n列表分为两个tab,分别为:已完成、待检查;已完成列表右侧包含新增、导出按钮,下方增加分页功能,支持选择单页显示数据记录条数;\n1.已完成tab显示完成车辆记录及备车检查两个步骤表单并点击提交的记录;\n2.待检查tab显示新增备车时只点击保存,但未提交的记录;\n3.点击新增按钮,打开新页面进行新增表单页;\n4.导出需全选/多选对应数据记录,再点击导出下载csv;\n5.列表以区域进行数据权限划分,用户表中对应区域的用户只能查询自己区域的数据;'; + + // 日期范围变化(antd RangePicker 返回 dayjs 或 [string, string] 依项目而定,此处用字符串存) + function onDateRangeChange(dates, dateStrings) { + setFilters(function (f) { + var g = {}; + for (var k in f) g[k] = f[k]; + g.dateStart = (dateStrings && dateStrings[0]) || ''; + g.dateEnd = (dateStrings && dateStrings[1]) || ''; + return g; + }); + } + + var dateRangeValue = useMemo(function () { + if (!filters.dateStart && !filters.dateEnd) return null; + try { + if (typeof window.dayjs === 'function' && filters.dateStart && filters.dateEnd) { + return [window.dayjs(filters.dateStart), window.dayjs(filters.dateEnd)]; + } + } catch (e) {} + return null; + }, [filters.dateStart, filters.dateEnd]); + + // 已完成表格列(含多选、照片链接、操作列查看按钮,点击不打开新页面) + function getCompletedColumns(openPhoto) { + return [ + { title: '备车时间', dataIndex: 'completeDate', key: 'completeDate', width: 160 }, + { title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 }, + { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 }, + { title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 }, + { title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) { + var photos = r.adPhotos && r.adPhotos.length > 0 ? r.adPhotos : []; + return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; + }}, + { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 }, + { title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 }, + { title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } }, + { title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) { + var photos = r.flawPhotos && r.flawPhotos.length > 0 ? r.flawPhotos : []; + return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; + }}, + { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () {} }, '查看'); + }} + ]; + } + + function getPendingColumns(openPhoto, onEdit) { + return [ + { title: '创建时间', dataIndex: 'operateDate', key: 'operateDate', width: 160 }, + { title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 }, + { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 }, + { title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 }, + { title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) { + var photos = (r.adPhotos && r.adPhotos.length > 0) ? r.adPhotos : []; + return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; + }}, + { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 }, + { title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 }, + { title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } }, + { title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) { + var photos = (r.flawPhotos && r.flawPhotos.length > 0) ? r.flawPhotos : []; + return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; + }}, + { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { + setAddForm(function (prev) { + var n = {}; + for (var k in prev) n[k] = prev[k]; + n.plateNo = r.plateNo; n.vehicleType = r.vehicleType; n.brand = r.brand; n.vin = r.vin; + n.bodyAd = r.bodyAd; n.adPhotos = r.adPhotos || []; n.tailboard = r.tailboard; + n.trailerPlate = r.trailerPlate || ''; n.flawPhotos = r.flawPhotos || []; + return n; + }); + setAddStep(1); + setCurrentView('add'); + } }, '编辑'); + }} + ]; + } + + var tablePagination = useMemo(function () { + return { + current: page, + pageSize: pageSize, + total: totalCount, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + pageSizeOptions: ['10', '20', '50'], + onChange: function (p, size) { + setPage(p); + if (size !== pageSize) setPageSize(size); + } + }; + }, [page, pageSize, totalCount]); + + // —————— 列表页 —————— + function ListView() { + var breadcrumbItems = [ + { title: '运维管理' }, + { title: '车辆业务' }, + { title: '备车管理' } + ]; + return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: breadcrumbItems }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明') + ), + React.createElement(Modal, { + title: '需求说明', + open: reqDocOpenState[0], + onCancel: function () { reqDocOpenState[1](false); }, + footer: null, + width: 720, + styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14 } } + }, requirementDocContent.split('\n').map(function (line, idx) { + var trimmed = line; + if (trimmed.indexOf('#') === 0) { + return React.createElement('div', { key: idx, style: { fontWeight: 'bold', marginTop: idx === 0 ? 0 : 16, marginBottom: 8, paddingBottom: 4, borderBottom: '1px solid #f0f0f0' } }, trimmed.replace(/^#\.?/, '').trim()); + } + if (/^\d+\./.test(trimmed)) return React.createElement('div', { key: idx, style: { marginLeft: 12, marginBottom: 4 } }, trimmed); + if (trimmed === '') return React.createElement('div', { key: idx, style: { height: 12 } }); + return React.createElement('div', { key: idx, style: { marginBottom: 8 } }, trimmed); + })), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + flex: 1, + minWidth: 0 + } + }, + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '备车日期'), + React.createElement(RangePicker, { + value: dateRangeValue, + onChange: onDateRangeChange, + style: { width: '100%' }, + placeholder: ['开始日期', '结束日期'] + }) + ), + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '备车人'), + React.createElement(Select, { + placeholder: '请选择备车人', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.operator, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.operator = v; return g; }); }, + style: { width: '100%' }, + options: operatorOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '车牌号'), + React.createElement(Select, { + placeholder: '请选择车牌号', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.plateNo, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.plateNo = v; return g; }); }, + style: { width: '100%' }, + options: plateOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '车辆类型'), + React.createElement(Select, { + placeholder: '请选择车辆类型', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.vehicleType, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.vehicleType = v; return g; }); }, + style: { width: '100%' }, + options: vehicleTypeOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '停车场'), + React.createElement(Select, { + placeholder: '请选择停车场', + allowClear: true, + showSearch: true, + optionFilterProp: 'label', + value: filters.parkingLot, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.parkingLot = v; return g; }); }, + style: { width: '100%' }, + options: parkingOptions + }) + ) + ), + 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, null, + React.createElement(Tabs, { + activeKey: activeTab, + onChange: function (k) { setActiveTab(k); setPage(1); }, + tabBarExtraContent: React.createElement('div', { style: { display: 'flex', gap: 8 } }, + React.createElement(Button, { type: 'primary', onClick: function () {} }, '新增'), + activeTab === 'completed' ? React.createElement(Button, { onClick: handleExport }, '导出') : null + ), + items: [ + { + key: 'completed', + label: '已完成', + children: React.createElement(Table, { + columns: getCompletedColumns(openPhotoModal), + dataSource: displayCompleted, + rowKey: 'id', + pagination: tablePagination, + scroll: { x: 1400 }, + size: 'middle' + }) + }, + { + key: 'pending', + label: '待提交', + children: React.createElement(Table, { + columns: getPendingColumns(openPhotoModal), + dataSource: displayPending, + rowKey: 'id', + pagination: tablePagination, + scroll: { x: 1400 }, + size: 'middle' + }) + } + ] + }) + ), + // 照片预览:全屏遮罩 + 放大图片,点击遮罩关闭 + photoModalState[0].open && photoModalState[0].photos && photoModalState[0].photos.length > 0 ? React.createElement('div', { + style: { + position: 'fixed', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'rgba(0,0,0,0.75)', + zIndex: 1000, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer' + }, + onClick: function () { photoModalState[1]({ open: false, photos: [], currentIndex: 0 }); } + }, React.createElement('div', { + style: { position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', cursor: 'default', maxWidth: '100%', maxHeight: '100%' }, + onClick: function (e) { if (e) e.stopPropagation(); } + }, + React.createElement('img', { + src: photoModalState[0].photos[photoModalState[0].currentIndex] || photoModalState[0].photos[0], + alt: '', + style: { maxWidth: '90vw', maxHeight: '85vh', objectFit: 'contain' } + }), + photoModalState[0].photos.length > 1 ? React.createElement('div', { style: { marginTop: 16, display: 'flex', alignItems: 'center', gap: 16 } }, + React.createElement(Button, { + disabled: photoModalState[0].currentIndex === 0, + onClick: function (e) { e.stopPropagation(); photoModalState[1]({ open: true, photos: photoModalState[0].photos, currentIndex: Math.max(0, photoModalState[0].currentIndex - 1) }); } + }, '上一张'), + React.createElement('span', { style: { color: '#fff' } }, (photoModalState[0].currentIndex + 1) + ' / ' + photoModalState[0].photos.length), + React.createElement(Button, { + disabled: photoModalState[0].currentIndex >= photoModalState[0].photos.length - 1, + onClick: function (e) { e.stopPropagation(); photoModalState[1]({ open: true, photos: photoModalState[0].photos, currentIndex: Math.min(photoModalState[0].photos.length - 1, photoModalState[0].currentIndex + 1) }); } + }, '下一张') + ) : null + )) : null + ); + } + + // —————— 新增页 —————— + function AddView() { + var form = addFormState[0]; + var setForm = addFormState[1]; + var step = addStepState[0]; + var addBreadcrumbItems = [ + { title: '运维管理' }, + { title: '车辆业务' }, + { title: '备车管理' }, + { title: '新增备车' } + ]; + return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } }, + React.createElement(Breadcrumb, { items: addBreadcrumbItems, style: { marginBottom: 16 } }), + React.createElement(Card, null, + React.createElement(Steps, { current: step - 1, style: { marginBottom: 24 }, items: [ + { title: '步骤1 车辆记录' }, + { title: '步骤2 备车检查' } + ] }), + step === 1 ? React.createElement(Row, { gutter: [16, 16] }, + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车牌号 *'), React.createElement(Select, { placeholder: '请选择车牌号(仅库存车辆)', allowClear: true, showSearch: true, optionFilterProp: 'label', value: form.plateNo || undefined, onChange: onPlateSelect, style: { width: 200 }, options: plateOptions })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车辆类型'), React.createElement(Input, { value: form.vehicleType, readOnly: true, style: { width: 200, background: '#f5f5f5' } })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '品牌'), React.createElement(Input, { value: form.brand, readOnly: true, style: { width: 200, background: '#f5f5f5' } })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车架号'), React.createElement(Input, { value: form.vin, readOnly: true, style: { width: 200, background: '#f5f5f5' } })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车身广告 *'), React.createElement(Radio.Group, { value: form.bodyAd, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.bodyAd = e.target.value; return n; }); }, options: [{ value: '无', label: '无' }, { value: '有', label: '有' }] })), + form.bodyAd === '有' ? React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '广告照片(最多5张)'), React.createElement(Space, null, (form.adPhotos || []).slice(0, 5).map(function (url, i) { return React.createElement('img', { key: i, src: url, alt: '', style: { width: 60, height: 60, objectFit: 'cover', borderRadius: 4 } }); }), (form.adPhotos || []).length < 5 ? React.createElement(Button, { onClick: function () { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = (p.adPhotos || []).concat(['https://picsum.photos/200/150?r=' + Date.now()]); return n; }); } }, '上传') : null)) : null, + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '尾板 *'), React.createElement(Radio.Group, { value: form.tailboard, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = e.target.value; return n; }); }, options: [{ value: '无', label: '无' }, { value: '有', label: '有' }] })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '挂车牌号(有挂时填写)'), React.createElement(Input, { value: form.trailerPlate, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.trailerPlate = e.target.value; return n; }); }, placeholder: '不填视为无挂', style: { width: 200 } })), + React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '瑕疵照片(最多5张)'), React.createElement(Space, null, (form.flawPhotos || []).slice(0, 5).map(function (url, i) { return React.createElement('img', { key: i, src: url, alt: '', style: { width: 60, height: 60, objectFit: 'cover', borderRadius: 4 } }); }), (form.flawPhotos || []).length < 5 ? React.createElement(Button, { onClick: function () { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.flawPhotos = (p.flawPhotos || []).concat(['https://picsum.photos/200/150?f=' + Date.now()]); return n; }); } }, '上传') : null)), + React.createElement(Col, { span: 24 }, React.createElement(Space, null, React.createElement(Button, { type: 'primary', onClick: handleAddNext }, '下一步'), React.createElement(Button, { onClick: handleAddSave }, '保存'), React.createElement(Button, { onClick: function () { setCurrentView('list'); } }, '取消'))) + ) : React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 16, fontWeight: 600 } }, '备车检查'), + React.createElement(Table, { + dataSource: form.inspectionList || [], + rowKey: function (_, i) { return i; }, + pagination: false, + columns: [ + { title: '检查类型', dataIndex: 'checkType', width: 100 }, + { title: '检查项', dataIndex: 'checkItem', width: 120 }, + { title: '检查项结果', dataIndex: 'result', width: 120, render: function (val, record, i) { + return React.createElement(Select, { value: val, onChange: function (v) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { result: v }); n.inspectionList = list; return n; }); }, style: { width: '100%' }, options: [{ value: '正常', label: '正常' }, { value: '异常', label: '异常' }] }); + }}, + { title: '胎纹深度(轮胎时)', key: 'treadDepth', width: 140, render: function (_, record, i) { + if (record.checkItem && record.checkItem.indexOf('胎') !== -1) { + return React.createElement(Input, { value: record.treadDepth, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { treadDepth: e.target.value }); n.inspectionList = list; return n; }); }, placeholder: '胎纹深度' }); + } + return '-'; + }}, + { title: '备注', dataIndex: 'remark', render: function (val, record, i) { + return React.createElement(Input, { value: val, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { remark: e.target.value }); n.inspectionList = list; return n; }); }, placeholder: '备注' }); + }} + ] + }), + React.createElement('div', { style: { marginTop: 16 } }, React.createElement(Space, null, React.createElement(Button, { type: 'primary', onClick: handleAddSubmit }, '提交'), React.createElement(Button, { onClick: handleAddSave }, '保存'), React.createElement(Button, { onClick: function () { setCurrentView('list'); } }, '取消'))) + ) + ) + ); + } + + return ListView(); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function () { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/运维管理/车辆业务/新增后装设备.jsx b/web端/运维管理/车辆业务/新增后装设备.jsx new file mode 100644 index 0000000..9aa6f3c --- /dev/null +++ b/web端/运维管理/车辆业务/新增后装设备.jsx @@ -0,0 +1,143 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 新增后装设备 - 运维管理-车辆业务 +var ARCO_TOKEN = { + primary: '#165DFF', + primaryHover: '#4080FF', + neutral1: '#FFFFFF', + neutral4: '#E5E6EB', + neutral6: '#86909C', + neutral8: '#1D2129', + border: '#E5E6EB', + fill: '#F2F3F5', + shadowLight: '0 1px 2px rgba(0,0,0,0.05)', + radiusLarge: '8px', + spacing16: '16px', + spacing24: '24px', + fontSize14: '14px', + fontSize16: '16px', + fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif', + link: '#165DFF' +}; + +var DEVICE_TYPES = ['GPS', '尾板', '车身广告', 'G7安全套件', 'G7普通设备', 'G7温控设备', '备胎']; + +var MOCK_VEHICLE_OPTIONS = ['粤A12345', '粤A67890', '粤B11111', '粤B22222', '粤C33333', '京A88888']; + +const Component = function () { + var antd = window.antd; + var Select = antd.Select; + var Button = antd.Button; + var Input = antd.Input; + var message = antd.message; + var Option = Select.Option; + + var _useState = React.useState(''); + var deviceType = _useState[0]; + var setDeviceType = _useState[1]; + + var _useState2 = React.useState(''); + var vehicle = _useState2[0]; + var setVehicle = _useState2[1]; + + var _useState3 = React.useState(''); + var supplier = _useState3[0]; + var setSupplier = _useState3[1]; + + var handleSubmit = function () { + message.success('新增成功'); + setDeviceType(''); + setVehicle(''); + setSupplier(''); + }; + + var t = ARCO_TOKEN; + var styles = { + page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' }, + breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center' }, + breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: '8px' }, + breadcrumbCurrent: { color: t.neutral8 }, + card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing24, maxWidth: 560 }, + label: { display: 'block', marginBottom: 8, fontSize: t.fontSize14, color: t.neutral8 }, + row: { marginBottom: t.spacing16 }, + actions: { marginTop: t.spacing24, display: 'flex', gap: 12 } + }; + + var deviceTypeOptions = DEVICE_TYPES.map(function (type) { + return React.createElement(Option, { key: type, value: type }, type); + }); + + var vehicleOptions = MOCK_VEHICLE_OPTIONS.map(function (v) { + return React.createElement(Option, { key: v, value: v }, v); + }); + + return React.createElement( + 'div', + { style: styles.page }, + React.createElement( + 'div', + { style: styles.breadcrumb }, + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('span', { style: styles.breadcrumbCurrent }, '新增后装设备') + ), + React.createElement( + 'div', + { style: styles.card }, + React.createElement('div', { style: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing24, color: t.neutral8 } }, '新增后装设备'), + React.createElement('div', { style: styles.row }, + React.createElement('label', { style: styles.label }, '设备类型'), + React.createElement(Select, { + placeholder: '请选择设备类型', + style: { width: '100%' }, + value: deviceType || undefined, + onChange: function (v) { setDeviceType(v || ''); }, + allowClear: true + }, deviceTypeOptions) + ), + React.createElement('div', { style: styles.row }, + React.createElement('label', { style: styles.label }, '使用车辆'), + React.createElement(Select, { + placeholder: '请选择或输入搜索', + style: { width: '100%' }, + value: vehicle || undefined, + onChange: function (v) { setVehicle(v || ''); }, + showSearch: true, + allowClear: true, + filterOption: function (input, opt) { + var c = opt && opt.children; + return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; + } + }, vehicleOptions) + ), + React.createElement('div', { style: styles.row }, + React.createElement('label', { style: styles.label }, '供应商'), + React.createElement(Input, { + placeholder: '请输入供应商', + value: supplier, + onChange: function (e) { setSupplier(e.target.value); } + }) + ), + React.createElement('div', { style: styles.actions }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: function () { setDeviceType(''); setVehicle(''); setSupplier(''); } }, '重置') + ) + ) + ); +}; +if (typeof window !== 'undefined') { + window.Component = Component; + function mount() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); + } else { + setTimeout(mount, 0); + } +} diff --git a/web端/运维管理/车辆业务/新增备车.jsx b/web端/运维管理/车辆业务/新增备车.jsx new file mode 100644 index 0000000..5f12c7e --- /dev/null +++ b/web端/运维管理/车辆业务/新增备车.jsx @@ -0,0 +1,472 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆业务 - 新增备车(ONEOS运管平台,布局参照新增租赁合同) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + var useRef = React.useRef; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Select = antd.Select; + var Input = antd.Input; + var Button = antd.Button; + var Switch = antd.Switch; + var Drawer = antd.Drawer; + var Table = antd.Table; + var message = antd.message; + var Modal = antd.Modal; + var Space = antd.Space; + + // 车牌管理 mock:车牌号及对应车辆信息、后装设备(车身广告、尾板) + var plateManageList = [ + { plateNo: '京A12345', vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, tailboard: true }, + { plateNo: '京C11111', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', bodyAd: false, tailboard: false }, + { plateNo: '京D22222', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', bodyAd: true, tailboard: false }, + { plateNo: '京E33333', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', bodyAd: false, tailboard: true }, + { plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true } + ]; + + // 备车检查单:类别 -> 检查项目列表 + var inspectionCategoryItems = { + '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], + '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], + '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], + '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎'], + '液位检查': ['冷却液', '制动液', '玻璃水'], + '外观检查': ['车身外观', '漆面', '玻璃'], + '车辆外观': ['整车外观'], + '其他': ['其他检查项'], + '随车工具': ['三角牌', '灭火器', '反光背心'], + '随车证件': ['行驶证', '营运证', '保险单'], + '整车': ['整车状态'], + '燃料电池系统': ['氢系统', '储氢瓶'], + '冷机': ['冷机运行'], + '制动系统': ['制动踏板', '驻车制动'] + }; + + function buildInspectionList() { + var list = []; + var categories = Object.keys(inspectionCategoryItems); + for (var i = 0; i < categories.length; i++) { + var cat = categories[i]; + var items = inspectionCategoryItems[cat]; + for (var j = 0; j < items.length; j++) { + list.push({ category: cat, checkItem: items[j], checked: true, remark: '' }); + } + } + return list; + } + + var formState = useState({ + plateNo: '', + vehicleType: '', + brand: '', + model: '', + vin: '', + bodyAd: true, + adPhotos: [], + tailboard: false, + trailerPlate: '', + flawPhotos: [], + inspectionList: buildInspectionList() + }); + var form = formState[0]; + var setForm = formState[1]; + + var drawerOpenState = useState(false); + var photoPreviewState = useState({ open: false, url: null }); + var reqDocOpenState = useState(false); + var adPhotoInputRef = useRef(null); + var flawPhotoInputRef = useRef(null); + var plateOptions = useMemo(function () { + return plateManageList.map(function (p) { return { value: p.plateNo, label: p.plateNo }; }); + }, []); + + var onPlateSelect = useCallback(function (plateNo) { + var vehicle = plateManageList.find(function (p) { return p.plateNo === plateNo; }); + if (!vehicle) return; + setForm(function (prev) { + var next = {}; + for (var k in prev) next[k] = prev[k]; + next.plateNo = vehicle.plateNo; + next.vehicleType = vehicle.vehicleType || ''; + next.brand = vehicle.brand || ''; + next.model = vehicle.model || ''; + next.vin = vehicle.vin || ''; + next.bodyAd = !!vehicle.bodyAd; + next.tailboard = !!vehicle.tailboard; + return next; + }); + }, []); + + var updateInspection = useCallback(function (index, field, value) { + setForm(function (prev) { + var n = {}; + for (var k in prev) n[k] = prev[k]; + var list = (prev.inspectionList || []).slice(); + var row = list[index] || {}; + list[index] = Object.assign({}, row, { [field]: value }); + n.inspectionList = list; + return n; + }); + }, []); + + var addAdPhoto = useCallback(function () { + if ((form.adPhotos || []).length >= 4) { message.warning('最多上传4张'); return; } + if (adPhotoInputRef.current) adPhotoInputRef.current.click(); + }, [form.adPhotos]); + var onAdPhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + setForm(function (p) { + var current = p.adPhotos || []; + var remain = 4 - current.length; + if (remain <= 0) return p; + var urls = []; + for (var i = 0; i < files.length && urls.length < remain; i++) { + if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i])); + } + if (urls.length === 0) return p; + var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = current.concat(urls); return n; + }); + e.target.value = ''; + }, []); + var removeAdPhoto = useCallback(function (idx) { + setForm(function (p) { + var n = {}; + for (var k in p) n[k] = p[k]; + var arr = (p.adPhotos || []).slice(); + var url = arr[idx]; + if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url); + arr.splice(idx, 1); + n.adPhotos = arr; + return n; + }); + }, []); + var addFlawPhoto = useCallback(function () { + if ((form.flawPhotos || []).length >= 4) { message.warning('最多上传4张'); return; } + if (flawPhotoInputRef.current) flawPhotoInputRef.current.click(); + }, [form.flawPhotos]); + var onFlawPhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + setForm(function (p) { + var current = p.flawPhotos || []; + var remain = 4 - current.length; + if (remain <= 0) return p; + var urls = []; + for (var i = 0; i < files.length && urls.length < remain; i++) { + if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i])); + } + if (urls.length === 0) return p; + var n = {}; for (var k in p) n[k] = p[k]; n.flawPhotos = current.concat(urls); return n; + }); + e.target.value = ''; + }, []); + var removeFlawPhoto = useCallback(function (idx) { + setForm(function (p) { + var n = {}; + for (var k in p) n[k] = p[k]; + var arr = (p.flawPhotos || []).slice(); + var url = arr[idx]; + if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url); + arr.splice(idx, 1); + n.flawPhotos = arr; + return n; + }); + }, []); + + var handleSubmit = useCallback(function () { + if (!form.plateNo) { message.warning('请选择车牌号'); return; } + message.success('备车成功'); + // 保存数据并跳转至备车管理-已完成(实际项目接路由) + if (typeof window !== 'undefined' && window.history) window.history.back(); + }, [form.plateNo]); + var handleSave = useCallback(function () { + if (!form.plateNo) { message.warning('请选择车牌号'); return; } + message.success('暂存成功'); + // 保存数据并跳转至备车管理-待提交 + if (typeof window !== 'undefined' && window.history) window.history.back(); + }, [form.plateNo]); + + // 布局样式(参照新增租赁合同) + var styles = { + page: { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh', 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' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }, + formColFull: { flex: '0 0 100%', marginBottom: 8, boxSizing: 'border-box' }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 } + }; + + var FormItem = function (props) { + var colStyle = props.fullWidth ? styles.formColFull : styles.formCol; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, + props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, + props.label + ), + props.children + ); + }; + + var inspectionList = form.inspectionList || []; + var categoryRowSpans = useMemo(function () { + var list = inspectionList; + var spans = []; + var i = 0; + while (i < list.length) { + var cat = list[i].category; + var start = i; + while (i < list.length && list[i].category === cat) i++; + spans[start] = i - start; + for (var j = start + 1; j < i; j++) spans[j] = 0; + } + return spans; + }, [form.inspectionList]); + + var inspectionColumns = [ + { + title: '类别', + dataIndex: 'category', + key: 'category', + width: 120, + onCell: function (_, index) { return { rowSpan: categoryRowSpans[index] !== undefined ? categoryRowSpans[index] : 1 }; } + }, + { title: '检查项目', dataIndex: 'checkItem', key: 'checkItem', width: 180 }, + { + title: '检查情况', + key: 'checked', + width: 100, + render: function (_, record, index) { + return React.createElement(Switch, { + checked: record.checked !== false, + onChange: function (checked) { updateInspection(index, 'checked', checked); } + }); + } + }, + { + title: '备注', + dataIndex: 'remark', + key: 'remark', + render: function (val, record, index) { + return React.createElement(Input, { + value: val, + onChange: function (e) { updateInspection(index, 'remark', e.target.value); }, + placeholder: '备注' + }); + } + } + ]; + + // 方形拍照图标(广告照片与瑕疵共用) + var cameraIconBox = function (onClick) { + return React.createElement('div', { + role: 'button', + tabIndex: 0, + onClick: onClick, + style: { width: 64, height: 64, border: '1px dashed #d9d9d9', borderRadius: 4, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', background: '#fafafa', boxSizing: 'border-box' } + }, React.createElement('svg', { width: 28, height: 28, viewBox: '0 0 24 24', fill: 'none', stroke: '#999', strokeWidth: 2 }, + React.createElement('path', { d: 'M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z' }), + React.createElement('circle', { cx: 12, cy: 13, r: 4 }) + )); + }; + + var requirementDocContent = '新增备车\n\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增备车\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂;\n2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;'; + + var breadcrumbNodes = [ + React.createElement('span', { key: '1' }, '运维管理'), + React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '3' }, '车辆业务'), + React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '5' }, '备车管理'), + React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '新增备车') + ]; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明') + ), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车牌号', required: true }, + React.createElement(Select, { + placeholder: '请选择车牌号', + style: { width: '100%' }, + value: form.plateNo || undefined, + onChange: onPlateSelect, + showSearch: true, + allowClear: true, + optionFilterProp: 'label', + filterOption: function (input, opt) { + return (opt && opt.label && String(opt.label).toLowerCase().indexOf((input || '').toLowerCase()) >= 0); + }, + options: plateOptions + }) + ), + React.createElement(FormItem, { label: '车辆类型' }, + React.createElement(Input, { value: form.vehicleType, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: form.brand, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: form.model, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '车辆识别代码' }, + React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement('div', { style: styles.formCol, key: 'ph0' }), + React.createElement(FormItem, { label: '车身广告' }, + React.createElement(Switch, { + checked: form.bodyAd, + checkedChildren: '开', + unCheckedChildren: '关', + onChange: function (checked) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.bodyAd = checked; return n; }); + } + }) + ), + React.createElement('div', { style: styles.formCol, key: 'ph1' }), + React.createElement('div', { style: styles.formCol, key: 'ph2' }), + React.createElement(FormItem, { label: '尾板' }, + React.createElement(Switch, { + checked: form.tailboard, + checkedChildren: '开', + unCheckedChildren: '关', + onChange: function (checked) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = checked; return n; }); + } + }) + ), + React.createElement(FormItem, { label: '广告照片', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: adPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onAdPhotoFileChange }), + (form.adPhotos || []).length < 4 ? cameraIconBox(addAdPhoto) : null, + (form.adPhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeAdPhoto(i); } + }, '×') + ); + }) + ) + ), + React.createElement(FormItem, { label: '是否有挂' }, + React.createElement(Input, { + placeholder: '请输入挂车牌号,不输入为无挂', + value: form.trailerPlate, + onChange: function (e) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.trailerPlate = e.target.value; return n; }); + }, + style: { width: '100%', maxWidth: 320 } + }) + ), + React.createElement(FormItem, { label: '瑕疵', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: flawPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onFlawPhotoFileChange }), + (form.flawPhotos || []).length < 4 ? cameraIconBox(addFlawPhoto) : null, + (form.flawPhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeFlawPhoto(i); } + }, '×') + ); + }) + ) + ), + React.createElement(FormItem, { label: '车辆检查', fullWidth: true }, + React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单') + ) + ) + ) + ), + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleSave }, '保存'), + React.createElement(Button, { onClick: function () { if (typeof window !== 'undefined' && window.history) window.history.back(); } }, '取消') + ), + React.createElement(Drawer, { + title: '备车检查单', + placement: 'right', + width: 640, + open: drawerOpenState[0], + onClose: function () { drawerOpenState[1](false); }, + styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } }, + footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + React.createElement(Button, { onClick: function () { drawerOpenState[1](false); } }, '返回'), + React.createElement(Button, { type: 'primary', onClick: function () { drawerOpenState[1](false); message.success('检查单已保存'); } }, '提交') + ) + }, + React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } }, + React.createElement(Table, { + columns: inspectionColumns, + dataSource: form.inspectionList || [], + rowKey: function (_, i) { return i; }, + pagination: false, + size: 'small' + }) + ) + ), + React.createElement(Modal, { + title: '需求说明', + open: reqDocOpenState[0], + onCancel: function () { reqDocOpenState[1](false); }, + footer: null, + width: 720, + styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.8 } } + }, requirementDocContent), + photoPreviewState[0].open && photoPreviewState[0].url + ? React.createElement(Modal, { + title: '预览', + open: true, + footer: null, + onCancel: function () { photoPreviewState[1]({ open: false, url: null }); } + }, React.createElement('img', { src: photoPreviewState[0].url, alt: '', style: { width: '100%' } })) + : null + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function () { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/运维管理/车辆业务/查看备车.jsx b/web端/运维管理/车辆业务/查看备车.jsx new file mode 100644 index 0000000..815b0e1 --- /dev/null +++ b/web端/运维管理/车辆业务/查看备车.jsx @@ -0,0 +1,275 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆业务 - 查看备车(与新增备车相同布局,全部只读) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Button = antd.Button; + var Drawer = antd.Drawer; + var Table = antd.Table; + var Modal = antd.Modal; + + var plateManageList = [ + { plateNo: '京A12345', vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, tailboard: true }, + { plateNo: '京C11111', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', bodyAd: false, tailboard: false }, + { plateNo: '京D22222', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', bodyAd: true, tailboard: false }, + { plateNo: '京E33333', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', bodyAd: false, tailboard: true }, + { plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true } + ]; + + var inspectionCategoryItems = { + '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], + '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], + '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], + '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎'], + '液位检查': ['冷却液', '制动液', '玻璃水'], + '外观检查': ['车身外观', '漆面', '玻璃'], + '车辆外观': ['整车外观'], + '其他': ['其他检查项'], + '随车工具': ['三角牌', '灭火器', '反光背心'], + '随车证件': ['行驶证', '营运证', '保险单'], + '整车': ['整车状态'], + '燃料电池系统': ['氢系统', '储氢瓶'], + '冷机': ['冷机运行'], + '制动系统': ['制动踏板', '驻车制动'] + }; + + function buildInspectionList() { + var list = []; + var categories = Object.keys(inspectionCategoryItems); + for (var i = 0; i < categories.length; i++) { + var cat = categories[i]; + var items = inspectionCategoryItems[cat]; + for (var j = 0; j < items.length; j++) { + list.push({ category: cat, checkItem: items[j], checked: true, remark: '' }); + } + } + return list; + } + + // 只读:一条已填写的备车记录(不可编辑) + var form = useMemo(function () { + return { + plateNo: '京A12345', + vehicleType: '厢式货车', + brand: '东风', + model: 'DFH1180', + vin: 'LGHXCAE28M1234567', + bodyAd: true, + adPhotos: ['https://picsum.photos/200/150?r=1', 'https://picsum.photos/200/150?r=2'], + tailboard: true, + trailerPlate: '京B67890', + flawPhotos: ['https://picsum.photos/200/150?f=1'], + inspectionList: buildInspectionList() + }; + }, []); + + var drawerOpenState = useState(false); + var photoPreviewState = useState({ open: false, url: null }); + var reqDocOpenState = useState(false); + + var styles = { + page: { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh', 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' }, + cardBody: { padding: '20px 24px' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }, + formColFull: { flex: '0 0 100%', marginBottom: 8, boxSizing: 'border-box' }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 } + }; + + var FormItem = function (props) { + var colStyle = props.fullWidth ? styles.formColFull : styles.formCol; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, + props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, + props.label + ), + props.children + ); + }; + + var inspectionList = form.inspectionList || []; + var categoryRowSpans = useMemo(function () { + var list = inspectionList; + var spans = []; + var i = 0; + while (i < list.length) { + var cat = list[i].category; + var start = i; + while (i < list.length && list[i].category === cat) i++; + spans[start] = i - start; + for (var j = start + 1; j < i; j++) spans[j] = 0; + } + return spans; + }, [form.inspectionList]); + + var inspectionColumns = [ + { + title: '类别', + dataIndex: 'category', + key: 'category', + width: 120, + onCell: function (_, index) { return { rowSpan: categoryRowSpans[index] !== undefined ? categoryRowSpans[index] : 1 }; } + }, + { title: '检查项目', dataIndex: 'checkItem', key: 'checkItem', width: 180 }, + { + title: '检查情况', + key: 'checked', + width: 100, + render: function (_, record) { return record.checked !== false ? '开' : '关'; } + }, + { title: '备注', dataIndex: 'remark', key: 'remark', render: function (val) { return val || '-'; } } + ]; + + var requirementDocContent = '新增备车\n\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增备车\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂;\n2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;'; + + var breadcrumbNodes = [ + React.createElement('span', { key: '1' }, '运维管理'), + React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '3' }, '车辆业务'), + React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '5' }, '备车管理'), + React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '查看备车') + ]; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明') + ), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车牌号', required: true }, + React.createElement(Input, { value: form.plateNo, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '车辆类型' }, + React.createElement(Input, { value: form.vehicleType, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: form.brand, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: form.model, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '车辆识别代码' }, + React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement('div', { style: styles.formCol, key: 'ph0' }), + React.createElement(FormItem, { label: '车身广告' }, + React.createElement(Input, { value: form.bodyAd ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement('div', { style: styles.formCol, key: 'ph1' }), + React.createElement('div', { style: styles.formCol, key: 'ph2' }), + React.createElement(FormItem, { label: '尾板' }, + React.createElement(Input, { value: form.tailboard ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '广告照片', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + (form.adPhotos || []).map(function (url, i) { + return React.createElement('img', { + key: i, + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }); + }), + (form.adPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null + ) + ), + React.createElement(FormItem, { label: '是否有挂' }, + React.createElement(Input, { value: form.trailerPlate || '无', disabled: true, style: { width: '100%', maxWidth: 320, background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '瑕疵', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + (form.flawPhotos || []).map(function (url, i) { + return React.createElement('img', { + key: i, + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }); + }), + (form.flawPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null + ) + ), + React.createElement(FormItem, { label: '车辆检查', fullWidth: true }, + React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单') + ) + ) + ) + ), + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { onClick: function () { if (typeof window !== 'undefined' && window.history) window.history.back(); } }, '返回') + ), + React.createElement(Drawer, { + title: '备车检查单', + placement: 'right', + width: 640, + open: drawerOpenState[0], + onClose: function () { drawerOpenState[1](false); }, + styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } }, + footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end' } }, + React.createElement(Button, { onClick: function () { drawerOpenState[1](false); } }, '返回') + ) + }, + React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } }, + React.createElement(Table, { + columns: inspectionColumns, + dataSource: form.inspectionList || [], + rowKey: function (_, i) { return i; }, + pagination: false, + size: 'small' + }) + ) + ), + React.createElement(Modal, { + title: '需求说明', + open: reqDocOpenState[0], + onCancel: function () { reqDocOpenState[1](false); }, + footer: null, + width: 720, + styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.8 } } + }, requirementDocContent), + photoPreviewState[0].open && photoPreviewState[0].url + ? React.createElement(Modal, { + title: '预览', + open: true, + footer: null, + onCancel: function () { photoPreviewState[1]({ open: false, url: null }); } + }, React.createElement('img', { src: photoPreviewState[0].url, alt: '', style: { width: '100%' } })) + : null + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function () { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + }); + } else { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } +} diff --git a/web端/运维管理/车辆业务/调拨管理.jsx b/web端/运维管理/车辆业务/调拨管理.jsx new file mode 100644 index 0000000..2293371 --- /dev/null +++ b/web端/运维管理/车辆业务/调拨管理.jsx @@ -0,0 +1,2240 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 调拨管理 - 车辆资产管理后台模块 +// 设计变量参考 Arco Design Token: https://arco.design/react/docs/token +var ARCO_TOKEN = { + primary: '#165DFF', + primaryHover: '#4080FF', + primaryActive: '#0E42D2', + danger: '#F53F3F', + success: '#00B42A', + warning: '#FF7D00', + link: '#165DFF', + neutral1: '#FFFFFF', + neutral2: '#F7F8FA', + neutral3: '#F2F3F5', + neutral4: '#E5E6EB', + neutral5: '#C9CDD4', + neutral6: '#86909C', + neutral7: '#4E5969', + neutral8: '#1D2129', + border: '#E5E6EB', + borderSecondary: '#C9CDD4', + fill: '#F2F3F5', + fillSecondary: '#F7F8FA', + shadowLight: '0 1px 2px rgba(0,0,0,0.05)', + shadowMedium: '0 2px 8px rgba(0,0,0,0.08)', + radiusSmall: '2px', + radiusMedium: '4px', + radiusLarge: '8px', + spacing4: '4px', + spacing8: '8px', + spacing12: '12px', + spacing16: '16px', + spacing24: '24px', + fontSize14: '14px', + fontSize16: '16px', + fontFamily: '"PingFang SC", "苹方-简", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif' +}; + +var APPROVAL_STATUS = [ + { value: 'pending_submit', label: '待提交' }, + { value: 'pending_approval', label: '待审批' }, + { value: 'approving', label: '审批中' }, + { value: 'completed', label: '审批完成' }, + { value: 'rejected', label: '审批驳回' }, + { value: 'withdrawn', label: '撤回' } +]; + +var TRANSFER_METHODS = [ + { value: 'third_party', label: '第三方运输' }, + { value: 'driver', label: '司机驾驶' } +]; + +var provinceList = [ + { code: 'gd', name: '广东省' }, + { code: 'zj', name: '浙江省' }, + { code: 'bj', name: '北京市' } +]; +var cityMap = { + gd: [{ code: 'gz', name: '广州市' }, { code: 'sz', name: '深圳市' }], + zj: [{ code: 'hz', name: '杭州市' }, { code: 'nb', name: '宁波市' }], + bj: [{ code: 'bj', name: '北京市' }] +}; + +// 运维部用户(操作人、创建人来源) +var mockOpsUsers = [ + { id: 'u1', name: '张明', dept: '运维部' }, + { id: 'u2', name: '王芳', dept: '运维部' }, + { id: 'u3', name: '李华', dept: '运维部' }, + { id: 'u4', name: '赵强', dept: '运维部' }, + { id: 'u5', name: '陈静', dept: '运维部' } +]; + +// 库存车辆(含车辆类型、品牌、型号、停车场、氢量单位等,用于反写) +var mockStockVehicles = [ + { id: 'v1', plateNo: '粤A12345', status: '库存', vehicleType: '轿车', brand: '比亚迪', model: '秦', departParking: '天河智慧停车场', h2Unit: '%' }, + { id: 'v2', plateNo: '粤A67890', status: '库存', vehicleType: '轿车', brand: '特斯拉', model: 'Model 3', departParking: '南山科技园停车场', h2Unit: 'MPa' }, + { id: 'v3', plateNo: '浙A11111', status: '库存', vehicleType: 'SUV', brand: '小鹏', model: 'P7', departParking: '西湖景区停车场', h2Unit: 'KG' }, + { id: 'v4', plateNo: '浙B22222', status: '库存', vehicleType: '轿车', brand: '蔚来', model: 'ET5', departParking: '宁波江北停车场', h2Unit: '%' }, + { id: 'v5', plateNo: '京A88888', status: '库存', vehicleType: 'MPV', brand: '理想', model: 'L9', departParking: '朝阳区停车场', h2Unit: 'MPa' }, + { id: 'v6', plateNo: '粤B11111', status: '库存', vehicleType: '轿车', brand: '比亚迪', model: '汉', departParking: '福田停车场', h2Unit: 'KG' } +]; + +// 审批节点(查看时步骤条) +var mockApprovalSteps = [ + { key: 'node1', name: '区域负责人审批' }, + { key: 'node2', name: '运营负责人审批' }, + { key: 'node3', name: '财务确认' } +]; + +const Component = function () { + // 视图:list | add | edit | view + var _sv = React.useState('list'); + var currentView = _sv[0]; + var setCurrentView = _sv[1]; + + // Tab:in_progress | history + var _tab = React.useState('in_progress'); + var listTab = _tab[0]; + var setListTab = _tab[1]; + + // 筛选 + var _fd = React.useState(''); + var filterDateStart = _fd[0]; + var setFilterDateStart = _fd[1]; + var _fe = React.useState(''); + var filterDateEnd = _fe[0]; + var setFilterDateEnd = _fe[1]; + var _fo = React.useState(''); + var filterOperator = _fo[0]; + var setFilterOperator = _fo[1]; + var _frdStart = React.useState(''); + var filterReceiveDateStart = _frdStart[0]; + var setFilterReceiveDateStart = _frdStart[1]; + var _frdEnd = React.useState(''); + var filterReceiveDateEnd = _frdEnd[0]; + var setFilterReceiveDateEnd = _frdEnd[1]; + var _frc = React.useState(''); + var filterReceiver = _frc[0]; + var setFilterReceiver = _frc[1]; + var _fpl = React.useState(''); + var filterPlateNo = _fpl[0]; + var setFilterPlateNo = _fpl[1]; + var _fdp = React.useState(''); + var filterDepartProvince = _fdp[0]; + var setFilterDepartProvince = _fdp[1]; + var _fdc = React.useState(''); + var filterDepartCity = _fdc[0]; + var setFilterDepartCity = _fdc[1]; + var _ftp = React.useState(''); + var filterTransferProvince = _ftp[0]; + var setFilterTransferProvince = _ftp[1]; + var _ftc = React.useState(''); + var filterTransferCity = _ftc[0]; + var setFilterTransferCity = _ftc[1]; + var _fm = React.useState(''); + var filterMethod = _fm[0]; + var setFilterMethod = _fm[1]; + var _fas = React.useState(''); + var filterApprovalStatus = _fas[0]; + var setFilterApprovalStatus = _fas[1]; + + // 已应用的筛选条件(仅点击查询后更新,列表按此筛选) + var _applied = React.useState({}); + var appliedFilters = _applied[0]; + var setAppliedFilters = _applied[1]; + + // 筛选下拉展开 + var _ao = React.useState(false); + var filterOperatorOpen = _ao[0]; + var setFilterOperatorOpen = _ao[1]; + var _arc = React.useState(false); + var filterReceiverOpen = _arc[0]; + var setFilterReceiverOpen = _arc[1]; + var _ap = React.useState(false); + var filterPlateOpen = _ap[0]; + var setFilterPlateOpen = _ap[1]; + var _cd = React.useState(false); + var filterCascaderDepartOpen = _cd[0]; + var setFilterCascaderDepartOpen = _cd[1]; + var _ct = React.useState(false); + var filterCascaderTransferOpen = _ct[0]; + var setFilterCascaderTransferOpen = _ct[1]; + var _drOpen = React.useState(false); + var filterDateRangeOpen = _drOpen[0]; + var setFilterDateRangeOpen = _drOpen[1]; + var _drRecOpen = React.useState(false); + var filterReceiveDateRangeOpen = _drRecOpen[0]; + var setFilterReceiveDateRangeOpen = _drRecOpen[1]; + function getInitialViewMonth(valueStr) { + if (valueStr && /^\d{4}-\d{2}-\d{2}$/.test(valueStr)) return valueStr.substring(0, 7); + var d = new Date(); + return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0'); + } + var _drViewL = React.useState(''); + var filterDateRangeViewLeft = _drViewL[0]; + var setFilterDateRangeViewLeft = _drViewL[1]; + var _drViewR = React.useState(''); + var filterDateRangeViewRight = _drViewR[0]; + var setFilterDateRangeViewRight = _drViewR[1]; + var _drRecViewL = React.useState(''); + var filterReceiveDateRangeViewLeft = _drRecViewL[0]; + var setFilterReceiveDateRangeViewLeft = _drRecViewL[1]; + var _drRecViewR = React.useState(''); + var filterReceiveDateRangeViewRight = _drRecViewR[0]; + var setFilterReceiveDateRangeViewRight = _drRecViewR[1]; + + // 分页 + var _cp = React.useState(1); + var currentPage = _cp[0]; + var setCurrentPage = _cp[1]; + var _ps = React.useState(20); + var pageSize = _ps[0]; + var setPageSize = _ps[1]; + + // 调拨记录列表:每条主记录含 lines(按车牌号展开,每车牌一行),hasReceiveRecord 表示是否已补充接收 + // 进行中 10 条(20 行),默认每页 20 条,第一页同时展示「接收记录」「编辑」「撤回」供开发参考 + var _mockList = React.useState((function () { + var regions = [ + { p: 'gd', c: 'gz', label: '广东省-广州市' }, + { p: 'gd', c: 'sz', label: '广东省-深圳市' }, + { p: 'zj', c: 'hz', label: '浙江省-杭州市' }, + { p: 'zj', c: 'nb', label: '浙江省-宁波市' }, + { p: 'bj', c: 'bj', label: '北京市-北京市' } + ]; + var users = [{ id: 'u1', name: '张明' }, { id: 'u2', name: '王芳' }, { id: 'u3', name: '李华' }, { id: 'u4', name: '赵强' }, { id: 'u5', name: '陈静' }]; + var vehicles = [ + { plateNo: '粤A12345', vehicleType: '轿车', brand: '比亚迪', model: '秦', departParking: '天河智慧停车场', h2Unit: '%' }, + { plateNo: '粤A67890', vehicleType: '轿车', brand: '特斯拉', model: 'Model 3', departParking: '南山科技园停车场', h2Unit: 'MPa' }, + { plateNo: '浙A11111', vehicleType: 'SUV', brand: '小鹏', model: 'P7', departParking: '西湖景区停车场', h2Unit: 'KG' }, + { plateNo: '浙B22222', vehicleType: '轿车', brand: '蔚来', model: 'ET5', departParking: '宁波江北停车场', h2Unit: '%' }, + { plateNo: '京A88888', vehicleType: 'MPV', brand: '理想', model: 'L9', departParking: '朝阳区停车场', h2Unit: 'MPa' }, + { plateNo: '粤B11111', vehicleType: '轿车', brand: '比亚迪', model: '汉', departParking: '福田停车场', h2Unit: 'KG' } + ]; + var reasons = ['区域调配需求', '司机驾驶调拨', '维修后回调', '新车投放', '租赁到期回调']; + var carriers = [{ name: '顺丰物流', contact: '400-111-2222' }, { name: '德邦物流', contact: '95353' }]; + var list = []; + var tid = 1; + function makeTransfer(inProgress, hasReceive, count) { + for (var c = 0; c < count; c++) { + var status = inProgress ? ['pending_submit', 'pending_approval', 'approving', 'rejected', 'withdrawn'][tid % 5] : 'completed'; + var hasRec = inProgress ? false : hasReceive; + var dep = regions[tid % regions.length]; + var trans = regions[(tid + 1) % regions.length]; + var u = users[tid % users.length]; + var d = '2025-' + String((tid % 12) + 1).padStart(2, '0') + '-' + String((tid % 20) + 1).padStart(2, '0'); + var createTime = d + ' ' + String(9 + (tid % 8)).padStart(2, '0') + ':' + (tid % 2 ? '15' : '30'); + var lineCount = 2; + var lines = []; + for (var L = 0; L < lineCount; L++) { + var v = vehicles[(tid + L) % vehicles.length]; + lines.push({ + plateNo: v.plateNo, + vehicleType: v.vehicleType, + brand: v.brand, + model: v.model, + departParking: v.departParking, + departMileage: 1000 + tid * 100 + L * 50, + departH2: (60 + tid + L) + (v.h2Unit === 'KG' ? '' : v.h2Unit), + departH2Unit: v.h2Unit, + departElec: 80 + tid + L, + receiveMileage: hasRec ? 1100 + tid * 100 + L * 50 : null, + receiveH2: hasRec ? (65 + tid + L) + v.h2Unit : null, + receiveElec: hasRec ? 85 + tid + L : null, + receiveParking: hasRec ? '抵达停车场' + tid : null, + receiverName: hasRec ? users[(tid + 1) % users.length].name : null + }); + } + list.push({ + id: 't' + tid, + transferDate: d, + departProvince: dep.p, + departCity: dep.c, + departLabel: dep.label, + operatorId: u.id, + operatorName: u.name, + transferProvince: trans.p, + transferCity: trans.c, + transferLabel: trans.label, + transferMethod: tid % 2 ? 'third_party' : 'driver', + transferReason: reasons[tid % reasons.length], + carrierName: carriers[tid % 2].name, + carrierContact: carriers[tid % 2].contact, + approvalStatus: status, + hasReceiveRecord: hasRec, + receiveDate: hasRec ? '2025-' + String((tid % 12) + 1).padStart(2, '0') + '-' + String((tid % 15) + 10).padStart(2, '0') : '', + receiverId: hasRec ? users[(tid + 1) % users.length].id : '', + receiverName: hasRec ? users[(tid + 1) % users.length].name : '', + createTime: createTime, + lines: lines + }); + tid++; + } + } + makeTransfer(false, false, 2); // 进行中:审批完成但未补充接收(2 条,排在最前) + makeTransfer(true, false, 18); // 进行中:待提交/待审批/审批中/驳回/撤回(共 20 条进行中) + makeTransfer(false, true, 20); // 历史记录:审批完成且已补充接收(20 条) + return list; + }())); + var mockTransferList = _mockList[0]; + var setMockTransferList = _mockList[1]; + + // 新增/编辑表单(编辑、查看、接收仍用 formData) + var defaultForm = { + transferDate: '', + receiveDate: '', + operatorId: '', + operatorName: '', + selectedVehicleIds: [], + transferMethod: '', + transferReason: '', + departProvince: '', + departCity: '', + transferProvince: '', + transferCity: '', + carrierName: '', + carrierContact: '' + }; + var _form = React.useState(defaultForm); + var formData = _form[0]; + var setFormData = _form[1]; + + // 新增页专用:上方 7 项 + 下方可编辑行表 + var defaultAddForm = { + transferDate: '', + departProvince: '', + departCity: '', + transferProvince: '', + transferCity: '', + transferMethod: '', + transferReason: '', + carrierName: '', + carrierContact: '' + }; + var _addForm = React.useState(defaultAddForm); + var addFormData = _addForm[0]; + var setAddFormData = _addForm[1]; + var addFormLineIdRef = React.useRef(0); + function createEmptyAddLine() { + addFormLineIdRef.current = (addFormLineIdRef.current || 0) + 1; + return { + rowId: 'addRow-' + addFormLineIdRef.current, + vehicleId: '', + plateNo: '', + vehicleType: '', + brand: '', + model: '', + departParking: '', + departMileage: '', + departH2: '', + departH2Unit: '', + departElec: '' + }; + } + var _addLines = React.useState(function () { return [{ rowId: 'addRow-1', vehicleId: '', plateNo: '', vehicleType: '', brand: '', model: '', departParking: '', departMileage: '', departH2: '', departH2Unit: '', departElec: '' }]; }); + var addFormLines = _addLines[0]; + var setAddFormLines = _addLines[1]; + var _plateOpen = React.useState(null); + var plateSelectOpenRowId = _plateOpen[0]; + var setPlateSelectOpenRowId = _plateOpen[1]; + var _plateKw = React.useState(''); + var plateSearchKeyword = _plateKw[0]; + var setPlateSearchKeyword = _plateKw[1]; + var _addDepartCascader = React.useState(false); + var addDepartCascaderOpen = _addDepartCascader[0]; + var setAddDepartCascaderOpen = _addDepartCascader[1]; + var _addAcceptCascader = React.useState(false); + var addAcceptCascaderOpen = _addAcceptCascader[0]; + var setAddAcceptCascaderOpen = _addAcceptCascader[1]; + + // 编辑页专用:行表 + 车牌选择展开态(与新增页结构一致) + var editFormLineIdRef = React.useRef(0); + function createEmptyEditLine() { + editFormLineIdRef.current = editFormLineIdRef.current + 1; + return { + rowId: 'editRow-' + editFormLineIdRef.current, + vehicleId: '', + plateNo: '', + vehicleType: '', + brand: '', + model: '', + departParking: '', + departMileage: '', + departH2: '', + departH2Unit: '', + departElec: '', + receiveMileage: '', + receiveH2: '', + receiveElec: '' + }; + } + function buildEditFormLinesFromRecord(transfer) { + if (!transfer || !transfer.lines || !transfer.lines.length) return [createEmptyEditLine()]; + editFormLineIdRef.current = Math.max(editFormLineIdRef.current || 0, transfer.lines.length); + return transfer.lines.map(function (line, idx) { + var v = mockStockVehicles.find(function (x) { return x.plateNo === line.plateNo; }); + return { + rowId: 'editRow-' + (idx + 1), + vehicleId: v ? v.id : (line.vehicleId || ''), + plateNo: line.plateNo || '', + vehicleType: line.vehicleType || (v && v.vehicleType) || '', + brand: line.brand || (v && v.brand) || '', + model: line.model || (v && v.model) || '', + departParking: line.departParking || (v && v.departParking) || '', + departMileage: line.departMileage != null && line.departMileage !== '' ? line.departMileage : '', + departH2: line.departH2 != null && line.departH2 !== '' ? line.departH2 : '', + departH2Unit: (line.departH2Unit || (v && v.h2Unit) || ''), + departElec: line.departElec != null && line.departElec !== '' ? line.departElec : '', + receiveMileage: line.receiveMileage != null && line.receiveMileage !== '' ? line.receiveMileage : '', + receiveH2: line.receiveH2 != null && line.receiveH2 !== '' ? line.receiveH2 : '', + receiveElec: line.receiveElec != null && line.receiveElec !== '' ? line.receiveElec : '' + }; + }); + } + var _editFormLines = React.useState([]); + var editFormLines = _editFormLines[0]; + var setEditFormLines = _editFormLines[1]; + var _editPlateOpen = React.useState(null); + var editPlateSelectOpenRowId = _editPlateOpen[0]; + var setEditPlateSelectOpenRowId = _editPlateOpen[1]; + var _editPlateKw = React.useState(''); + var editPlateSearchKeyword = _editPlateKw[0]; + var setEditPlateSearchKeyword = _editPlateKw[1]; + + function updateEditLine(rowId, updates) { + setEditFormLines(function (prev) { + return prev.map(function (l) { + if (l.rowId !== rowId) return l; + var next = {}; + for (var k in l) next[k] = l[k]; + for (var k in updates) next[k] = updates[k]; + return next; + }); + }); + } + function getEditPagePlateOptions(rowId) { + var usedElsewhere = function (v) { + return editFormLines.some(function (l) { return l.rowId !== rowId && l.vehicleId === v.id; }); + }; + var list = mockStockVehicles.filter(function (v) { return !usedElsewhere(v); }); + var kw = (editPlateSearchKeyword || '').trim(); + if (kw) list = list.filter(function (v) { return (v.plateNo || '').indexOf(kw) >= 0; }); + return list; + } + + // 编辑/查看 当前记录 + var _edit = React.useState(null); + var editRecord = _edit[0]; + var setEditRecord = _edit[1]; + var _view = React.useState(null); + var viewRecord = _view[0]; + var setViewRecord = _view[1]; + + // 新增页级联/穿梭框等 UI 状态 + var _formDp = React.useState(false); + var formDepartCascaderOpen = _formDp[0]; + var setFormDepartCascaderOpen = _formDp[1]; + var _formTp = React.useState(false); + var formTransferCascaderOpen = _formTp[0]; + var setFormTransferCascaderOpen = _formTp[1]; + var _formOp = React.useState(false); + var formOperatorOpen = _formOp[0]; + var setFormOperatorOpen = _formOp[1]; + // 穿梭框:左侧/右侧选中的 id 列表(用于多选后点击添加/移除) + var _leftSel = React.useState([]); + var leftSelectedIds = _leftSel[0]; + var setLeftSelectedIds = _leftSel[1]; + var _rightSel = React.useState([]); + var rightSelectedIds = _rightSel[0]; + var setRightSelectedIds = _rightSel[1]; + + var _toast = React.useState(''); + var toastMessage = _toast[0]; + var setToastMessage = _toast[1]; + var _reqModal = React.useState(false); + var showRequirementModal = _reqModal[0]; + var setShowRequirementModal = _reqModal[1]; + var _withdrawConfirm = React.useState(null); + var withdrawConfirmRecord = _withdrawConfirm[0]; + var setWithdrawConfirmRecord = _withdrawConfirm[1]; + var _selectedRows = React.useState([]); + var selectedRowIds = _selectedRows[0]; + var setSelectedRowIds = _selectedRows[1]; + var _receiveForm = React.useState({ receiveDate: '', receiverName: '张明', lines: [] }); + var receiveFormData = _receiveForm[0]; + var setReceiveFormData = _receiveForm[1]; + + function getStatusLabel(value) { + var s = APPROVAL_STATUS.find(function (x) { return x.value === value; }); + return s ? s.label : value; + } + function getMethodLabel(value) { + var m = TRANSFER_METHODS.find(function (x) { return x.value === value; }); + return m ? m.label : value; + } + // 氢量只显示一次单位:若值已带 %/MPa/KG 则不追加单位 + function formatH2(val, unit) { + if (val == null || val === '') return '-'; + var s = String(val); + if (s.match(/%|MPa|KG$/)) return s; + return s + (unit || ''); + } + + // 将主记录按 lines 展开为行(每车牌一行),并进行中/历史记录 tab 筛选 + function getFlattenedRows() { + var rows = []; + mockTransferList.forEach(function (t) { + var inProgress = ['pending_submit', 'pending_approval', 'approving', 'rejected', 'withdrawn'].indexOf(t.approvalStatus) >= 0 || (t.approvalStatus === 'completed' && !t.hasReceiveRecord); + var inHistory = t.approvalStatus === 'completed' && t.hasReceiveRecord; + if (listTab === 'in_progress' && !inProgress) return; + if (listTab === 'history' && !inHistory) return; + (t.lines || []).forEach(function (line) { + rows.push({ transfer: t, line: line, rowId: t.id + '-' + (line.plateNo || '') }); + }); + }); + return rows; + } + + function getFilteredList() { + var list = getFlattenedRows(); + var a = appliedFilters; + list = list.filter(function (row) { + var t = row.transfer; + var line = row.line; + if (a.filterDateStart && t.transferDate < a.filterDateStart) return false; + if (a.filterDateEnd && t.transferDate > a.filterDateEnd) return false; + if (a.filterOperator && (!t.operatorName || t.operatorName.indexOf(a.filterOperator) < 0)) return false; + if (a.filterReceiveDateStart && t.receiveDate && t.receiveDate < a.filterReceiveDateStart) return false; + if (a.filterReceiveDateEnd && t.receiveDate && t.receiveDate > a.filterReceiveDateEnd) return false; + if (a.filterReceiver && (!t.receiverName || t.receiverName.indexOf(a.filterReceiver) < 0)) return false; + if (a.filterPlateNo && (!line.plateNo || line.plateNo.indexOf(a.filterPlateNo) < 0)) return false; + if (a.filterDepartProvince && t.departProvince !== a.filterDepartProvince) return false; + if (a.filterDepartCity && t.departCity !== a.filterDepartCity) return false; + if (a.filterTransferProvince && t.transferProvince !== a.filterTransferProvince) return false; + if (a.filterTransferCity && t.transferCity !== a.filterTransferCity) return false; + if (a.filterMethod && t.transferMethod !== a.filterMethod) return false; + if (a.filterApprovalStatus && t.approvalStatus !== a.filterApprovalStatus) return false; + return true; + }); + return list; + } + + var filteredList = getFilteredList(); + var totalItems = filteredList.length; + var totalPages = Math.ceil(totalItems / pageSize) || 1; + var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage); + if (validPage !== currentPage && totalPages > 0) { + setCurrentPage(1); + validPage = 1; + } + var startIndex = (validPage - 1) * pageSize; + var endIndex = startIndex + pageSize; + var paginatedList = filteredList.slice(startIndex, endIndex); + + function handleFormChange(field, value) { + var next = {}; + for (var k in formData) { next[k] = formData[k]; } + next[field] = value; + setFormData(next); + } + + function handleAddFormChange(field, value) { + var next = {}; + for (var k in addFormData) { next[k] = addFormData[k]; } + next[field] = value; + setAddFormData(next); + } + + function validateAddForm(submitForApproval) { + if (!addFormData.transferDate) { + setToastMessage('请选择调拨日期'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.departProvince || !addFormData.departCity) { + setToastMessage('请选择出发区域'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.transferProvince || !addFormData.transferCity) { + setToastMessage('请选择接收区域'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.transferMethod) { + setToastMessage('请选择调拨方式'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.transferReason || !addFormData.transferReason.trim()) { + setToastMessage('请填写调拨原因'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.carrierName || !addFormData.carrierName.trim()) { + setToastMessage('请填写运输方名称'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!addFormData.carrierContact || !addFormData.carrierContact.trim()) { + setToastMessage('请填写运输方联系方式'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + var validLines = addFormLines.filter(function (row) { return row.vehicleId && row.plateNo; }); + if (validLines.length === 0) { + setToastMessage('请至少添加一行并选择车牌号'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + for (var i = 0; i < addFormLines.length; i++) { + var row = addFormLines[i]; + if (!row.plateNo) continue; + var rowNum = i + 1; + if (row.departMileage === '' || row.departMileage === undefined || row.departMileage === null) { + setToastMessage('请填写第' + rowNum + '行出发里程(KM)'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.departH2 === '' || row.departH2 === undefined || row.departH2 === null) { + setToastMessage('请填写第' + rowNum + '行出发氢量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.departElec === '' || row.departElec === undefined || row.departElec === null) { + setToastMessage('请填写第' + rowNum + '行出发电量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + } + return true; + } + + function buildAddRecord(approvalStatus) { + var dp = provinceList.find(function (x) { return x.code === addFormData.departProvince; }); + var dc = (cityMap[addFormData.departProvince] || []).find(function (x) { return x.code === addFormData.departCity; }); + var tp = provinceList.find(function (x) { return x.code === addFormData.transferProvince; }); + var tc = (cityMap[addFormData.transferProvince] || []).find(function (x) { return x.code === addFormData.transferCity; }); + var newId = 't' + (mockTransferList.length + 1); + var now = new Date(); + var createTimeStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'); + var validLines = addFormLines.filter(function (row) { return row.vehicleId && row.plateNo; }); + var lines = validLines.map(function (row) { + return { + plateNo: row.plateNo, + vehicleType: row.vehicleType || '-', + brand: row.brand || '-', + model: row.model || '-', + departParking: row.departParking || '-', + departMileage: row.departMileage != null && row.departMileage !== '' ? Number(row.departMileage) : null, + departH2: row.departH2 != null && row.departH2 !== '' ? row.departH2 : '', + departH2Unit: row.departH2Unit || '', + departElec: row.departElec != null && row.departElec !== '' ? Number(row.departElec) : null, + receiveMileage: null, + receiveH2: null, + receiveElec: null, + receiveParking: null, + receiverName: null + }; + }); + return { + id: newId, + transferDate: addFormData.transferDate, + operatorId: 'u1', + operatorName: '张明', + departProvince: addFormData.departProvince, + departCity: addFormData.departCity, + departLabel: (dp ? dp.name : '') + '-' + (dc ? dc.name : ''), + transferProvince: addFormData.transferProvince, + transferCity: addFormData.transferCity, + transferLabel: (tp ? tp.name : '') + '-' + (tc ? tc.name : ''), + transferMethod: addFormData.transferMethod, + transferReason: addFormData.transferReason || '', + carrierName: addFormData.carrierName || '', + carrierContact: addFormData.carrierContact || '', + approvalStatus: approvalStatus, + hasReceiveRecord: false, + receiveDate: '', + receiverId: '', + receiverName: '', + createTime: createTimeStr, + lines: lines + }; + } + + function handleAddSave() { + if (!validateAddForm(false)) return; + var newRecord = buildAddRecord('pending_submit'); + setMockTransferList(mockTransferList.concat([newRecord])); + setToastMessage('保存成功'); + setTimeout(function () { setToastMessage(''); }, 2000); + setCurrentView('list'); + setAddFormData(defaultAddForm); + setAddFormLines([createEmptyAddLine()]); + setPlateSelectOpenRowId(null); + } + + function handleAddSubmit() { + if (!validateAddForm(true)) return; + var newRecord = buildAddRecord('pending_approval'); + setMockTransferList(mockTransferList.concat([newRecord])); + setToastMessage('已提交审核'); + setTimeout(function () { setToastMessage(''); }, 2000); + setCurrentView('list'); + setAddFormData(defaultAddForm); + setAddFormLines([createEmptyAddLine()]); + setPlateSelectOpenRowId(null); + } + + function handleAddCancel() { + setCurrentView('list'); + setAddFormData(defaultAddForm); + setAddFormLines([createEmptyAddLine()]); + setPlateSelectOpenRowId(null); + } + + function updateAddLine(rowId, updates) { + setAddFormLines(function (prev) { + return prev.map(function (l) { + if (l.rowId !== rowId) return l; + var next = {}; + for (var k in l) next[k] = l[k]; + for (var k in updates) next[k] = updates[k]; + return next; + }); + }); + } + + function getAddPagePlateOptions(rowId) { + var usedElsewhere = function (v) { + return addFormLines.some(function (l) { return l.rowId !== rowId && l.vehicleId === v.id; }); + }; + var list = mockStockVehicles.filter(function (v) { return !usedElsewhere(v); }); + var kw = (plateSearchKeyword || '').trim(); + if (kw) list = list.filter(function (v) { return (v.plateNo || '').indexOf(kw) >= 0; }); + return list; + } + + function renderAddPage() { + var grid = styles.formGrid; + return React.createElement('div', {}, + React.createElement('div', { style: Object.assign({}, styles.card, { marginBottom: t.spacing16 }) }, + React.createElement('div', { style: grid }, + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨日期', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, type: 'date', value: addFormData.transferDate, onChange: function (e) { handleAddFormChange('transferDate', e.target.value); } }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '出发区域', React.createElement('span', { style: styles.formLabelReq }, '*')), + renderCascader(addFormData.departProvince, addFormData.departCity, function (v) { setAddFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departProvince = v; n.departCity = ''; return n; }); }, function (v) { setAddFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departCity = v; return n; }); }, addDepartCascaderOpen, setAddDepartCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收区域', React.createElement('span', { style: styles.formLabelReq }, '*')), + renderCascader(addFormData.transferProvince, addFormData.transferCity, function (v) { setAddFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferProvince = v; n.transferCity = ''; return n; }); }, function (v) { setAddFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferCity = v; return n; }); }, addAcceptCascaderOpen, setAddAcceptCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨方式', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('div', { style: styles.btnGroup }, + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btnGroupItem, addFormData.transferMethod === 'third_party' ? styles.btnGroupItemActive : {}), onClick: function () { handleAddFormChange('transferMethod', 'third_party'); } }, '第三方运输'), + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btnGroupItem, styles.btnGroupItemLast, addFormData.transferMethod === 'driver' ? styles.btnGroupItemActive : {}), onClick: function () { handleAddFormChange('transferMethod', 'driver'); } }, '司机驾驶') + ) + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, styles.formGridFull) }, + React.createElement('label', { style: styles.formLabel }, '调拨原因', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('textarea', { style: styles.textarea, value: addFormData.transferReason, onChange: function (e) { handleAddFormChange('transferReason', e.target.value); }, placeholder: '请输入调拨原因' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方名称', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, value: addFormData.carrierName, onChange: function (e) { handleAddFormChange('carrierName', e.target.value); }, placeholder: '请输入运输方名称或司机姓名' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方联系方式', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, value: addFormData.carrierContact, onChange: function (e) { handleAddFormChange('carrierContact', e.target.value); }, placeholder: '请输入运输方联系方式' }) + ) + ) + ), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.tableWrapAddPage }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车牌号', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车辆类型'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '品牌'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '型号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发停车场'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发里程(KM)', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发氢量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发电量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '操作') + ) + ), + React.createElement('tbody', {}, + addFormLines.map(function (row, rowIndex) { + var isPlateOpen = plateSelectOpenRowId === row.rowId; + var plateOptions = getAddPagePlateOptions(row.rowId); + var isEvenRow = rowIndex % 2 === 1; + return React.createElement('tr', { key: row.rowId, style: isEvenRow ? styles.trStripe : styles.trOdd }, + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('div', { style: styles.autocompleteWrap }, + React.createElement('input', { + style: Object.assign({}, styles.input, { minWidth: '120px' }), + placeholder: '请选择', + value: isPlateOpen ? plateSearchKeyword : (row.plateNo || ''), + onChange: function (e) { setPlateSearchKeyword(e.target.value); setPlateSelectOpenRowId(row.rowId); }, + onFocus: function () { setPlateSelectOpenRowId(row.rowId); setPlateSearchKeyword(row.plateNo || ''); }, + onBlur: function () { setTimeout(function () { setPlateSelectOpenRowId(null); }, 200); } + }), + isPlateOpen && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 1049 }, onClick: function () { setPlateSelectOpenRowId(null); } }), + React.createElement('div', { style: Object.assign({}, styles.autocompletePanel, styles.autocompletePanelOverflow), onClick: function (e) { e.stopPropagation(); } }, + plateOptions.length === 0 + ? React.createElement('div', { style: { padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral6 } }, '无匹配的库存车辆') + : plateOptions.map(function (v) { + return React.createElement('div', { + key: v.id, + style: styles.autocompleteOption, + onMouseDown: function (e) { e.preventDefault(); }, + onClick: function () { + updateAddLine(row.rowId, { vehicleId: v.id, plateNo: v.plateNo, vehicleType: v.vehicleType || '', brand: v.brand || '', model: v.model || '', departParking: v.departParking || '', departH2Unit: v.h2Unit || '' }); + setPlateSelectOpenRowId(null); + setPlateSearchKeyword(''); + } + }, v.plateNo); + }) + ) + ) + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.vehicleType || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.brand || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.model || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.departParking || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: row.departMileage, onChange: function (e) { updateAddLine(row.rowId, { departMileage: e.target.value }); }, placeholder: '' }) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: row.departH2, onChange: function (e) { updateAddLine(row.rowId, { departH2: e.target.value }); }, placeholder: '' }), + row.departH2Unit ? React.createElement('span', { style: { marginLeft: '4px', color: t.neutral6, fontSize: t.fontSize14 } }, row.departH2Unit) : null + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: row.departElec, onChange: function (e) { updateAddLine(row.rowId, { departElec: e.target.value }); }, placeholder: '' }), + React.createElement('span', { style: { marginLeft: '4px', color: t.neutral6, fontSize: t.fontSize14 } }, 'kWh') + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('a', { href: '#', style: styles.actionLink, onClick: function (e) { + e.preventDefault(); + if (addFormLines.length <= 1) { setToastMessage('至少保留一行'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + setAddFormLines(addFormLines.filter(function (l) { return l.rowId !== row.rowId; })); + } }, '删除') + ) + ); + }) + ) + ) + ), + React.createElement('div', { style: { marginTop: t.spacing12 } }, + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setAddFormLines(addFormLines.concat([createEmptyAddLine()])); } }, '新增一行') + ), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: handleAddCancel }, '取消'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: handleAddSave }, '保存'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: handleAddSubmit }, '提交审核') + ) + ) + ); + } + + function renderEditPage() { + var grid = styles.formGrid; + return React.createElement('div', {}, + React.createElement('div', { style: Object.assign({}, styles.card, { marginBottom: t.spacing16 }) }, + React.createElement('div', { style: grid }, + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨日期', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, type: 'date', value: formData.transferDate, onChange: function (e) { handleFormChange('transferDate', e.target.value); } }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '出发区域', React.createElement('span', { style: styles.formLabelReq }, '*')), + renderCascader(formData.departProvince, formData.departCity, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departProvince = v; n.departCity = ''; return n; }); }, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departCity = v; return n; }); }, formDepartCascaderOpen, setFormDepartCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收区域', React.createElement('span', { style: styles.formLabelReq }, '*')), + renderCascader(formData.transferProvince, formData.transferCity, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferProvince = v; n.transferCity = ''; return n; }); }, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferCity = v; return n; }); }, formTransferCascaderOpen, setFormTransferCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨方式', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('div', { style: styles.btnGroup }, + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btnGroupItem, formData.transferMethod === 'third_party' ? styles.btnGroupItemActive : {}), onClick: function () { handleFormChange('transferMethod', 'third_party'); } }, '第三方运输'), + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btnGroupItem, styles.btnGroupItemLast, formData.transferMethod === 'driver' ? styles.btnGroupItemActive : {}), onClick: function () { handleFormChange('transferMethod', 'driver'); } }, '司机驾驶') + ) + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, styles.formGridFull) }, + React.createElement('label', { style: styles.formLabel }, '调拨原因', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('textarea', { style: styles.textarea, value: formData.transferReason, onChange: function (e) { handleFormChange('transferReason', e.target.value); }, placeholder: '请输入调拨原因' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方名称', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, value: formData.carrierName, onChange: function (e) { handleFormChange('carrierName', e.target.value); }, placeholder: '请输入运输方名称或司机姓名' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方联系方式', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, value: formData.carrierContact, onChange: function (e) { handleFormChange('carrierContact', e.target.value); }, placeholder: '请输入运输方联系方式' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收日期', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, type: 'date', value: formData.receiveDate, onChange: function (e) { handleFormChange('receiveDate', e.target.value); } }) + ) + ) + ), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.tableWrapAddPage }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车牌号', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车辆类型'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '品牌'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '型号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发停车场'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发里程(KM)', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收里程(KM)'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发氢量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收氢量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发电量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收电量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '操作') + ) + ), + React.createElement('tbody', {}, + editFormLines.map(function (row, rowIndex) { + var isPlateOpen = editPlateSelectOpenRowId === row.rowId; + var plateOptions = getEditPagePlateOptions(row.rowId); + var isEvenRow = rowIndex % 2 === 1; + return React.createElement('tr', { key: row.rowId, style: isEvenRow ? styles.trStripe : styles.trOdd }, + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('div', { style: styles.autocompleteWrap }, + React.createElement('input', { + style: Object.assign({}, styles.input, { minWidth: '120px' }), + placeholder: '请选择', + value: isPlateOpen ? editPlateSearchKeyword : (row.plateNo || ''), + onChange: function (e) { setEditPlateSearchKeyword(e.target.value); setEditPlateSelectOpenRowId(row.rowId); }, + onFocus: function () { setEditPlateSelectOpenRowId(row.rowId); setEditPlateSearchKeyword(row.plateNo || ''); }, + onBlur: function () { setTimeout(function () { setEditPlateSelectOpenRowId(null); }, 200); } + }), + isPlateOpen && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 1049 }, onClick: function () { setEditPlateSelectOpenRowId(null); } }), + React.createElement('div', { style: Object.assign({}, styles.autocompletePanel, styles.autocompletePanelOverflow), onClick: function (e) { e.stopPropagation(); } }, + plateOptions.length === 0 + ? React.createElement('div', { style: { padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral6 } }, '无匹配的库存车辆') + : plateOptions.map(function (v) { + return React.createElement('div', { + key: v.id, + style: styles.autocompleteOption, + onMouseDown: function (e) { e.preventDefault(); }, + onClick: function () { + updateEditLine(row.rowId, { vehicleId: v.id, plateNo: v.plateNo, vehicleType: v.vehicleType || '', brand: v.brand || '', model: v.model || '', departParking: v.departParking || '', departH2Unit: v.h2Unit || '' }); + setEditPlateSelectOpenRowId(null); + setEditPlateSearchKeyword(''); + } + }, v.plateNo); + }) + ) + ) + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.vehicleType || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.brand || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.model || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, row.departParking || '-'), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: row.departMileage, onChange: function (e) { updateEditLine(row.rowId, { departMileage: e.target.value }); }, placeholder: '' }) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: row.receiveMileage, onChange: function (e) { updateEditLine(row.rowId, { receiveMileage: e.target.value }); }, placeholder: '' }) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: row.departH2, onChange: function (e) { updateEditLine(row.rowId, { departH2: e.target.value }); }, placeholder: '' }), + row.departH2Unit ? React.createElement('span', { style: { marginLeft: '4px', color: t.neutral6, fontSize: t.fontSize14 } }, row.departH2Unit) : null + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: row.receiveH2, onChange: function (e) { updateEditLine(row.rowId, { receiveH2: e.target.value }); }, placeholder: '' }), + row.departH2Unit ? React.createElement('span', { style: { marginLeft: '4px', color: t.neutral6, fontSize: t.fontSize14 } }, row.departH2Unit) : null + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center' } }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: row.departElec, onChange: function (e) { updateEditLine(row.rowId, { departElec: e.target.value }); }, placeholder: '' }), + React.createElement('span', { style: { marginLeft: '4px', color: t.neutral6, fontSize: t.fontSize14 } }, 'kWh') + ) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: row.receiveElec, onChange: function (e) { updateEditLine(row.rowId, { receiveElec: e.target.value }); }, placeholder: '' }) + ), + React.createElement('td', { style: Object.assign({}, styles.td, styles.tdNowrap) }, + React.createElement('a', { href: '#', style: styles.actionLink, onClick: function (e) { + e.preventDefault(); + if (editFormLines.length <= 1) { setToastMessage('至少保留一行'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + setEditFormLines(editFormLines.filter(function (l) { return l.rowId !== row.rowId; })); + } }, '删除') + ) + ); + }) + ) + ) + ), + React.createElement('div', { style: { marginTop: t.spacing12 } }, + React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setEditFormLines(editFormLines.concat([createEmptyEditLine()])); } }, '新增一行') + ), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setCurrentView('list'); setEditRecord(null); setFormData(defaultForm); setEditFormLines([]); setEditPlateSelectOpenRowId(null); setEditPlateSearchKeyword(''); } }, '取消'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: handleEditSave }, '保存'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function () { handleEditSave(); } }, '提交') + ) + ) + ); + } + + // 查看页:与接收记录页布局、字段一致,全部只读禁用(调拨信息 + 接收日期 + 明细表) + function renderViewPage(record) { + if (!record) return null; + var lines = record.lines || []; + return React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.modalTitle }, '查看调拨'), + renderFormContent(true, true), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收日期'), + React.createElement('span', { style: { fontSize: t.fontSize14, color: t.neutral8 } }, record.receiveDate || '-') + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, { marginTop: t.spacing24 }) }, + React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + 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 }, '出发里程(KM)'), + React.createElement('th', { style: styles.th }, '接收里程(KM)'), + React.createElement('th', { style: styles.th }, '出发氢量'), + React.createElement('th', { style: styles.th }, '接收氢量'), + React.createElement('th', { style: styles.th }, '出发电量'), + React.createElement('th', { style: styles.th }, '接收电量(kWh)') + ) + ), + React.createElement('tbody', {}, + lines.map(function (line, idx) { + var isEvenRow = idx % 2 === 1; + return React.createElement('tr', { key: idx, style: isEvenRow ? styles.trStripe : styles.trOdd }, + React.createElement('td', { style: styles.td }, line.plateNo || '-'), + React.createElement('td', { style: styles.td }, line.vehicleType || '-'), + React.createElement('td', { style: styles.td }, line.brand || '-'), + React.createElement('td', { style: styles.td }, line.model || '-'), + React.createElement('td', { style: styles.td }, line.departParking || '-'), + React.createElement('td', { style: styles.td }, line.departMileage != null ? line.departMileage : '-'), + React.createElement('td', { style: styles.td }, line.receiveMileage != null ? line.receiveMileage : '-'), + React.createElement('td', { style: styles.td }, formatH2(line.departH2, line.departH2Unit)), + React.createElement('td', { style: styles.td }, formatH2(line.receiveH2, line.departH2Unit)), + React.createElement('td', { style: styles.td }, line.departElec != null ? line.departElec + ' kWh' : '-'), + React.createElement('td', { style: styles.td }, line.receiveElec != null ? line.receiveElec + ' kWh' : '-') + ); + }) + ) + ) + ) + ), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setViewRecord(null); setCurrentView('list'); } }, '返回') + ) + ); + } + + function validateEditForm() { + if (!formData.transferDate) { + setToastMessage('请选择调拨日期'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.departProvince || !formData.departCity) { + setToastMessage('请选择出发区域'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.transferProvince || !formData.transferCity) { + setToastMessage('请选择接收区域'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.transferMethod) { + setToastMessage('请选择调拨方式'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.transferReason || !formData.transferReason.trim()) { + setToastMessage('请填写调拨原因'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.carrierName || !formData.carrierName.trim()) { + setToastMessage('请填写运输方名称'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.carrierContact || !formData.carrierContact.trim()) { + setToastMessage('请填写运输方联系方式'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (!formData.receiveDate) { + setToastMessage('请选择接收日期'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + var validLines = editFormLines.filter(function (row) { return row.plateNo; }); + if (validLines.length === 0) { + setToastMessage('请至少保留一行并选择车牌号'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + for (var i = 0; i < editFormLines.length; i++) { + var row = editFormLines[i]; + if (!row.plateNo) continue; + var rowNum = i + 1; + if (row.departMileage === '' || row.departMileage === undefined || row.departMileage === null) { + setToastMessage('请填写第' + rowNum + '行出发里程(KM)'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.departH2 === '' || row.departH2 === undefined || row.departH2 === null) { + setToastMessage('请填写第' + rowNum + '行出发氢量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.departElec === '' || row.departElec === undefined || row.departElec === null) { + setToastMessage('请填写第' + rowNum + '行出发电量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.receiveH2 === '' || row.receiveH2 === undefined || row.receiveH2 === null) { + setToastMessage('请填写第' + rowNum + '行接收氢量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + if (row.receiveElec === '' || row.receiveElec === undefined || row.receiveElec === null) { + setToastMessage('请填写第' + rowNum + '行接收电量'); + setTimeout(function () { setToastMessage(''); }, 2000); + return false; + } + } + return true; + } + + function handleEditSave() { + if (!editRecord) return; + if (!validateEditForm()) return; + var dp = provinceList.find(function (x) { return x.code === formData.departProvince; }); + var dc = (cityMap[formData.departProvince] || []).find(function (x) { return x.code === formData.departCity; }); + var tp = provinceList.find(function (x) { return x.code === formData.transferProvince; }); + var tc = (cityMap[formData.transferProvince] || []).find(function (x) { return x.code === formData.transferCity; }); + var validRows = editFormLines.filter(function (row) { return row.plateNo; }); + var lines = validRows.map(function (row) { + var existing = (editRecord.lines || []).find(function (l) { return l.plateNo === row.plateNo; }); + return { + plateNo: row.plateNo, + vehicleType: row.vehicleType || '-', + brand: row.brand || '-', + model: row.model || '-', + departParking: row.departParking || '-', + departMileage: row.departMileage != null && row.departMileage !== '' ? Number(row.departMileage) : null, + departH2: row.departH2 != null && row.departH2 !== '' ? row.departH2 : '', + departH2Unit: row.departH2Unit || '', + departElec: row.departElec != null && row.departElec !== '' ? Number(row.departElec) : null, + receiveMileage: row.receiveMileage !== '' && row.receiveMileage !== undefined ? Number(row.receiveMileage) : (existing && existing.receiveMileage), + receiveH2: row.receiveH2 != null && row.receiveH2 !== '' ? row.receiveH2 : (existing && existing.receiveH2), + receiveElec: row.receiveElec !== '' && row.receiveElec !== undefined ? Number(row.receiveElec) : (existing && existing.receiveElec), + receiveParking: existing && existing.receiveParking, + receiverName: existing && existing.receiverName + }; + }); + var updated = { + id: editRecord.id, + transferDate: formData.transferDate, + operatorId: editRecord.operatorId, + operatorName: editRecord.operatorName, + departProvince: formData.departProvince, + departCity: formData.departCity, + departLabel: (dp ? dp.name : '') + '-' + (dc ? dc.name : ''), + transferProvince: formData.transferProvince, + transferCity: formData.transferCity, + transferLabel: (tp ? tp.name : '') + '-' + (tc ? tc.name : ''), + transferMethod: formData.transferMethod, + transferReason: formData.transferReason || '', + carrierName: formData.carrierName || '', + carrierContact: formData.carrierContact || '', + approvalStatus: editRecord.approvalStatus, + hasReceiveRecord: editRecord.hasReceiveRecord, + receiveDate: formData.receiveDate, + receiverId: editRecord.receiverId, + receiverName: editRecord.receiverName, + createTime: editRecord.createTime, + lines: lines + }; + setMockTransferList(mockTransferList.map(function (r) { return r.id === editRecord.id ? updated : r; })); + setToastMessage('保存成功'); + setTimeout(function () { setToastMessage(''); }, 2000); + setEditRecord(null); + setCurrentView('list'); + setFormData(defaultForm); + setEditFormLines([]); + setEditPlateSelectOpenRowId(null); + setEditPlateSearchKeyword(''); + } + + function handleWithdraw(transfer) { + if (!transfer) return; + setMockTransferList(mockTransferList.map(function (r) { + if (r.id === transfer.id) { + var next = {}; + for (var k in r) next[k] = r[k]; + next.approvalStatus = 'withdrawn'; + return next; + } + return r; + })); + setWithdrawConfirmRecord(null); + setToastMessage('撤回成功'); + setTimeout(function () { setToastMessage(''); }, 2000); + } + + function setFormDataFromTransfer(transfer) { + if (!transfer || !transfer.lines) return; + var plateNos = transfer.lines.map(function (l) { return l.plateNo; }); + var selectedIds = mockStockVehicles.filter(function (v) { return plateNos.indexOf(v.plateNo) >= 0; }).map(function (v) { return v.id; }); + setFormData({ + transferDate: transfer.transferDate, + operatorId: transfer.operatorId, + operatorName: transfer.operatorName, + selectedVehicleIds: selectedIds, + transferMethod: transfer.transferMethod, + transferReason: transfer.transferReason || '', + departProvince: transfer.departProvince || '', + departCity: transfer.departCity || '', + transferProvince: transfer.transferProvince || '', + transferCity: transfer.transferCity || '', + carrierName: transfer.carrierName || '', + carrierContact: transfer.carrierContact || '' + }); + } + + function openEdit(transfer) { + if (!transfer || !transfer.lines) return; + setEditRecord(transfer); + setFormData({ + transferDate: transfer.transferDate, + receiveDate: transfer.receiveDate || '', + operatorId: transfer.operatorId, + operatorName: transfer.operatorName, + selectedVehicleIds: [], + transferMethod: transfer.transferMethod, + transferReason: transfer.transferReason || '', + departProvince: transfer.departProvince || '', + departCity: transfer.departCity || '', + transferProvince: transfer.transferProvince || '', + transferCity: transfer.transferCity || '', + carrierName: transfer.carrierName || '', + carrierContact: transfer.carrierContact || '' + }); + setEditFormLines(buildEditFormLinesFromRecord(transfer)); + setEditPlateSelectOpenRowId(null); + setEditPlateSearchKeyword(''); + setCurrentView('edit'); + } + + var t = ARCO_TOKEN; + var styles = { + page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' }, + breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, + breadcrumbLeft: { display: 'flex', alignItems: 'center' }, + breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: t.spacing8 }, + breadcrumbCurrent: { color: t.neutral8 }, + breadcrumbRight: { display: 'flex', alignItems: 'center' }, + requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' }, + modalCloseBtn: { position: 'absolute', right: t.spacing16, top: t.spacing16, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', borderRadius: t.radiusMedium, backgroundColor: 'transparent', border: 'none', color: t.neutral6, fontSize: '18px', lineHeight: '1', padding: 0 }, + requirementSection: { marginBottom: t.spacing16 }, + requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: t.spacing8 }, + requirementItem: { marginBottom: t.spacing8, paddingLeft: t.spacing16, fontSize: t.fontSize14, color: t.neutral7 }, + requirementSubItem: { marginBottom: t.spacing4, paddingLeft: t.spacing24, fontSize: t.fontSize14, color: t.neutral7 }, + card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing24 + ' 20px' }, + searchPanel: { paddingBottom: t.spacing24, marginBottom: t.spacing24, borderBottom: '1px solid ' + t.neutral4 }, + searchPanelInner: { display: 'flex', alignItems: 'flex-start', gap: t.spacing16 }, + searchGridWrap: { flex: 1, minWidth: 0 }, + searchGrid: { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: t.spacing16 + ' ' + t.spacing24 }, + searchField: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '20px', minWidth: 0 }, + searchFieldControl: { flex: 1, minWidth: 0, overflow: 'hidden' }, + searchFieldLabel: { fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap', flexShrink: 0 }, + searchDivider: { width: '1px', backgroundColor: t.neutral4, alignSelf: 'stretch', flexShrink: 0 }, + searchActions: { display: 'flex', flexDirection: 'column', gap: t.spacing8, justifyContent: 'center', flexShrink: 0 }, + filterGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: t.spacing16 }, + filterItem: { display: 'flex', alignItems: 'center', gap: t.spacing8, flexWrap: 'wrap' }, + filterItemFull: { gridColumn: '1 / -1', display: 'flex', justifyContent: 'flex-end', gap: t.spacing8 }, + label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap' }, + filterLabel: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap', minWidth: '80px', textAlign: 'right' }, + input: { padding: '0 10px', height: '32px', width: '100%', minWidth: '140px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily, boxSizing: 'border-box', outline: 'none', transition: 'border-color 0.2s, box-shadow 0.2s', textAlign: 'left' }, + select: { padding: '0 32px 0 12px', height: '32px', minWidth: '120px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, backgroundColor: t.neutral1, cursor: 'pointer', outline: 'none', appearance: 'none', WebkitAppearance: 'none', backgroundImage: 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'12\' height=\'12\' viewBox=\'0 0 48 48\'%3E%3Cpath fill=\'%2386909C\' d=\'M24 32l-12-12h24z\'/%3E%3C/svg%3E")', backgroundRepeat: 'no-repeat', backgroundPosition: 'right 20px center', transition: 'border-color 0.2s, box-shadow 0.2s', textAlign: 'left' }, + btn: { padding: t.spacing8 + ' ' + t.spacing16, borderRadius: t.radiusMedium, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '6px' }, + btnFixed: { height: '32px', padding: '0 16px' }, + btnPrimary: { backgroundColor: t.primary, color: t.neutral1, border: 'none' }, + btnDefault: { backgroundColor: t.neutral1, color: t.neutral8, border: '1px solid ' + t.border }, + btnOutline: { backgroundColor: t.neutral1, color: t.primary, border: '1px solid ' + t.primary }, + toolbar: { display: 'flex', justifyContent: 'flex-end', marginBottom: t.spacing16 }, + tabs: { display: 'flex', borderBottom: '1px solid ' + t.border, marginBottom: t.spacing16 }, + tab: { padding: '10px 16px', cursor: 'pointer', fontSize: t.fontSize14, color: t.neutral7 }, + tabActive: { color: t.primary, borderBottom: '2px solid ' + t.primary, marginBottom: '-1px' }, + tableWrap: { overflowX: 'auto', border: '1px solid ' + t.neutral4, borderRadius: t.radiusMedium }, + tableWrapAddPage: { overflow: 'visible', border: '1px solid ' + t.neutral4, borderRadius: t.radiusMedium }, + table: { width: '100%', borderCollapse: 'collapse', fontSize: t.fontSize14 }, + th: { textAlign: 'left', padding: '0 16px', height: '40px', boxSizing: 'border-box', backgroundColor: t.fillSecondary, borderBottom: '1px solid ' + t.neutral4, fontWeight: 600, color: t.neutral8, verticalAlign: 'middle' }, + thNowrap: { whiteSpace: 'nowrap' }, + thStickyRight: { position: 'sticky', right: 0, backgroundColor: t.fillSecondary, boxShadow: '-2px 0 4px rgba(0,0,0,0.06)', zIndex: 1 }, + tdStickyRight: { position: 'sticky', right: 0, backgroundColor: t.neutral1, boxShadow: '-2px 0 4px rgba(0,0,0,0.06)', zIndex: 1 }, + tdStickyRightStripe: { position: 'sticky', right: 0, backgroundColor: t.fillSecondary, boxShadow: '-2px 0 4px rgba(0,0,0,0.06)', zIndex: 1 }, + td: { padding: '0 16px', height: '42px', boxSizing: 'border-box', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8, verticalAlign: 'middle' }, + tdNowrap: { whiteSpace: 'nowrap' }, + trStripe: { backgroundColor: t.fillSecondary }, + trOdd: { backgroundColor: t.neutral1 }, + actionLink: { color: t.link, cursor: 'pointer', marginRight: t.spacing12, fontSize: t.fontSize14 }, + pagination: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px', borderTop: '1px solid ' + t.neutral4 }, + paginationLeft: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: t.fontSize14, color: t.neutral7 }, + paginationRight: { display: 'flex', alignItems: 'center', gap: '8px' }, + paginationSelect: { padding: '4px 8px', height: '28px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily }, + paginationBtn: { minWidth: '28px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, backgroundColor: t.neutral1, cursor: 'pointer', fontSize: t.fontSize14 }, + paginationBtnActive: { backgroundColor: t.primary, color: t.neutral1, borderColor: t.primary }, + paginationBtnDisabled: { opacity: 0.5, cursor: 'not-allowed' }, + cascaderWrap: { position: 'relative', width: '100%', minWidth: 0 }, + cascaderInput: { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', width: '100%', height: '32px', padding: '0 32px 0 12px', borderRadius: '2px', border: '1px solid ' + t.border, cursor: 'pointer', fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, backgroundColor: t.neutral1, outline: 'none', transition: 'border-color 0.2s, box-shadow 0.2s', boxSizing: 'border-box', textAlign: 'left', backgroundImage: 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'12\' height=\'12\' viewBox=\'0 0 48 48\'%3E%3Cpath fill=\'%2386909C\' d=\'M24 32l-12-12h24z\'/%3E%3C/svg%3E")', backgroundRepeat: 'no-repeat', backgroundPosition: 'right 20px center' }, + cascaderPanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, border: '1px solid ' + t.border, borderRadius: '2px', boxShadow: '0 4px 10px rgba(0,0,0,0.08)', zIndex: 1050, display: 'flex', minWidth: '240px', maxHeight: '256px' }, + cascaderColumn: { minWidth: '100px', maxHeight: '256px', overflowY: 'auto', borderRight: '1px solid ' + t.neutral4 }, + cascaderColumnLast: { borderRight: 'none' }, + cascaderOption: { padding: '8px 12px', fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, cursor: 'pointer', transition: 'background-color 0.1s' }, + cascaderOptionHover: { backgroundColor: t.neutral2 }, + autocompleteWrap: { position: 'relative', minWidth: '140px' }, + autocompleteWrapFull: { width: '100%', minWidth: 0, maxWidth: '100%' }, + autocompleteTrigger: { padding: '0 32px 0 12px', height: '32px', minWidth: '140px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, backgroundColor: t.neutral1, boxSizing: 'border-box', outline: 'none', transition: 'border-color 0.2s, box-shadow 0.2s', textAlign: 'left', backgroundImage: 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'12\' height=\'12\' viewBox=\'0 0 48 48\'%3E%3Cpath fill=\'%2386909C\' d=\'M24 32l-12-12h24z\'/%3E%3C/svg%3E")', backgroundRepeat: 'no-repeat', backgroundPosition: 'right 20px center' }, + autocompleteTriggerFull: { width: '100%', minWidth: 0, maxWidth: '100%' }, + autocompletePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, border: '1px solid ' + t.border, borderRadius: '2px', boxShadow: '0 4px 10px rgba(0,0,0,0.08)', zIndex: 100, minWidth: '100%', maxHeight: '256px', overflowY: 'auto', padding: '4px 0' }, + autocompletePanelOverflow: { zIndex: 1050 }, + autocompleteOption: { padding: '8px 12px', fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, cursor: 'pointer', transition: 'background-color 0.1s' }, + dateRangeWrap: { position: 'relative', width: '100%' }, + dateRangeTrigger: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', height: '32px', padding: '0 10px 0 12px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily, color: t.neutral8, backgroundColor: t.neutral1, cursor: 'pointer', outline: 'none', transition: 'border-color 0.2s, box-shadow 0.2s', boxSizing: 'border-box', textAlign: 'left' }, + dateRangeTriggerFocus: { borderColor: t.primary, boxShadow: '0 0 0 2px rgba(22,93,255,0.2)' }, + dateRangeDropdown: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, border: '1px solid ' + t.border, borderRadius: '4px', boxShadow: '0 4px 10px rgba(0,0,0,0.12)', zIndex: 1050, padding: '12px 16px', minWidth: '480px' }, + dateRangeCalendars: { display: 'flex', gap: '24px' }, + dateRangeCalendar: { minWidth: '200px' }, + dateRangeCalendarHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px' }, + dateRangeCalendarNav: { display: 'flex', alignItems: 'center', gap: '4px' }, + dateRangeCalendarNavBtn: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none', borderRadius: '2px', backgroundColor: 'transparent', color: t.neutral7, cursor: 'pointer', fontSize: '14px' }, + dateRangeCalendarTitle: { fontSize: t.fontSize14, fontWeight: 600, color: t.neutral8 }, + dateRangeCalendarWeekdays: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px', marginBottom: '4px', fontSize: '12px', color: t.neutral6 }, + dateRangeCalendarGrid: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px' }, + dateRangeDay: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '13px', color: t.neutral8, cursor: 'pointer', borderRadius: '2px' }, + dateRangeDayOther: { color: t.neutral5 }, + dateRangeDaySelected: { backgroundColor: t.primary, color: t.neutral1 }, + dateRangeDayInRange: { backgroundColor: 'rgba(22,93,255,0.15)', color: t.primary }, + dateRangeDayToday: { position: 'relative', fontWeight: 600 }, + formRow: { marginBottom: t.spacing16 }, + formLabel: { display: 'block', marginBottom: '6px', fontSize: t.fontSize14, color: t.neutral8 }, + formLabelReq: { color: t.danger, marginLeft: '2px' }, + formGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: t.spacing24, maxWidth: '100%' }, + formGridFull: { gridColumn: '1 / -1' }, + textarea: { width: '100%', minHeight: '80px', padding: '8px 10px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, fontFamily: t.fontFamily, resize: 'vertical', boxSizing: 'border-box' }, + radioGroup: { display: 'flex', gap: t.spacing16, alignItems: 'center' }, + radioItem: { display: 'flex', alignItems: 'center', cursor: 'pointer' }, + btnGroup: { display: 'inline-flex', border: '1px solid ' + t.border, borderRadius: t.radiusMedium, overflow: 'hidden' }, + btnGroupItem: { padding: '0 16px', height: '32px', border: 'none', borderRight: '1px solid ' + t.border, cursor: 'pointer', fontSize: t.fontSize14, backgroundColor: t.neutral1, color: t.neutral7 }, + btnGroupItemLast: { borderRight: 'none' }, + btnGroupItemActive: { backgroundColor: t.primary, color: t.neutral1 }, + transferBox: { display: 'flex', alignItems: 'stretch', gap: t.spacing12, minHeight: '240px', border: '1px solid ' + t.border, borderRadius: t.radiusMedium, padding: t.spacing12 }, + transferColumn: { flex: 1, border: '1px solid ' + t.neutral4, borderRadius: t.radiusSmall, overflow: 'hidden', display: 'flex', flexDirection: 'column' }, + transferColumnHead: { padding: '8px 12px', backgroundColor: t.fillSecondary, fontSize: t.fontSize14, fontWeight: 600 }, + transferColumnList: { flex: 1, overflowY: 'auto', padding: '4px' }, + transferItem: { padding: '8px 12px', fontSize: t.fontSize14, cursor: 'pointer', borderRadius: t.radiusSmall }, + transferItemSelected: { backgroundColor: t.primary, color: t.neutral1 }, + transferBtns: { display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: t.spacing8 }, + stepBar: { display: 'flex', alignItems: 'center', marginBottom: t.spacing24, padding: t.spacing16, backgroundColor: t.fillSecondary, borderRadius: t.radiusMedium }, + stepItem: { display: 'flex', alignItems: 'center', flex: 1 }, + stepDot: { width: '24px', height: '24px', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', marginRight: '8px' }, + stepDotDone: { backgroundColor: t.success, color: t.neutral1 }, + stepDotCurrent: { backgroundColor: t.primary, color: t.neutral1 }, + stepDotPending: { backgroundColor: t.neutral4, color: t.neutral6 }, + modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, + modalBox: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, maxWidth: '90%', maxHeight: '90%', overflow: 'auto', padding: t.spacing24, minWidth: '600px', position: 'relative' }, + modalTitle: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing16 }, + modalFooter: { marginTop: t.spacing24, display: 'flex', justifyContent: 'flex-end', gap: t.spacing8 }, + toast: { position: 'fixed', top: t.spacing24, left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.75)', color: t.neutral1, padding: '10px 20px', borderRadius: t.radiusMedium, zIndex: 2000, fontSize: t.fontSize14 } + }; + + // 列表运输方名称、运输方联系方式列样式(支持长文本换行) + var carrierInfoTdStyle = Object.assign({}, styles.td, { whiteSpace: 'normal', verticalAlign: 'middle', minWidth: '100px', wordBreak: 'break-word', overflowWrap: 'break-word' }); + + // 穿梭框:左侧可选车辆 = 库存中未在 formData.selectedVehicleIds 的 + var leftVehicles = mockStockVehicles.filter(function (v) { return formData.selectedVehicleIds.indexOf(v.id) < 0; }); + var rightVehicles = mockStockVehicles.filter(function (v) { return formData.selectedVehicleIds.indexOf(v.id) >= 0; }); + + function renderFilterSelect(options, value, onChange, open, setOpen, placeholder, filterFn, fullWidth) { + var list = filterFn ? options.filter(filterFn) : options; + var wrapStyle = fullWidth ? Object.assign({}, styles.autocompleteWrap, styles.autocompleteWrapFull) : styles.autocompleteWrap; + var triggerBase = fullWidth ? Object.assign({}, styles.autocompleteTrigger, styles.autocompleteTriggerFull) : styles.autocompleteTrigger; + var triggerStyle = Object.assign({}, triggerBase, open ? { borderColor: t.primary, boxShadow: '0 0 0 2px rgba(22,93,255,0.2)' } : {}); + return React.createElement('div', { style: wrapStyle }, + React.createElement('input', { + style: triggerStyle, + className: 'arco-select-trigger', + placeholder: placeholder, + value: value, + onChange: function (e) { onChange(e.target.value); }, + onFocus: function () { setOpen(true); }, + onBlur: function () { setTimeout(function () { setOpen(false); }, 200); } + }), + open && list.length > 0 && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 99 }, onClick: function () { setOpen(false); } }), + React.createElement('div', { style: styles.autocompletePanel, onClick: function (e) { e.stopPropagation(); } }, + list.map(function (opt) { + var label = typeof opt === 'string' ? opt : (opt.label || opt.name || opt.plateNo); + var val = typeof opt === 'string' ? opt : (opt.value || opt.id || opt.plateNo); + return React.createElement('div', { + key: val, + style: styles.autocompleteOption, + className: 'arco-select-option', + onMouseDown: function (e) { e.preventDefault(); }, + onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.neutral2; }, + onMouseLeave: function (e) { e.currentTarget.style.backgroundColor = 'transparent'; }, + onClick: function () { onChange(typeof opt === 'string' ? opt : (opt.name || opt.plateNo || val)); setOpen(false); } + }, label); + }) + ) + ) + ); + } + + function getCalendarWeeks(year, month) { + var d = new Date(year, month - 1, 1); + var firstDay = d.getDay(); + var daysInMonth = new Date(year, month, 0).getDate(); + var weeks = []; + var week = []; + for (var i = 0; i < firstDay; i++) { + var prev = new Date(year, month - 1, -firstDay + i + 1); + week.push({ date: prev.getDate(), yyyymmdd: prev.getFullYear() + '-' + String(prev.getMonth() + 1).padStart(2, '0') + '-' + String(prev.getDate()).padStart(2, '0'), isCurrentMonth: false }); + } + for (var j = 1; j <= daysInMonth; j++) { + week.push({ date: j, yyyymmdd: year + '-' + String(month).padStart(2, '0') + '-' + String(j).padStart(2, '0'), isCurrentMonth: true }); + if (week.length === 7) { weeks.push(week); week = []; } + } + if (week.length) { + var nextMonth = month === 12 ? 1 : month + 1; + var nextYear = month === 12 ? year + 1 : year; + for (var k = week.length; k < 7; k++) { + var dayNum = k - week.length + 1; + week.push({ date: dayNum, yyyymmdd: nextYear + '-' + String(nextMonth).padStart(2, '0') + '-' + String(dayNum).padStart(2, '0'), isCurrentMonth: false }); + } + weeks.push(week); + } + return weeks; + } + function renderDateRangePicker(startValue, endValue, setStart, setEnd, open, setOpen, viewLeft, viewRight, setViewLeft, setViewRight) { + var displayText = (startValue && endValue) ? (startValue + ' - ' + endValue) : (startValue ? (startValue + ' - 结束日期') : '开始日期 - 结束日期'); + var triggerStyle = Object.assign({}, styles.dateRangeTrigger, open ? styles.dateRangeTriggerFocus : {}); + var today = new Date(); + var todayStr = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0'); + var leftYM = viewLeft || getInitialViewMonth(startValue); + var rightYM = viewRight || (function () { + var p = leftYM.split('-'); + var y = parseInt(p[0], 10); + var m = parseInt(p[1], 10); + if (m === 12) return (y + 1) + '-01'; + return y + '-' + String(m + 1).padStart(2, '0'); + }()); + var leftParts = leftYM.split('-'); + var rightParts = rightYM.split('-'); + var leftWeeks = getCalendarWeeks(parseInt(leftParts[0], 10), parseInt(leftParts[1], 10)); + var rightWeeks = getCalendarWeeks(parseInt(rightParts[0], 10), parseInt(rightParts[1], 10)); + function goPrevMonth(isLeftCalendar) { + var ym = isLeftCalendar ? leftYM : rightYM; + var p = ym.split('-'); + var y = parseInt(p[0], 10); + var m = parseInt(p[1], 10); + if (m === 1) { y--; m = 12; } else m--; + var next = y + '-' + String(m).padStart(2, '0'); + if (isLeftCalendar) { setViewLeft(next); setViewRight(m === 12 ? (y + 1) + '-01' : y + '-' + String(m + 1).padStart(2, '0')); } + else { setViewRight(next); setViewLeft(m === 1 ? (y - 1) + '-12' : y + '-' + String(m - 1).padStart(2, '0')); } + } + function goNextMonth(isLeftCalendar) { + var ym = isLeftCalendar ? leftYM : rightYM; + var p = ym.split('-'); + var y = parseInt(p[0], 10); + var m = parseInt(p[1], 10); + if (m === 12) { y++; m = 1; } else m++; + var next = y + '-' + String(m).padStart(2, '0'); + if (isLeftCalendar) { setViewLeft(next); setViewRight(m === 12 ? (y + 1) + '-01' : y + '-' + String(m + 1).padStart(2, '0')); } + else { setViewRight(next); setViewLeft(m === 1 ? (y - 1) + '-12' : y + '-' + String(m - 1).padStart(2, '0')); } + } + function onSelectDate(yyyymmdd) { + if (!startValue || (startValue && endValue)) { + setStart(yyyymmdd); + setEnd(''); + } else { + if (yyyymmdd < startValue) { setEnd(startValue); setStart(yyyymmdd); } + else setEnd(yyyymmdd); + } + } + function renderOneCalendar(weeks, isLeft) { + return React.createElement('div', { style: styles.dateRangeCalendar }, + React.createElement('div', { style: styles.dateRangeCalendarHeader }, + React.createElement('div', { style: styles.dateRangeCalendarNav }, + React.createElement('button', { type: 'button', style: styles.dateRangeCalendarNavBtn, onClick: function () { goPrevMonth(isLeft); } }, '\u00AB'), + React.createElement('button', { type: 'button', style: styles.dateRangeCalendarNavBtn, onClick: function () { goPrevMonth(isLeft); } }, '\u2039') + ), + React.createElement('span', { style: styles.dateRangeCalendarTitle }, (isLeft ? leftYM : rightYM).replace('-', '\u5E74') + '\u6708'), + React.createElement('div', { style: styles.dateRangeCalendarNav }, + React.createElement('button', { type: 'button', style: styles.dateRangeCalendarNavBtn, onClick: function () { goNextMonth(isLeft); } }, '\u203A'), + React.createElement('button', { type: 'button', style: styles.dateRangeCalendarNavBtn, onClick: function () { goNextMonth(isLeft); } }, '\u00BB') + ) + ), + React.createElement('div', { style: styles.dateRangeCalendarWeekdays }, ['日', '一', '二', '三', '四', '五', '六'].map(function (w) { return React.createElement('div', { key: w }, w); })), + React.createElement('div', { style: styles.dateRangeCalendarGrid }, + weeks.map(function (week, wi) { + return week.map(function (day, di) { + var isSelected = day.yyyymmdd === startValue || day.yyyymmdd === endValue; + var isInRange = startValue && endValue && day.yyyymmdd >= startValue && day.yyyymmdd <= endValue; + var isToday = day.yyyymmdd === todayStr; + var dayStyle = Object.assign({}, styles.dateRangeDay, !day.isCurrentMonth ? styles.dateRangeDayOther : {}, isSelected ? styles.dateRangeDaySelected : {}, isInRange && !isSelected ? styles.dateRangeDayInRange : {}, isToday ? styles.dateRangeDayToday : {}); + return React.createElement('div', { + key: wi + '-' + di, + style: dayStyle, + onClick: function () { onSelectDate(day.yyyymmdd); } + }, day.date); + }); + }) + ) + ); + } + return React.createElement('div', { style: styles.dateRangeWrap }, + React.createElement('div', { + style: triggerStyle, + onClick: function () { + if (!open) { + setViewLeft(getInitialViewMonth(startValue)); + var l = getInitialViewMonth(startValue); + var p = l.split('-'); + var y = parseInt(p[0], 10); + var m = parseInt(p[1], 10); + setViewRight(m === 12 ? (y + 1) + '-01' : y + '-' + String(m + 1).padStart(2, '0')); + } + setOpen(!open); + } + }, React.createElement('span', { style: { color: (startValue || endValue) ? t.neutral8 : t.neutral6 } }, displayText), React.createElement('span', { style: { width: '16px', height: '16px', flexShrink: 0, marginLeft: '8px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: t.neutral6 } }, React.createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 }, React.createElement('rect', { x: 3, y: 4, width: 18, height: 18, rx: 2 }), React.createElement('line', { x1: 16, y1: 2, x2: 16, y2: 6 }), React.createElement('line', { x1: 8, y1: 2, x2: 8, y2: 6 }), React.createElement('line', { x1: 3, y1: 10, x2: 21, y2: 10 })))), + open && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 1049 }, onClick: function () { setOpen(false); } }), + React.createElement('div', { style: styles.dateRangeDropdown, onClick: function (e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.dateRangeCalendars }, + renderOneCalendar(leftWeeks, true), + renderOneCalendar(rightWeeks, false) + ) + ) + ) + ); + } + + function renderCascader(provinceCode, cityCode, setProvince, setCity, open, setOpen) { + var p = provinceList.find(function (x) { return x.code === provinceCode; }); + var c = (cityMap[provinceCode] || []).find(function (x) { return x.code === cityCode; }); + var display = (p ? p.name : '') + (p && c ? ' - ' : '') + (c ? c.name : ''); + var cascaderTriggerStyle = Object.assign({}, styles.cascaderInput, open ? { borderColor: t.primary, boxShadow: '0 0 0 2px rgba(22,93,255,0.2)' } : {}); + return React.createElement('div', { style: styles.cascaderWrap }, + React.createElement('div', { + style: cascaderTriggerStyle, + className: 'arco-select-trigger', + onClick: function () { setOpen(!open); } + }, React.createElement('span', { style: { color: (provinceCode && cityCode) ? t.neutral8 : t.neutral6, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, display || '请选择省-市')), + open && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, zIndex: 1049 }, onClick: function () { setOpen(false); } }), + React.createElement('div', { style: styles.cascaderPanel, onClick: function (e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.cascaderColumn }, + provinceList.map(function (pr) { + return React.createElement('div', { + key: pr.code, + style: styles.cascaderOption, + className: 'arco-select-option', + onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.neutral2; }, + onMouseLeave: function (e) { e.currentTarget.style.backgroundColor = 'transparent'; }, + onClick: function () { setProvince(pr.code); setCity(''); } + }, pr.name); + }) + ), + React.createElement('div', { style: Object.assign({}, styles.cascaderColumn, styles.cascaderColumnLast) }, + (cityMap[provinceCode] || []).map(function (ci) { + return React.createElement('div', { + key: ci.code, + style: styles.cascaderOption, + className: 'arco-select-option', + onMouseEnter: function (e) { e.currentTarget.style.backgroundColor = t.neutral2; }, + onMouseLeave: function (e) { e.currentTarget.style.backgroundColor = 'transparent'; }, + onClick: function () { setCity(ci.code); setOpen(false); } + }, ci.name); + }) + ) + ) + ) + ); + } + + function renderFormContent(readonly, isView) { + var disabled = readonly === true; + var grid = styles.formGrid; + return React.createElement('div', { style: grid }, + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨日期', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, formData.transferDate) : React.createElement('input', { style: styles.input, type: 'date', value: formData.transferDate, onChange: function (e) { handleFormChange('transferDate', e.target.value); } }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '操作人', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, formData.operatorName) : renderFilterSelect(mockOpsUsers, formData.operatorName, function (v) { handleFormChange('operatorName', v); var u = mockOpsUsers.find(function (x) { return x.name === v; }); if (u) handleFormChange('operatorId', u.id); }, formOperatorOpen, setFormOperatorOpen, '支持模糊搜索', function (u) { return !formData.operatorName || (u.name && u.name.indexOf(formData.operatorName) >= 0); }) + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, styles.formGridFull) }, + React.createElement('label', { style: styles.formLabel }, '车牌号', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, (formData.selectedVehicleIds && mockStockVehicles.filter(function (v) { return formData.selectedVehicleIds.indexOf(v.id) >= 0; }).map(function (v) { return v.plateNo; }).join('、')) || '-') : React.createElement('div', { style: styles.transferBox }, + React.createElement('div', { style: styles.transferColumn }, + React.createElement('div', { style: styles.transferColumnHead }, '库存车辆'), + React.createElement('div', { style: styles.transferColumnList }, + leftVehicles.map(function (v) { + var selected = leftSelectedIds.indexOf(v.id) >= 0; + return React.createElement('div', { + key: v.id, + style: Object.assign({}, styles.transferItem, selected ? styles.transferItemSelected : {}), + onClick: function () { + if (selected) setLeftSelectedIds(leftSelectedIds.filter(function (id) { return id !== v.id; })); + else setLeftSelectedIds(leftSelectedIds.concat([v.id])); + } + }, v.plateNo); + }) + ) + ), + React.createElement('div', { style: styles.transferBtns }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), title: '添加', onClick: function () { + handleFormChange('selectedVehicleIds', (formData.selectedVehicleIds || []).concat(leftSelectedIds)); + setLeftSelectedIds([]); + } }, '→'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), title: '移除', onClick: function () { + handleFormChange('selectedVehicleIds', (formData.selectedVehicleIds || []).filter(function (id) { return rightSelectedIds.indexOf(id) < 0; })); + setRightSelectedIds([]); + } }, '←') + ), + React.createElement('div', { style: styles.transferColumn }, + React.createElement('div', { style: styles.transferColumnHead }, '已选车辆'), + React.createElement('div', { style: styles.transferColumnList }, + rightVehicles.map(function (v) { + var selected = rightSelectedIds.indexOf(v.id) >= 0; + return React.createElement('div', { + key: v.id, + style: Object.assign({}, styles.transferItem, selected ? styles.transferItemSelected : {}), + onClick: function () { + if (selected) setRightSelectedIds(rightSelectedIds.filter(function (id) { return id !== v.id; })); + else setRightSelectedIds(rightSelectedIds.concat([v.id])); + } + }, v.plateNo); + }) + ) + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '调拨方式', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, getMethodLabel(formData.transferMethod)) : React.createElement('div', { style: styles.radioGroup }, + TRANSFER_METHODS.map(function (m) { + return React.createElement('label', { key: m.value, style: styles.radioItem }, + React.createElement('input', { type: 'radio', name: 'transferMethod', value: m.value, checked: formData.transferMethod === m.value, onChange: function () { handleFormChange('transferMethod', m.value); } }), + React.createElement('span', { style: { marginLeft: '6px' } }, m.label) + ); + }) + ) + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, styles.formGridFull) }, + React.createElement('label', { style: styles.formLabel }, '调拨原因', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, formData.transferReason || '-') : React.createElement('textarea', { style: styles.textarea, value: formData.transferReason, onChange: function (e) { handleFormChange('transferReason', e.target.value); }, placeholder: '请输入' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '出发区域', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, (function () { + var r = viewRecord || editRecord; + if (r && r.departLabel) return r.departLabel; + if (formData.departProvince && formData.departCity) { + var p = provinceList.find(function (x) { return x.code === formData.departProvince; }); + var c = (cityMap[formData.departProvince] || []).find(function (x) { return x.code === formData.departCity; }); + return (p ? p.name : '') + '-' + (c ? c.name : ''); + } + return '-'; + }()) ) : renderCascader(formData.departProvince, formData.departCity, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departProvince = v; n.departCity = ''; return n; }); }, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.departCity = v; return n; }); }, formDepartCascaderOpen, setFormDepartCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收区域', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, (function () { + var r = viewRecord || editRecord; + if (r && r.transferLabel) return r.transferLabel; + if (formData.transferProvince && formData.transferCity) { + var p = provinceList.find(function (x) { return x.code === formData.transferProvince; }); + var c = (cityMap[formData.transferProvince] || []).find(function (x) { return x.code === formData.transferCity; }); + return (p ? p.name : '') + '-' + (c ? c.name : ''); + } + return '-'; + }()) ) : renderCascader(formData.transferProvince, formData.transferCity, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferProvince = v; n.transferCity = ''; return n; }); }, function (v) { setFormData(function (prev) { var n = {}; for (var k in prev) n[k] = prev[k]; n.transferCity = v; return n; }); }, formTransferCascaderOpen, setFormTransferCascaderOpen) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方名称', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, (viewRecord || editRecord || formData).carrierName || '-') : React.createElement('input', { style: styles.input, value: formData.carrierName, onChange: function (e) { handleFormChange('carrierName', e.target.value); }, placeholder: '请输入' }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '运输方联系方式', !disabled && React.createElement('span', { style: styles.formLabelReq }, '*')), + disabled ? React.createElement('span', {}, (viewRecord || editRecord || formData).carrierContact || '-') : React.createElement('input', { style: styles.input, value: formData.carrierContact, onChange: function (e) { handleFormChange('carrierContact', e.target.value); }, placeholder: '请输入' }) + ) + ); + } + + return ( + React.createElement('div', { style: styles.page }, + React.createElement('style', { + dangerouslySetInnerHTML: { + __html: '.arco-select-trigger:hover{border-color:#C9CDD4 !important;}' + + 'select:hover{border-color:#C9CDD4 !important;}' + + 'select:focus{border-color:#165DFF !important;box-shadow:0 0 0 2px rgba(22,93,255,0.2) !important;}' + + '.arco-select-option:hover{background-color:#F7F8FA !important;}' + } + }), + React.createElement('div', { style: styles.breadcrumb }, + React.createElement('div', { style: styles.breadcrumbLeft }, + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'), + React.createElement('span', { style: { marginRight: '8px' } }, '/'), + React.createElement('span', { style: styles.breadcrumbCurrent }, '调拨管理') + ), + React.createElement('div', { style: styles.breadcrumbRight }, + React.createElement('a', { href: '#', style: styles.requirementLink, onClick: function (e) { e.preventDefault(); setShowRequirementModal(true); } }, '查看需求说明') + ) + ), + + currentView === 'list' && React.createElement(React.Fragment, {}, + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.searchPanel }, + React.createElement('div', { style: styles.searchPanelInner }, + React.createElement('div', { style: styles.searchGridWrap }, + React.createElement('div', { style: styles.searchGrid }, + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '调拨日期'), + React.createElement('div', { style: styles.searchFieldControl }, renderDateRangePicker(filterDateStart, filterDateEnd, setFilterDateStart, setFilterDateEnd, filterDateRangeOpen, setFilterDateRangeOpen, filterDateRangeViewLeft, filterDateRangeViewRight, setFilterDateRangeViewLeft, setFilterDateRangeViewRight)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '调拨人'), + React.createElement('div', { style: styles.searchFieldControl }, renderFilterSelect(mockOpsUsers.map(function (u) { return u.name; }), filterOperator, setFilterOperator, filterOperatorOpen, setFilterOperatorOpen, '模糊搜索', function (n) { return !filterOperator || n.indexOf(filterOperator) >= 0; }, true)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '接收日期'), + React.createElement('div', { style: styles.searchFieldControl }, renderDateRangePicker(filterReceiveDateStart, filterReceiveDateEnd, setFilterReceiveDateStart, setFilterReceiveDateEnd, filterReceiveDateRangeOpen, setFilterReceiveDateRangeOpen, filterReceiveDateRangeViewLeft, filterReceiveDateRangeViewRight, setFilterReceiveDateRangeViewLeft, setFilterReceiveDateRangeViewRight)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '接收人'), + React.createElement('div', { style: styles.searchFieldControl }, renderFilterSelect(mockOpsUsers.map(function (u) { return u.name; }), filterReceiver, setFilterReceiver, filterReceiverOpen, setFilterReceiverOpen, '模糊搜索', function (n) { return !filterReceiver || n.indexOf(filterReceiver) >= 0; }, true)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '车牌号'), + React.createElement('div', { style: styles.searchFieldControl }, renderFilterSelect(mockStockVehicles.map(function (v) { return v.plateNo; }), filterPlateNo, setFilterPlateNo, filterPlateOpen, setFilterPlateOpen, '模糊搜索', function (p) { return !filterPlateNo || p.indexOf(filterPlateNo) >= 0; }, true)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '出发区域'), + React.createElement('div', { style: styles.searchFieldControl }, renderCascader(filterDepartProvince, filterDepartCity, setFilterDepartProvince, setFilterDepartCity, filterCascaderDepartOpen, setFilterCascaderDepartOpen)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '调拨区域'), + React.createElement('div', { style: styles.searchFieldControl }, renderCascader(filterTransferProvince, filterTransferCity, setFilterTransferProvince, setFilterTransferCity, filterCascaderTransferOpen, setFilterCascaderTransferOpen)) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '调拨方式'), + React.createElement('div', { style: styles.searchFieldControl }, + React.createElement('select', { + style: Object.assign({}, styles.select, { width: '100%', minWidth: 0 }), + value: filterMethod, + onChange: function (e) { setFilterMethod(e.target.value); } + }, React.createElement('option', { value: '' }, '全部'), TRANSFER_METHODS.map(function (m) { return React.createElement('option', { key: m.value, value: m.value }, m.label); })) + ) + ), + React.createElement('div', { style: styles.searchField }, + React.createElement('span', { style: styles.searchFieldLabel }, '审批状态'), + React.createElement('div', { style: styles.searchFieldControl }, + React.createElement('select', { + style: Object.assign({}, styles.select, { width: '100%', minWidth: 0 }), + value: filterApprovalStatus, + onChange: function (e) { setFilterApprovalStatus(e.target.value); } + }, React.createElement('option', { value: '' }, '全部'), APPROVAL_STATUS.map(function (s) { return React.createElement('option', { key: s.value, value: s.value }, s.label); })) + ) + ) + ) + ), + React.createElement('div', { style: styles.searchDivider }), + React.createElement('div', { style: styles.searchActions }, + React.createElement('button', { + style: Object.assign({}, styles.btn, styles.btnPrimary, styles.btnFixed), + onClick: function () { + setAppliedFilters({ + filterDateStart: filterDateStart, + filterDateEnd: filterDateEnd, + filterOperator: filterOperator, + filterReceiveDateStart: filterReceiveDateStart, + filterReceiveDateEnd: filterReceiveDateEnd, + filterReceiver: filterReceiver, + filterPlateNo: filterPlateNo, + filterDepartProvince: filterDepartProvince, + filterDepartCity: filterDepartCity, + filterTransferProvince: filterTransferProvince, + filterTransferCity: filterTransferCity, + filterMethod: filterMethod, + filterApprovalStatus: filterApprovalStatus + }); + setCurrentPage(1); + setToastMessage('查询成功'); + setTimeout(function () { setToastMessage(''); }, 1500); + } + }, '查询'), + React.createElement('button', { + style: Object.assign({}, styles.btn, styles.btnDefault, styles.btnFixed), + onClick: function () { + setFilterDateStart(''); setFilterDateEnd(''); setFilterReceiveDateStart(''); setFilterReceiveDateEnd(''); setFilterOperator(''); setFilterReceiver(''); setFilterPlateNo(''); + setFilterDepartProvince(''); setFilterDepartCity(''); setFilterTransferProvince(''); setFilterTransferCity(''); + setFilterMethod(''); setFilterApprovalStatus(''); + setAppliedFilters({}); + setCurrentPage(1); + } + }, '重置') + ) + ) + ), + React.createElement('div', { style: styles.tabs }, + React.createElement('div', { style: Object.assign({}, styles.tab, listTab === 'in_progress' ? styles.tabActive : {}), onClick: function () { setListTab('in_progress'); setCurrentPage(1); setSelectedRowIds([]); } }, '进行中'), + React.createElement('div', { style: Object.assign({}, styles.tab, listTab === 'history' ? styles.tabActive : {}), onClick: function () { setListTab('history'); setCurrentPage(1); setSelectedRowIds([]); } }, '历史记录') + ), + React.createElement('div', { style: styles.toolbar }, + listTab === 'in_progress' && React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnPrimary, styles.btnFixed), onClick: function () { setAddFormData(defaultAddForm); setAddFormLines([createEmptyAddLine()]); setCurrentView('add'); } }, '新增'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault, styles.btnFixed), onClick: function () { setToastMessage(selectedRowIds.length ? '导出' + selectedRowIds.length + '条' : '请先勾选要导出的数据'); setTimeout(function () { setToastMessage(''); }, 2000); } }, '导出') + ), + React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + listTab === 'in_progress' ? React.createElement(React.Fragment, {}, + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap, { width: '32px' }) }, React.createElement('input', { type: 'checkbox', checked: paginatedList.length > 0 && selectedRowIds.length === paginatedList.length, onChange: function (e) { setSelectedRowIds(e.target.checked ? paginatedList.map(function (r) { return r.rowId; }) : []); } })), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨日期'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发区域'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨人'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收区域'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨方式'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨原因'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车牌号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车辆类型'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '品牌'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '型号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '运输方名称'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '运输方联系方式'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发里程(KM)'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发氢量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发电量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发停车场'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '创建日期'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap, styles.thStickyRight) }, '操作') + ) : React.createElement(React.Fragment, {}, + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap, { width: '32px' }) }, React.createElement('input', { type: 'checkbox', checked: paginatedList.length > 0 && selectedRowIds.length === paginatedList.length, onChange: function (e) { setSelectedRowIds(e.target.checked ? paginatedList.map(function (r) { return r.rowId; }) : []); } })), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨日期'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发区域'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨人'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收日期'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收区域'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨方式'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '调拨原因'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收人'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车牌号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '车辆类型'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '品牌'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '型号'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '运输方名称'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '运输方联系方式'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发里程(KM)'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收里程(KM)'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发氢量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收氢量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发电量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收电量'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '出发停车场'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '接收停车场'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap) }, '创建日期'), + React.createElement('th', { style: Object.assign({}, styles.th, styles.thNowrap, styles.thStickyRight) }, '操作') + ) + ) + ), + React.createElement('tbody', {}, + paginatedList.length === 0 + ? React.createElement('tr', {}, React.createElement('td', { colSpan: listTab === 'in_progress' ? 19 : 25, style: Object.assign({}, styles.td, { textAlign: 'center', color: t.neutral6 }) }, '暂无数据')) + : paginatedList.map(function (row, rowIndex) { + var tr = row.transfer; + var line = row.line; + var canReceive = tr.approvalStatus === 'completed' && !tr.hasReceiveRecord; + var canEdit = ['pending_submit', 'rejected', 'withdrawn'].indexOf(tr.approvalStatus) >= 0; + var canWithdraw = tr.approvalStatus === 'approving'; + var checked = selectedRowIds.indexOf(row.rowId) >= 0; + var isEvenRow = rowIndex % 2 === 1; + var tdOneLine = Object.assign({}, styles.td, styles.tdNowrap); + return React.createElement('tr', { key: row.rowId, style: isEvenRow ? styles.trStripe : styles.trOdd }, + React.createElement('td', { style: tdOneLine }, React.createElement('input', { type: 'checkbox', checked: checked, onChange: function (e) { setSelectedRowIds(e.target.checked ? selectedRowIds.concat([row.rowId]) : selectedRowIds.filter(function (id) { return id !== row.rowId; })); } })), + listTab === 'in_progress' ? React.createElement(React.Fragment, {}, + React.createElement('td', { style: tdOneLine }, tr.transferDate), + React.createElement('td', { style: tdOneLine }, tr.departLabel || '-'), + React.createElement('td', { style: tdOneLine }, tr.operatorName), + React.createElement('td', { style: tdOneLine }, tr.transferLabel || '-'), + React.createElement('td', { style: tdOneLine }, getMethodLabel(tr.transferMethod)), + React.createElement('td', { style: tdOneLine }, tr.transferReason || '-'), + React.createElement('td', { style: tdOneLine }, line.plateNo || '-'), + React.createElement('td', { style: tdOneLine }, line.vehicleType || '-'), + React.createElement('td', { style: tdOneLine }, line.brand || '-'), + React.createElement('td', { style: tdOneLine }, line.model || '-'), + React.createElement('td', { style: carrierInfoTdStyle }, tr.carrierName || '-'), + React.createElement('td', { style: carrierInfoTdStyle }, tr.carrierContact || '-'), + React.createElement('td', { style: tdOneLine }, line.departMileage != null ? line.departMileage : '-'), + React.createElement('td', { style: tdOneLine }, formatH2(line.departH2, line.departH2Unit)), + React.createElement('td', { style: tdOneLine }, line.departElec != null ? line.departElec + ' kWh' : '-'), + React.createElement('td', { style: tdOneLine }, line.departParking || '-'), + React.createElement('td', { style: tdOneLine }, tr.createTime), + React.createElement('td', { style: Object.assign({}, tdOneLine, isEvenRow ? styles.tdStickyRightStripe : styles.tdStickyRight) }, + canReceive && React.createElement('a', { style: styles.actionLink, onClick: function (e) { + e.preventDefault(); + setViewRecord(tr); + setFormDataFromTransfer(tr); + setReceiveFormData({ + receiveDate: '', + receiverName: '张明', + lines: (tr.lines || []).map(function (l) { + return { + plateNo: l.plateNo, + vehicleType: l.vehicleType, + brand: l.brand, + model: l.model, + departParking: l.departParking, + departMileage: l.departMileage, + departH2: l.departH2, + departH2Unit: l.departH2Unit, + departElec: l.departElec, + receiveMileage: l.receiveMileage != null ? l.receiveMileage : '', + receiveH2: l.receiveH2 != null ? l.receiveH2 : '', + receiveElec: l.receiveElec != null ? l.receiveElec : '' + }; + }) + }); + setCurrentView('receive'); + } }, '接收记录'), + canEdit && React.createElement('a', { style: styles.actionLink, onClick: function (e) { e.preventDefault(); openEdit(tr); } }, '编辑'), + canWithdraw && React.createElement('a', { style: styles.actionLink, onClick: function (e) { e.preventDefault(); setWithdrawConfirmRecord(tr); } }, '撤回') + ) + ) : React.createElement(React.Fragment, {}, + React.createElement('td', { style: tdOneLine }, tr.transferDate), + React.createElement('td', { style: tdOneLine }, tr.departLabel || '-'), + React.createElement('td', { style: tdOneLine }, tr.operatorName), + React.createElement('td', { style: tdOneLine }, tr.receiveDate || '-'), + React.createElement('td', { style: tdOneLine }, tr.transferLabel || '-'), + React.createElement('td', { style: tdOneLine }, getMethodLabel(tr.transferMethod)), + React.createElement('td', { style: tdOneLine }, tr.transferReason || '-'), + React.createElement('td', { style: tdOneLine }, tr.receiverName || '-'), + React.createElement('td', { style: tdOneLine }, line.plateNo || '-'), + React.createElement('td', { style: tdOneLine }, line.vehicleType || '-'), + React.createElement('td', { style: tdOneLine }, line.brand || '-'), + React.createElement('td', { style: tdOneLine }, line.model || '-'), + React.createElement('td', { style: carrierInfoTdStyle }, tr.carrierName || '-'), + React.createElement('td', { style: carrierInfoTdStyle }, tr.carrierContact || '-'), + React.createElement('td', { style: tdOneLine }, line.departMileage != null ? line.departMileage : '-'), + React.createElement('td', { style: tdOneLine }, line.receiveMileage != null ? line.receiveMileage : '-'), + React.createElement('td', { style: tdOneLine }, formatH2(line.departH2, line.departH2Unit)), + React.createElement('td', { style: tdOneLine }, formatH2(line.receiveH2, line.departH2Unit)), + React.createElement('td', { style: tdOneLine }, line.departElec != null ? line.departElec + ' kWh' : '-'), + React.createElement('td', { style: tdOneLine }, line.receiveElec != null ? line.receiveElec + ' kWh' : '-'), + React.createElement('td', { style: tdOneLine }, line.departParking || '-'), + React.createElement('td', { style: tdOneLine }, line.receiveParking || '-'), + React.createElement('td', { style: tdOneLine }, tr.createTime), + React.createElement('td', { style: Object.assign({}, tdOneLine, isEvenRow ? styles.tdStickyRightStripe : styles.tdStickyRight) }, React.createElement('a', { style: styles.actionLink, onClick: function (e) { e.preventDefault(); setViewRecord(tr); setFormDataFromTransfer(tr); setCurrentView('view'); } }, '查看')) + ) + ); + }) + ) + ) + ), + totalItems > 0 && React.createElement('div', { style: styles.pagination }, + React.createElement('div', { style: styles.paginationLeft }, + React.createElement('span', {}, '共 ' + totalItems + ' 条'), + React.createElement('span', { style: { marginLeft: '16px' } }, '每页'), + React.createElement('select', { style: styles.paginationSelect, value: pageSize, onChange: function (e) { setPageSize(Number(e.target.value)); setCurrentPage(1); } }, + React.createElement('option', { value: 10 }, '10'), + React.createElement('option', { value: 20 }, '20'), + React.createElement('option', { value: 50 }, '50'), + React.createElement('option', { value: 100 }, '100') + ), + React.createElement('span', {}, '条') + ), + React.createElement('div', { style: styles.paginationRight }, + React.createElement('button', { style: Object.assign({}, styles.paginationBtn, currentPage <= 1 ? styles.paginationBtnDisabled : {}), disabled: currentPage <= 1, onClick: function () { if (currentPage > 1) setCurrentPage(currentPage - 1); } }, '上一页'), + React.createElement('span', { style: { margin: '0 8px' } }, currentPage + ' / ' + totalPages), + React.createElement('button', { style: Object.assign({}, styles.paginationBtn, currentPage >= totalPages ? styles.paginationBtnDisabled : {}), disabled: currentPage >= totalPages, onClick: function () { if (currentPage < totalPages) setCurrentPage(currentPage + 1); } }, '下一页') + ) + ) + ) + ), + + currentView === 'add' && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { marginBottom: t.spacing16, fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8 } }, '新增调拨'), + renderAddPage() + ), + currentView === 'edit' && React.createElement(React.Fragment, {}, + React.createElement('div', { style: { marginBottom: t.spacing16, fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8 } }, '编辑调拨'), + renderEditPage() + ), + + withdrawConfirmRecord && React.createElement('div', { style: styles.modalMask, onClick: function () { setWithdrawConfirmRecord(null); } }, + React.createElement('div', { style: Object.assign({}, styles.modalBox, { minWidth: '360px' }), onClick: function (e) { e.stopPropagation(); } }, + React.createElement('div', { style: styles.modalTitle }, '确认撤回'), + React.createElement('p', { style: { margin: '0 0 16px', fontSize: t.fontSize14 } }, '是否确认撤回该调拨记录?'), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setWithdrawConfirmRecord(null); } }, '取消'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function () { handleWithdraw(withdrawConfirmRecord); } }, '确认') + ) + ) + ), + + currentView === 'view' && viewRecord && renderViewPage(viewRecord), + + currentView === 'receive' && viewRecord && React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.modalTitle }, '接收记录'), + renderFormContent(true, true), + React.createElement('div', { style: styles.formRow }, + React.createElement('label', { style: styles.formLabel }, '接收日期', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('input', { style: styles.input, type: 'date', value: receiveFormData.receiveDate, onChange: function (e) { setReceiveFormData(Object.assign({}, receiveFormData, { receiveDate: e.target.value })); } }) + ), + React.createElement('div', { style: Object.assign({}, styles.formRow, { marginTop: t.spacing24 }) }, + React.createElement('div', { style: styles.tableWrap }, + React.createElement('table', { style: styles.table }, + React.createElement('thead', {}, + React.createElement('tr', {}, + 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 }, '出发里程(KM)'), + React.createElement('th', { style: styles.th }, '接收里程(KM)', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: styles.th }, '出发氢量'), + React.createElement('th', { style: styles.th }, '接收氢量', React.createElement('span', { style: styles.formLabelReq }, '*')), + React.createElement('th', { style: styles.th }, '出发电量'), + React.createElement('th', { style: styles.th }, '接收电量', React.createElement('span', { style: styles.formLabelReq }, '*')) + ) + ), + React.createElement('tbody', {}, + (receiveFormData.lines || []).map(function (line, idx) { + var isEvenRow = idx % 2 === 1; + return React.createElement('tr', { key: idx, style: isEvenRow ? styles.trStripe : styles.trOdd }, + React.createElement('td', { style: styles.td }, line.plateNo), + React.createElement('td', { style: styles.td }, line.vehicleType), + React.createElement('td', { style: styles.td }, line.brand), + React.createElement('td', { style: styles.td }, line.model), + React.createElement('td', { style: styles.td }, line.departParking), + React.createElement('td', { style: styles.td }, line.departMileage != null ? line.departMileage : '-'), + React.createElement('td', { style: styles.td }, React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: line.receiveMileage, onChange: function (e) { var next = receiveFormData.lines.slice(); next[idx] = Object.assign({}, next[idx], { receiveMileage: e.target.value }); setReceiveFormData(Object.assign({}, receiveFormData, { lines: next })); } })), + React.createElement('td', { style: styles.td }, formatH2(line.departH2, line.departH2Unit)), + React.createElement('td', { style: styles.td }, React.createElement(React.Fragment, {}, React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), value: line.receiveH2, onChange: function (e) { var next = receiveFormData.lines.slice(); next[idx] = Object.assign({}, next[idx], { receiveH2: e.target.value }); setReceiveFormData(Object.assign({}, receiveFormData, { lines: next })); } }), React.createElement('span', { style: { marginLeft: '4px' } }, (line.departH2Unit && !String(line.receiveH2 || '').match(/%|MPa|KG$/)) ? line.departH2Unit : ''))), + React.createElement('td', { style: styles.td }, (line.departElec != null ? line.departElec : '-') + ' kWh'), + React.createElement('td', { style: styles.td }, React.createElement('input', { style: Object.assign({}, styles.input, { width: '80px' }), type: 'number', value: line.receiveElec, onChange: function (e) { var next = receiveFormData.lines.slice(); next[idx] = Object.assign({}, next[idx], { receiveElec: e.target.value }); setReceiveFormData(Object.assign({}, receiveFormData, { lines: next })); } })) + ); + }) + ) + ) + ) + ), + React.createElement('div', { style: styles.modalFooter }, + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { setViewRecord(null); setCurrentView('list'); } }, '取消'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function () { + if (!receiveFormData.receiveDate) { setToastMessage('请选择接收日期'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + var recLines = receiveFormData.lines || []; + for (var ri = 0; ri < recLines.length; ri++) { + var fl = recLines[ri]; + var rn = ri + 1; + if (fl.receiveMileage === '' || fl.receiveMileage === undefined || fl.receiveMileage === null) { setToastMessage('请填写第' + rn + '行接收里程(KM)'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + if (fl.receiveH2 === '' || fl.receiveH2 === undefined || fl.receiveH2 === null) { setToastMessage('请填写第' + rn + '行接收氢量'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + if (fl.receiveElec === '' || fl.receiveElec === undefined || fl.receiveElec === null) { setToastMessage('请填写第' + rn + '行接收电量'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + } + var updatedLines = (viewRecord.lines || []).map(function (l, i) { + var formLine = (receiveFormData.lines || [])[i] || {}; + var next = {}; + for (var k in l) next[k] = l[k]; + next.receiveMileage = formLine.receiveMileage !== '' && formLine.receiveMileage !== undefined ? Number(formLine.receiveMileage) : null; + next.receiveH2 = formLine.receiveH2 !== '' && formLine.receiveH2 !== undefined ? formLine.receiveH2 : null; + next.receiveElec = formLine.receiveElec !== '' && formLine.receiveElec !== undefined ? Number(formLine.receiveElec) : null; + next.receiveParking = '抵达停车场'; + next.receiverName = receiveFormData.receiverName; + return next; + }); + var updated = {}; + for (var k in viewRecord) updated[k] = viewRecord[k]; + updated.hasReceiveRecord = true; + updated.receiveDate = receiveFormData.receiveDate; + updated.receiverId = 'u1'; + updated.receiverName = receiveFormData.receiverName; + updated.lines = updatedLines; + setMockTransferList(mockTransferList.map(function (r) { return r.id === viewRecord.id ? updated : r; })); + setViewRecord(updated); + setToastMessage('保存成功'); + setTimeout(function () { setToastMessage(''); }, 2000); + } }, '保存'), + React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function () { + if (!receiveFormData.receiveDate) { setToastMessage('请选择接收日期'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + var recLines2 = receiveFormData.lines || []; + for (var ri2 = 0; ri2 < recLines2.length; ri2++) { + var fl2 = recLines2[ri2]; + var rn2 = ri2 + 1; + if (fl2.receiveMileage === '' || fl2.receiveMileage === undefined || fl2.receiveMileage === null) { setToastMessage('请填写第' + rn2 + '行接收里程(KM)'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + if (fl2.receiveH2 === '' || fl2.receiveH2 === undefined || fl2.receiveH2 === null) { setToastMessage('请填写第' + rn2 + '行接收氢量'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + if (fl2.receiveElec === '' || fl2.receiveElec === undefined || fl2.receiveElec === null) { setToastMessage('请填写第' + rn2 + '行接收电量'); setTimeout(function () { setToastMessage(''); }, 2000); return; } + } + var updatedLines = (viewRecord.lines || []).map(function (l, i) { + var formLine = (receiveFormData.lines || [])[i] || {}; + var next = {}; + for (var k in l) next[k] = l[k]; + next.receiveMileage = formLine.receiveMileage !== '' && formLine.receiveMileage !== undefined ? Number(formLine.receiveMileage) : null; + next.receiveH2 = formLine.receiveH2 !== '' && formLine.receiveH2 !== undefined ? formLine.receiveH2 : null; + next.receiveElec = formLine.receiveElec !== '' && formLine.receiveElec !== undefined ? Number(formLine.receiveElec) : null; + next.receiveParking = '抵达停车场'; + next.receiverName = receiveFormData.receiverName; + return next; + }); + var updated = {}; + for (var k in viewRecord) updated[k] = viewRecord[k]; + updated.hasReceiveRecord = true; + updated.receiveDate = receiveFormData.receiveDate; + updated.receiverId = 'u1'; + updated.receiverName = receiveFormData.receiverName; + updated.lines = updatedLines; + setMockTransferList(mockTransferList.map(function (r) { return r.id === viewRecord.id ? updated : r; })); + setToastMessage('保存成功'); + setTimeout(function () { setToastMessage(''); }, 2000); + setViewRecord(null); + setCurrentView('list'); + } }, '提交') + ) + ), + + showRequirementModal && React.createElement('div', { + style: styles.modalMask, + onClick: function () { setShowRequirementModal(false); } + }, React.createElement('div', { + style: Object.assign({}, styles.modalBox, { minWidth: '640px', maxWidth: '800px' }), + onClick: function (e) { e.stopPropagation(); } + }, + React.createElement('button', { + style: styles.modalCloseBtn, + onClick: function () { setShowRequirementModal(false); }, + title: '关闭' + }, '×'), + React.createElement('div', { style: { paddingRight: '32px', paddingBottom: t.spacing24, lineHeight: '1.65', maxHeight: '85vh', overflowY: 'auto', fontSize: t.fontSize14, color: t.neutral7 } }, + React.createElement('div', { style: Object.assign({}, styles.requirementSection, { marginTop: 0 }) }, + React.createElement('div', { style: styles.requirementSectionTitle }, '面包屑'), + React.createElement('div', { style: styles.requirementItem }, '运维管理-车辆业务-调拨管理') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '筛选'), + React.createElement('div', { style: styles.requirementItem }, '1.调拨日期:日期选择器,支持选择开始-结束时间'), + React.createElement('div', { style: styles.requirementItem }, '2.调拨人:选择器,支持从输入框输入内容模糊搜索'), + React.createElement('div', { style: styles.requirementItem }, '3.接收日期:日期选择器,支持选择开始-结束时间'), + React.createElement('div', { style: styles.requirementItem }, '4.接收人:选择器,支持从输入框输入内容模糊搜索'), + React.createElement('div', { style: styles.requirementItem }, '5.车牌号:选择器,支持从输入框输入内容模糊搜索'), + React.createElement('div', { style: styles.requirementItem }, '6.出发区域:级联选择,支持省市二级级联'), + React.createElement('div', { style: styles.requirementItem }, '7.调拨区域:级联选择,支持省市二级级联'), + React.createElement('div', { style: styles.requirementItem }, '8.调拨方式:选择器,状态分为:第三方运输、司机驾驶'), + React.createElement('div', { style: styles.requirementItem }, '9.审批状态:选择器,状态分为:待提交、待审批、审批中、审批完成、审批驳回、撤回'), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing8, marginBottom: t.spacing4 }) }, '审批状态说明(该说明只做需求说明,AI无需分析):'), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, + React.createElement('div', { style: { marginBottom: '2px' } }, 'a.待提交:新增完成后选择保存,但未提交审核;'), + React.createElement('div', { style: { marginBottom: '2px' } }, 'b.待审批:已提交审核,但未通过第一个流程节点审核;'), + React.createElement('div', { style: { marginBottom: '2px' } }, 'c.审批中:已提交审核,至少完成1个以上节点审核,但未完成最终节点审核;'), + React.createElement('div', { style: { marginBottom: '2px' } }, 'd.审批完成:已提交审核,并完成最终节点审核;'), + React.createElement('div', { style: { marginBottom: '2px' } }, 'e.审批驳回:已提交审核,但任意节点审核未通过,审批驳回的记录支持修改后重新提交审核;'), + React.createElement('div', {}, 'f.撤回:已提交审核,但完成最终审核前主动撤回,撤回的记录支持修改后重新提交审核;') + ) + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '列表'), + React.createElement('div', { style: styles.requirementItem }, '列表由两个tab页组成,分别为:进行中、历史记录;'), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing12, marginBottom: t.spacing4 }) }, 'a.进行中tab:显示审批状态为待提交、待审批、审批完成、审核驳回、撤回的记录以及未补充接收记录的数据:'), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24, marginBottom: t.spacing8 }) }, '列表上方右侧为新增、导出(支持多选)按钮,下方为分页功能,支持选择单页显示多少条数据记录,显示字段按以下顺序'), + [ '1.调拨日期:新建调拨记录时填写的调拨日期,格式为YYYY-MM-DD', '2.出发区域:显示该车辆出发时区域', '3.调拨人:新建调拨记录的操作人', '4.接收区域:新建调拨记录时填写的接收区域', '5.调拨方式:新建调拨记录时选择的状态', '6.调拨原因:新建调拨记录时输入的调拨原因', '7.运输方名称:新建调拨记录时输入的运输方名称', '8.运输方联系方式:新建调拨记录时输入的运输方联系方式', '9.车牌号:新建调拨记录时填写的车牌号,每个车牌号形成一条单独记录;', '10.车辆类型:根据车牌号自动反写车辆类型', '11.品牌:根据车牌号自动反写品牌', '12.型号:根据车牌号自动反写型号', '13.运输方信息:新建调拨记录时手动输入的运输方名称及联系方式,在列表中显示为两行,上方为运输方名称,下方为联系方式', '14.出发里程(KM):新建调拨记录时填写的出发里程', '15.出发氢量:新建调拨记录时填写的出发氢量,单位根据车辆型号对应氢气计量单位自动匹配,分为%、MPa、KG,分别给出一些样例', '16.出发电量:新建调拨记录时填写的出发电量,单位为kWh', '17.出发停车场:新建调拨记录时该车辆出发时的停车场', '18.创建日期:显示该记录创建日期', '19.操作:接收记录、编辑、撤回;操作记录说明如下' ].map(function (txt, i) { return React.createElement('div', { key: i, style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24, marginBottom: '2px' }) }, txt); }), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, + React.createElement('div', { style: { marginBottom: '2px' } }, 'a.接收记录:点击后与新增页样式和字段相同,但只有接收日期、抵达里程(KM)、抵达氢量、抵达电量、接收停车场等字段可以操作,仅审核状态为审核完成时显示该功能'), + React.createElement('div', { style: { marginBottom: '2px' } }, 'b.编辑:审核状态为待提交、审批驳回、撤回的数据可进行编辑,其他审核状态不显示编辑,点击编辑后页面与新增页样式相同所有字段可编辑;'), + React.createElement('div', {}, 'c.撤回:点击弹出框进行二次确认,点击弹出框确认按钮toast提示撤回成功,点击弹出框取消按钮,关闭弹出框;仅在审批状态为审批中可撤回,如审批状态为已完成则不显示撤回;') + ), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing16, marginBottom: t.spacing4 }) }, 'b.历史记录tab:'), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24, marginBottom: t.spacing8 }) }, '显示审批状态为审核完成以及完成补充接收记录的数据:列表上方右侧为导出(支持多选)按钮,下方为分页功能,支持选择单页显示多少条数据记录'), + [ '1.调拨日期:新建调拨记录时填写的调拨日期,格式为YYYY-MM-DD', '2.出发区域:显示该车辆出发时区域', '3.调拨人:新建调拨记录的操作人', '4.接收日期:补充接收记录时填写的接收日期,格式为YYYY-MM-DD', '5.接收区域:新建调拨记录时填写的接收区域', '6.调拨方式:新建调拨记录时选择的状态', '7.调拨原因:新建调拨记录时输入的调拨原因', '8.运输方名称:新建调拨记录时输入的运输方名称', '9.运输方联系方式:新建调拨记录时输入的运输方联系方式', '10.接收人:补充接收记录时的操作人', '11.车牌号:新建调拨记录时填写的车牌号,每个车牌号形成一条单独记录;', '12.车辆类型:根据车牌号自动反写车辆类型', '13.品牌:根据车牌号自动反写品牌', '14.型号:根据车牌号自动反写型号', '15.运输方信息:新建调拨记录时手动输入的运输方名称及联系方式,在列表中显示为两行,上方为运输方名称,下方为联系方式', '16.出发里程(KM):新建调拨记录时填写的出发里程', '17.接收里程(KM):补充接收记录时填写的抵达里程', '18.出发氢量:新建调拨记录时填写的出发氢量,单位根据车辆型号对应氢气计量单位自动匹配,分为%、MPa、KG,分别给出一些样例', '19.接收氢量:补充接收记录时填写的抵达氢量,单位根据车辆型号对应氢气计量单位自动匹配,分为%、MPa、KG,分别给出一些样例', '20.出发电量:新建调拨记录时填写的出发电量,单位为kWh', '21.接收电量:补充接收记录时填写的抵达电量,单位为kWh', '22.出发停车场:新建调拨记录时该车辆出发时的停车场', '23.接收停车场:补充接收记录时该车辆抵达场', '24.创建日期:显示该记录创建日期', '25.操作:查看;操作记录说明如下' ].map(function (txt, i) { return React.createElement('div', { key: i, style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24, marginBottom: '2px' }) }, txt); }), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, 'a.查看:与接收记录页所有字段和布局相同,但所有字段控件均为禁用状态;') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '新增'), + React.createElement('div', { style: styles.requirementItem }, '点击新增按钮以打开新页面的方式进行操作。'), + React.createElement('div', { style: styles.requirementItem }, '页面分为上下两部分组成,上方以3列布局,显示以下字段:'), + [ '1.调拨日期:必填项,日期选择器,格式为YYYY-MM-DD', '2.出发区域:必填项,级联选择器,显示格式为:省-市', '3.接受区域:必填项,级联选择器,显示格式为:省-市', '4.调拨方式:必填项,按钮式选择器,选项分别为第三方运输、司机驾驶', '5.调拨原因:必填项,文本域', '6.运输方名称:必填项,输入框,输入框提示信息为请输入运输方名称或司机姓名', '7.运输方联系方式:必填项,输入框,输入框提示信息为请输入运输方联系方式' ].map(function (txt, i) { return React.createElement('div', { key: i, style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, txt); }), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing8 }) }, '页面下方以可编辑列表方式显示以下字段,默认显示一行数据,列表下方为新增一行按钮,点击新增一行数据'), + [ '1.车牌号:选择器,输入框支持模糊搜索,只能选择状态为库存的车辆', '2.车辆类型:直接显示内容,根据车牌号自动反写车辆类型', '3.品牌:直接显示内容,根据车牌号自动反写品牌', '4.型号:直接显示内容,根据车牌号自动反写型号', '5.出发停车场:直接显示内容,根据车牌号当前停车场自动反写', '6.出发里程(KM):输入框', '7.出发氢量:输入框,后缀为单位,单位根据该车辆型号参数内氢系统仪表盘单位字段自动反写单位,如未设置则不显示后缀单位', '8.出发电量:输入框,后缀为单位,单位为kWh', '9.操作:删除,点击后删除该行数据' ].map(function (txt, i) { return React.createElement('div', { key: i, style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, txt); }), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing8 }) }, '页面最下方为提交审核、保存、取消按钮'), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, '点击提交审核提交并转入调拨审核流程;点击保存,保存数据但不提交审核;点击取消,跳转回调拨列表页') + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '接收记录'), + React.createElement('div', { style: styles.requirementItem }, '上方显示新增页所有字段和布局,但所有组件为禁用状态,除此外新增一个可编辑字段'), + React.createElement('div', { style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, '1.接收日期:必填项,日期选择器,格式为YYYY-MM-DD'), + React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: t.spacing8 }) }, '页面下方以可编辑列表方式显示以下字段'), + [ '2.车牌号:文字方式显示新增时记录的车牌号', '3.车辆类型:文字方式显示新增时记录的车牌号对应车辆类型', '4.品牌:文字方式显示新增时记录的车牌号对应品牌', '5.型号:文字方式显示新增时记录的车牌号对应型号', '6.出发停车场:文字方式显示新增时记录的车牌号对应停车场', '7.出发里程(KM):文字方式显示新增时记录的出发里程', '8.接收里程(KM):输入框', '9.出发氢量:文字显示新增时记录的车牌号对应出发氢量,并显示单位', '10.接收氢量:输入框,后缀为单位,单位根据车辆型号对应氢气计量单位自动匹配,分为%、MPa、KG', '11.出发电量:文字显示新增时记录的车牌号对应出发电量,单位为kWh', '12.接收电量:输入框,后缀为单位,单位为kWh' ].map(function (txt, i) { return React.createElement('div', { key: i, style: Object.assign({}, styles.requirementSubItem, { paddingLeft: t.spacing24 }) }, txt); }) + ), + React.createElement('div', { style: styles.requirementSection }, + React.createElement('div', { style: styles.requirementSectionTitle }, '导出'), + React.createElement('div', { style: styles.requirementItem }, '支持选择器选择对应数据进行导出') + ) + ) + )), + + toastMessage && React.createElement('div', { style: styles.toast }, toastMessage) + ) + ); +}; + +if (typeof window !== 'undefined') { + window.Component = Component; + function mount() { + var rootEl = document.getElementById('root'); + if (rootEl && window.ReactDOM && window.React) { + var root = ReactDOM.createRoot(rootEl); + root.render(React.createElement(Component)); + } + } + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', mount); + } else { + setTimeout(mount, 0); + } +} diff --git a/小程序/App.jsx b/小程序/App.jsx new file mode 100644 index 0000000..da83cd1 --- /dev/null +++ b/小程序/App.jsx @@ -0,0 +1,176 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 集成登录页 + 工作台 于单页面 +const Component = function () { + var _React$useState = React.useState("login"); + var page = _React$useState[0]; + var setPage = _React$useState[1]; + + // 登录页状态 + var _React$useState2 = React.useState(""); + var account = _React$useState2[0]; + var setAccount = _React$useState2[1]; + var _React$useState3 = React.useState(""); + var password = _React$useState3[0]; + var setPassword = _React$useState3[1]; + var _React$useState4 = React.useState(false); + var agreed = _React$useState4[0]; + var setAgreed = _React$useState4[1]; + + // 工作台状态 + var _React$useState5 = React.useState(0); + var activeTab = _React$useState5[0]; + var setActiveTab = _React$useState5[1]; + + var todos = [ + { id: 1, title: "审批设备巡检报告", status: "待处理", time: "今天" }, + { id: 2, title: "处理加氢站异常告警", status: "进行中", time: "今天" }, + { id: 3, title: "完成月度资产盘点", status: "待处理", time: "本周" } + ]; + var stats = [ + { label: "待办任务", value: "8", color: "#07c160" }, + { label: "本周完成", value: "23", color: "#576b95" }, + { label: "告警待处理", value: "2", color: "#fa5151" } + ]; + var navItems = [ + { key: 0, icon: "\uD83D\uDCCA", text: "工作台" }, + { key: 1, icon: "\uD83D\uDCC4", text: "业务" }, + { key: 2, icon: "\uD83D\uDC64", text: "我的" } + ]; + + var handleLogin = function () { + if (!agreed) { + alert("请先阅读并同意用户服务协议和隐私政策"); + return; + } + setPage("workbench"); + }; + + var styles = { + loginPage: { + minHeight: "100vh", + backgroundColor: "#f7f7f7", + padding: "40px 24px 24px", + boxSizing: "border-box", + display: "flex", + flexDirection: "column", + alignItems: "center" + }, + logo: { width: "80px", height: "80px", backgroundColor: "#07c160", borderRadius: "16px", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "20px", color: "#fff", fontSize: "16px", fontWeight: "bold", textAlign: "center", lineHeight: "1.3" }, + title: { fontSize: "20px", fontWeight: "600", color: "#333", textAlign: "center", marginBottom: "40px", lineHeight: "1.5", maxWidth: "280px" }, + form: { width: "100%", maxWidth: "340px" }, + inputGroup: { marginBottom: "16px" }, + input: { width: "100%", height: "48px", padding: "0 16px", fontSize: "16px", border: "1px solid #e5e5e5", borderRadius: "8px", backgroundColor: "#fff", boxSizing: "border-box", outline: "none" }, + checkboxRow: { display: "flex", alignItems: "center", marginBottom: "24px", cursor: "pointer" }, + checkbox: { width: "18px", height: "18px", marginRight: "8px", accentColor: "#07c160", cursor: "pointer" }, + agreement: { fontSize: "14px", color: "#666", lineHeight: "1.5" }, + link: { color: "#07c160", textDecoration: "none" }, + loginBtn: { width: "100%", height: "48px", backgroundColor: "#07c160", color: "#fff", fontSize: "17px", fontWeight: "500", border: "none", borderRadius: "8px", cursor: "pointer", marginBottom: "32px" }, + loginBtnDisabled: { width: "100%", height: "48px", backgroundColor: "#b0b0b0", color: "#fff", fontSize: "17px", fontWeight: "500", border: "none", borderRadius: "8px", cursor: "not-allowed", marginBottom: "32px" }, + divider: { display: "flex", alignItems: "center", width: "100%", maxWidth: "340px", marginBottom: "24px" }, + dividerLine: { flex: 1, height: "1px", backgroundColor: "#e5e5e5" }, + dividerText: { padding: "0 16px", fontSize: "14px", color: "#999" }, + phoneLogin: { display: "flex", flexDirection: "column", alignItems: "center" }, + iconLabel: { fontSize: "14px", color: "#999", marginTop: "8px" }, + iconBtn: { width: "48px", height: "48px", borderRadius: "50%", backgroundColor: "#fff", border: "1px solid #e5e5e5", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", fontSize: "24px" }, + workbenchPage: { minHeight: "100vh", backgroundColor: "#f7f7f7", paddingBottom: "70px", boxSizing: "border-box" }, + header: { backgroundColor: "#fff", padding: "24px 20px", marginBottom: "12px" }, + headerTitle: { fontSize: "22px", fontWeight: "600", color: "#333", marginBottom: "4px" }, + headerSub: { fontSize: "14px", color: "#999" }, + statsRow: { display: "flex", padding: "0 12px 12px", marginBottom: "12px" }, + statCard: { flex: 1, backgroundColor: "#fff", borderRadius: "8px", padding: "20px 12px", textAlign: "center", margin: "0 6px" }, + statValue: { fontSize: "24px", fontWeight: "600", marginBottom: "8px" }, + statLabel: { fontSize: "13px", color: "#999" }, + section: { backgroundColor: "#fff", padding: "20px", marginBottom: "12px" }, + sectionTitle: { fontSize: "17px", fontWeight: "600", color: "#333", marginBottom: "16px", display: "flex", justifyContent: "space-between", alignItems: "center" }, + sectionMore: { fontSize: "14px", color: "#07c160" }, + todoItem: { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "1px solid #f0f0f0" }, + todoLeft: { flex: 1 }, + todoTitle: { fontSize: "15px", color: "#333", marginBottom: "4px" }, + todoMeta: { fontSize: "12px", color: "#999" }, + todoStatus: { fontSize: "12px", padding: "4px 10px", borderRadius: "4px", backgroundColor: "#e8f5e9", color: "#07c160" }, + todoStatusDoing: { fontSize: "12px", padding: "4px 10px", borderRadius: "4px", backgroundColor: "#fff3e0", color: "#ff9800" }, + bottomNav: { position: "fixed", bottom: 0, left: 0, right: 0, height: "60px", backgroundColor: "#fff", display: "flex", borderTop: "1px solid #e5e5e5", boxSizing: "border-box" }, + navItem: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", cursor: "pointer" }, + navIcon: { fontSize: "24px", marginBottom: "4px" }, + navText: { fontSize: "11px", color: "#999" } + }; + + if (page === "login") { + return React.createElement("div", { style: styles.loginPage }, + React.createElement("style", null, "input::placeholder{color:#999;opacity:1;}"), + React.createElement("div", { style: styles.logo }, "羚牛氢能"), + React.createElement("h1", { style: styles.title }, "数字化资产ONE-OS运管平台"), + React.createElement("div", { style: styles.form }, + React.createElement("div", { style: styles.inputGroup }, + React.createElement("input", { style: styles.input, type: "text", placeholder: "请输入账号信息", value: account, onChange: function (e) { setAccount(e.target.value); } }) + ), + React.createElement("div", { style: styles.inputGroup }, + React.createElement("input", { style: styles.input, type: "password", placeholder: "请输入密码", value: password, onChange: function (e) { setPassword(e.target.value); } }) + ), + React.createElement("label", { style: styles.checkboxRow }, + React.createElement("input", { style: styles.checkbox, type: "checkbox", checked: agreed, onChange: function (e) { setAgreed(e.target.checked); } }), + React.createElement("span", { style: styles.agreement }, + "阅读并同意", + React.createElement("a", { href: "#", style: styles.link }, "《用户服务协议》"), + "和", + React.createElement("a", { href: "#", style: styles.link }, "《隐私政策》") + ) + ), + React.createElement("button", { style: agreed ? styles.loginBtn : styles.loginBtnDisabled, onClick: handleLogin, disabled: !agreed }, "登录") + ), + React.createElement("div", { style: styles.divider }, + React.createElement("div", { style: styles.dividerLine }), + React.createElement("span", { style: styles.dividerText }, "其他登录方式"), + React.createElement("div", { style: styles.dividerLine }) + ), + React.createElement("div", { style: styles.phoneLogin }, + React.createElement("div", { style: styles.iconBtn, title: "手机号登录" }, "\uD83D\uDCF1"), + React.createElement("span", { style: styles.iconLabel }, "授权登录") + ) + ); + } + + return React.createElement("div", { style: styles.workbenchPage }, + React.createElement("div", { style: styles.header }, + React.createElement("div", { style: styles.headerTitle }, "工作台"), + React.createElement("div", { style: styles.headerSub }, "欢迎使用 ONE-OS 运管平台") + ), + React.createElement("div", { style: styles.statsRow }, + stats.map(function (stat, i) { + var valueStyle = { fontSize: "24px", fontWeight: "600", marginBottom: "8px", color: stat.color }; + return React.createElement("div", { key: i, style: styles.statCard }, + React.createElement("div", { style: valueStyle }, stat.value), + React.createElement("div", { style: styles.statLabel }, stat.label) + ); + }) + ), + React.createElement("div", { style: styles.section }, + React.createElement("div", { style: styles.sectionTitle }, + React.createElement("span", null, "待办任务"), + React.createElement("span", { style: styles.sectionMore }, "全部") + ), + todos.map(function (todo, i) { + var isLast = i === todos.length - 1; + var statusStyle = todo.status === "进行中" ? styles.todoStatusDoing : styles.todoStatus; + var itemStyle = isLast ? { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "none" } : styles.todoItem; + return React.createElement("div", { key: todo.id, style: itemStyle }, + React.createElement("div", { style: styles.todoLeft }, + React.createElement("div", { style: styles.todoTitle }, todo.title), + React.createElement("div", { style: styles.todoMeta }, todo.time) + ), + React.createElement("span", { style: statusStyle }, todo.status) + ); + }) + ), + React.createElement("div", { style: styles.bottomNav }, + navItems.map(function (item) { + var isActive = activeTab === item.key; + var textStyle = isActive ? { fontSize: "11px", color: "#07c160" } : styles.navText; + return React.createElement("div", { key: item.key, style: styles.navItem, onClick: function () { setActiveTab(item.key); } }, + React.createElement("div", { style: styles.navIcon }, item.icon), + React.createElement("div", { style: textStyle }, item.text) + ); + }) + ) + ); +}; diff --git a/小程序/LoginPage.jsx b/小程序/LoginPage.jsx new file mode 100644 index 0000000..7d9ebc8 --- /dev/null +++ b/小程序/LoginPage.jsx @@ -0,0 +1,286 @@ +// 【重要】必须使用 const Component 作为组件变量名 +const LoginPage = function (props) { + var onLoginSuccess = (props && props.onLoginSuccess) ? props.onLoginSuccess : null; + var _React$useState = React.useState(""); + var account = _React$useState[0]; + var setAccount = _React$useState[1]; + + var _React$useState2 = React.useState(""); + var password = _React$useState2[0]; + var setPassword = _React$useState2[1]; + + var _React$useState3 = React.useState(false); + var agreed = _React$useState3[0]; + var setAgreed = _React$useState3[1]; + + var handleAccountChange = function (e) { + setAccount(e.target.value); + }; + + var handlePasswordChange = function (e) { + setPassword(e.target.value); + }; + + var handleAgreedChange = function (e) { + setAgreed(e.target.checked); + }; + + var handleLogin = function () { + if (!agreed) { + alert("请先阅读并同意用户服务协议和隐私政策"); + return; + } + if (onLoginSuccess) { + onLoginSuccess(); + } else { + alert("登录功能演示"); + } + }; + + var styles = { + page: { + minHeight: "100vh", + backgroundColor: "#f7f7f7", + padding: "40px 24px 24px", + boxSizing: "border-box", + display: "flex", + flexDirection: "column", + alignItems: "center" + }, + logo: { + width: "80px", + height: "80px", + backgroundColor: "#07c160", + borderRadius: "16px", + display: "flex", + alignItems: "center", + justifyContent: "center", + marginBottom: "20px", + color: "#fff", + fontSize: "16px", + fontWeight: "bold", + textAlign: "center", + lineHeight: "1.3" + }, + title: { + fontSize: "20px", + fontWeight: "600", + color: "#333", + textAlign: "center", + marginBottom: "40px", + lineHeight: "1.5", + maxWidth: "280px" + }, + form: { + width: "100%", + maxWidth: "340px" + }, + inputGroup: { + marginBottom: "16px" + }, + input: { + width: "100%", + height: "48px", + padding: "0 16px", + fontSize: "16px", + border: "1px solid #e5e5e5", + borderRadius: "8px", + backgroundColor: "#fff", + boxSizing: "border-box", + outline: "none" + }, + inputFocus: { + borderColor: "#07c160" + }, + checkboxRow: { + display: "flex", + alignItems: "center", + marginBottom: "24px", + cursor: "pointer" + }, + checkbox: { + width: "18px", + height: "18px", + marginRight: "8px", + accentColor: "#07c160", + cursor: "pointer" + }, + agreement: { + fontSize: "14px", + color: "#666", + lineHeight: "1.5" + }, + link: { + color: "#07c160", + textDecoration: "none" + }, + loginBtn: { + width: "100%", + height: "48px", + backgroundColor: "#07c160", + color: "#fff", + fontSize: "17px", + fontWeight: "500", + border: "none", + borderRadius: "8px", + cursor: "pointer", + marginBottom: "32px" + }, + loginBtnDisabled: { + width: "100%", + height: "48px", + backgroundColor: "#b0b0b0", + color: "#fff", + fontSize: "17px", + fontWeight: "500", + border: "none", + borderRadius: "8px", + cursor: "not-allowed", + marginBottom: "32px" + }, + divider: { + display: "flex", + alignItems: "center", + width: "100%", + maxWidth: "340px", + marginBottom: "24px" + }, + dividerLine: { + flex: 1, + height: "1px", + backgroundColor: "#e5e5e5" + }, + dividerText: { + padding: "0 16px", + fontSize: "14px", + color: "#999" + }, + phoneLogin: { + display: "flex", + flexDirection: "column", + alignItems: "center" + }, + iconLabel: { + fontSize: "14px", + color: "#999", + marginTop: "8px" + }, + iconBtn: { + width: "48px", + height: "48px", + borderRadius: "50%", + backgroundColor: "#fff", + border: "1px solid #e5e5e5", + display: "flex", + alignItems: "center", + justifyContent: "center", + cursor: "pointer", + fontSize: "24px" + } + }; + + return React.createElement( + "div", + { style: styles.page }, + React.createElement("style", null, "input::placeholder{color:#999;opacity:1;}"), + React.createElement( + "div", + { style: styles.logo }, + "羚牛氢能" + ), + React.createElement( + "h1", + { style: styles.title }, + "\u6570\u5B57\u5316\u8D44\u4EA7ONE-OS\u8FD0\u7BA1\u5E73\u53F0" + ), + React.createElement( + "div", + { style: styles.form }, + React.createElement( + "div", + { style: styles.inputGroup }, + React.createElement("input", { + style: styles.input, + type: "text", + placeholder: "请输入账号信息", + value: account, + onChange: handleAccountChange + }) + ), + React.createElement( + "div", + { style: styles.inputGroup }, + React.createElement("input", { + style: styles.input, + type: "password", + placeholder: "请输入密码", + value: password, + onChange: handlePasswordChange + }) + ), + React.createElement( + "label", + { style: styles.checkboxRow }, + React.createElement("input", { + style: styles.checkbox, + type: "checkbox", + checked: agreed, + onChange: handleAgreedChange + }), + React.createElement( + "span", + { style: styles.agreement }, + "阅读并同意", + React.createElement( + "a", + { href: "#", style: styles.link }, + "《用户服务协议》" + ), + "和", + React.createElement( + "a", + { href: "#", style: styles.link }, + "《隐私政策》" + ) + ) + ), + React.createElement( + "button", + { + style: agreed ? styles.loginBtn : styles.loginBtnDisabled, + onClick: handleLogin, + disabled: !agreed + }, + "\u767B\u5F55" + ) + ), + React.createElement( + "div", + { style: styles.divider }, + React.createElement("div", { style: styles.dividerLine }), + React.createElement( + "span", + { style: styles.dividerText }, + "\u5176\u4ED6\u767B\u5F55\u65B9\u5F0F" + ), + React.createElement("div", { style: styles.dividerLine }) + ), + React.createElement( + "div", + { style: styles.phoneLogin }, + React.createElement( + "div", + { style: styles.iconBtn, title: "手机号登录" }, + "\uD83D\uDCF1" + ), + React.createElement( + "span", + { style: styles.iconLabel }, + "授权登录" + ) + ) + ); +}; + +// Axhub 要求必须定义 Component 变量 +var Component = LoginPage; diff --git a/小程序/WorkbenchPage.jsx b/小程序/WorkbenchPage.jsx new file mode 100644 index 0000000..6394a68 --- /dev/null +++ b/小程序/WorkbenchPage.jsx @@ -0,0 +1,232 @@ +const WorkbenchPage = function () { + var _React$useState = React.useState(0); + var activeTab = _React$useState[0]; + var setActiveTab = _React$useState[1]; + + var todos = [ + { id: 1, title: "审批设备巡检报告", status: "待处理", time: "今天" }, + { id: 2, title: "处理加氢站异常告警", status: "进行中", time: "今天" }, + { id: 3, title: "完成月度资产盘点", status: "待处理", time: "本周" } + ]; + + var stats = [ + { label: "待办任务", value: "8", color: "#07c160" }, + { label: "本周完成", value: "23", color: "#576b95" }, + { label: "告警待处理", value: "2", color: "#fa5151" } + ]; + + var navItems = [ + { key: 0, icon: "\uD83D\uDCCA", text: "工作台" }, + { key: 1, icon: "\uD83D\uDCC4", text: "业务" }, + { key: 2, icon: "\uD83D\uDC64", text: "我的" } + ]; + + var handleNavClick = function (key) { + setActiveTab(key); + }; + + var styles = { + page: { + minHeight: "100vh", + backgroundColor: "#f7f7f7", + paddingBottom: "70px", + boxSizing: "border-box" + }, + header: { + backgroundColor: "#fff", + padding: "24px 20px", + marginBottom: "12px" + }, + headerTitle: { + fontSize: "22px", + fontWeight: "600", + color: "#333", + marginBottom: "4px" + }, + headerSub: { + fontSize: "14px", + color: "#999" + }, + statsRow: { + display: "flex", + padding: "0 12px 12px", + marginBottom: "12px" + }, + statCard: { + flex: 1, + backgroundColor: "#fff", + borderRadius: "8px", + padding: "20px 12px", + textAlign: "center", + margin: "0 6px" + }, + statValue: { + fontSize: "24px", + fontWeight: "600", + marginBottom: "8px" + }, + statLabel: { + fontSize: "13px", + color: "#999" + }, + section: { + backgroundColor: "#fff", + padding: "20px", + marginBottom: "12px" + }, + sectionTitle: { + fontSize: "17px", + fontWeight: "600", + color: "#333", + marginBottom: "16px", + display: "flex", + justifyContent: "space-between", + alignItems: "center" + }, + sectionMore: { + fontSize: "14px", + color: "#07c160" + }, + todoItem: { + display: "flex", + alignItems: "center", + padding: "14px 0", + borderBottom: "1px solid #f0f0f0" + }, + todoItemLast: { + borderBottom: "none" + }, + todoLeft: { + flex: 1 + }, + todoTitle: { + fontSize: "15px", + color: "#333", + marginBottom: "4px" + }, + todoMeta: { + fontSize: "12px", + color: "#999" + }, + todoStatus: { + fontSize: "12px", + padding: "4px 10px", + borderRadius: "4px", + backgroundColor: "#e8f5e9", + color: "#07c160" + }, + todoStatusDoing: { + fontSize: "12px", + padding: "4px 10px", + borderRadius: "4px", + backgroundColor: "#fff3e0", + color: "#ff9800" + }, + bottomNav: { + position: "fixed", + bottom: 0, + left: 0, + right: 0, + height: "60px", + backgroundColor: "#fff", + display: "flex", + borderTop: "1px solid #e5e5e5", + boxSizing: "border-box" + }, + navItem: { + flex: 1, + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + cursor: "pointer" + }, + navIcon: { + fontSize: "24px", + marginBottom: "4px" + }, + navText: { + fontSize: "11px", + color: "#999" + }, + navTextActive: { + color: "#07c160" + } + }; + + return React.createElement( + "div", + { style: styles.page }, + React.createElement( + "div", + { style: styles.header }, + React.createElement("div", { style: styles.headerTitle }, "工作台"), + React.createElement("div", { style: styles.headerSub }, "欢迎使用 ONE-OS 运管平台") + ), + React.createElement( + "div", + { style: styles.statsRow }, + stats.map(function (stat, i) { + var valueStyle = { + fontSize: "24px", + fontWeight: "600", + marginBottom: "8px", + color: stat.color + }; + return React.createElement( + "div", + { key: i, style: styles.statCard }, + React.createElement("div", { style: valueStyle }, stat.value), + React.createElement("div", { style: styles.statLabel }, stat.label) + ); + }) + ), + React.createElement( + "div", + { style: styles.section }, + React.createElement( + "div", + { style: styles.sectionTitle }, + React.createElement("span", null, "待办任务"), + React.createElement("span", { style: styles.sectionMore }, "全部") + ), + todos.map(function (todo, i) { + var isLast = i === todos.length - 1; + var statusStyle = todo.status === "进行中" ? styles.todoStatusDoing : styles.todoStatus; + var itemStyle = isLast ? { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "none" } : styles.todoItem; + return React.createElement( + "div", + { key: todo.id, style: itemStyle }, + React.createElement( + "div", + { style: styles.todoLeft }, + React.createElement("div", { style: styles.todoTitle }, todo.title), + React.createElement("div", { style: styles.todoMeta }, todo.time) + ), + React.createElement("span", { style: statusStyle }, todo.status) + ); + }) + ), + React.createElement( + "div", + { style: styles.bottomNav }, + navItems.map(function (item) { + var isActive = activeTab === item.key; + var textStyle = isActive ? { fontSize: "11px", color: "#07c160" } : styles.navText; + return React.createElement( + "div", + { + key: item.key, + style: styles.navItem, + onClick: function () { handleNavClick(item.key); } + }, + React.createElement("div", { style: styles.navIcon }, item.icon), + React.createElement("div", { style: textStyle }, item.text) + ); + }) + ) + ); +}; + +// Axhub 要求必须定义 Component 变量 +var Component = WorkbenchPage; diff --git a/小程序/login-preview.html b/小程序/login-preview.html new file mode 100644 index 0000000..9cb0e35 --- /dev/null +++ b/小程序/login-preview.html @@ -0,0 +1,20 @@ + + + + + + ONE-OS运管平台 + + + + + + +
+ + + + diff --git a/小程序/workbench-preview.html b/小程序/workbench-preview.html new file mode 100644 index 0000000..81dfaeb --- /dev/null +++ b/小程序/workbench-preview.html @@ -0,0 +1,20 @@ + + + + + + 工作台 - 数字化资产ONE-OS运管平台 + + + + + + +
+ + + +