From d3da3da73b316ccd8d85a2667ff0156ad4bea5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Mon, 23 Mar 2026 09:35:39 +0800 Subject: [PATCH] =?UTF-8?q?web=E7=AB=AF=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8F=B0=EF=BC=9B=E5=BC=82=E5=8A=A8=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=A1=B5=E9=9D=A2=E4=B8=8E=E9=9C=80=E6=B1=82=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- web端/工作台.jsx | 488 ++++++++++++++++++ .../{异动管理.新增.jsx => 异动管理-新增.jsx} | 53 +- web端/运维管理/车辆业务/异动管理-查看.jsx | 35 +- web端/运维管理/车辆业务/异动管理-结束异动.jsx | 47 +- web端/运维管理/车辆业务/异动管理-编辑.jsx | 110 ++-- web端/运维管理/车辆业务/异动管理.jsx | 4 +- web端/需求说明/运维管理-车务管理/异动-新增 | 14 +- web端/需求说明/运维管理-车务管理/异动管理 | 29 +- .../运维管理-车务管理/异动管理-结束异动 | 30 ++ 9 files changed, 706 insertions(+), 104 deletions(-) create mode 100644 web端/工作台.jsx rename web端/运维管理/车辆业务/{异动管理.新增.jsx => 异动管理-新增.jsx} (94%) create mode 100644 web端/需求说明/运维管理-车务管理/异动管理-结束异动 diff --git a/web端/工作台.jsx b/web端/工作台.jsx new file mode 100644 index 0000000..25b9383 --- /dev/null +++ b/web端/工作台.jsx @@ -0,0 +1,488 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 数字化资产 ONEOS 运管平台 - 工作台(需求见文件内「查看需求说明」) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var App = antd.App; + var Row = antd.Row; + var Col = antd.Col; + var Card = antd.Card; + var Statistic = antd.Statistic; + var List = antd.List; + var Tag = antd.Tag; + var Tabs = antd.Tabs; + var Badge = antd.Badge; + var Button = antd.Button; + var Space = antd.Space; + var Divider = antd.Divider; + var Modal = antd.Modal; + var Table = antd.Table; + var Popover = antd.Popover; + var Typography = antd.Typography; + var message = antd.message; + + var Text = Typography.Text; + + var layoutStyle = { padding: '16px 20px 24px', background: '#f0f2f5', minHeight: '100vh' }; + var cardStyle = { borderRadius: 8, boxShadow: '0 1px 2px rgba(0,0,0,0.06)' }; + + function protoNav(hint) { + message.info('跳转「' + hint + '」(原型,联调配置路由)'); + } + + var requirementOpenState = useState(false); + var requirementOpen = requirementOpenState[0]; + var setRequirementOpen = requirementOpenState[1]; + + var auditModalState = useState(false); + var auditModalOpen = auditModalState[0]; + var setAuditModalOpen = auditModalState[1]; + + var noticeModalState = useState(false); + var noticeModalOpen = noticeModalState[0]; + var setNoticeModalOpen = noticeModalState[1]; + + var roleTabState = useState('ops'); + var roleTab = roleTabState[0]; + var setRoleTab = roleTabState[1]; + + var requirementDoc = [ + '一个「数字化资产ONEOS运管平台」中的「工作台」模块', + '#面包屑:工作台', + '', + '1.顶部为关键指标,以多个指标卡片分别展示,分别为:', + '1.1.待办工作:显示待办工作数量,包括以下任务:', + ' 1.1.1.运维侧:交车任务、调拨任务、异动任务、年审任务;', + ' 1.1.2.业管侧:商业险到期、租赁账单生成、提车应收款、还车应结款;', + ' 1.1.3.业管-能源部侧:氢费审核;', + ' 权限分配规则:业管、运维基层员工:仅显示当前登录人作为处理人的任务;业管、运维主管:显示所属部门所有未完成任务;', + '1.2.已完成工作:显示已完成工作数量,包括当前用户所有已完成的待办工作;', + '1.3.待审批任务:显示所有待审批任务数量,点击弹出卡片,列表显示流程到达时间、流程类型、发起时间、发起人、操作(去处理);', + '1.4.已审批任务:显示所有已审批任务数量,包括当前用户所有已完成的审批任务;', + '1.5.通知消息:显示所有通知消息数量,点击弹出卡片,列表显示时间、通知类型、内容;(含运维/业管/审批侧各类提醒格式,见需求原文)', + '', + '2.中间分为几个单独卡片:', + '2.1.我的待办清单:左侧;2.2.数据统计(车辆+合同);2.3.我的通知清单:右侧;', + '2.4.底部快速入口:按角色 Tab(业管、业管-能源部、运维、财务、安全、法务),图标+名称。', + '', + '(原型:数据为示例;权限与接口联调时对接。)' + ].join('\n'); + + var todoPopoverContent = React.createElement('div', { style: { maxWidth: 360, fontSize: 12, lineHeight: 1.6 } }, + React.createElement(Text, { strong: true }, '包含任务类型'), + React.createElement('div', { style: { marginTop: 8 } }, + React.createElement('div', null, '【运维】交车、调拨、异动、年审'), + React.createElement('div', null, '【业管】商业险到期、租赁账单生成、提车应收款、还车应结款'), + React.createElement('div', null, '【业管-能源】氢费审核') + ), + React.createElement(Divider, { style: { margin: '8px 0' } }), + React.createElement(Text, { type: 'secondary' }, '基层员工:仅本人待办;主管:本部门全部未完成。') + ); + + var topMetrics = useMemo(function () { + return [ + { key: 'todo', title: '待办工作', value: 18, color: '#722ed1', pop: true }, + { key: 'done', title: '已完成工作', value: 126, color: '#52c41a', pop: false }, + { key: 'auditPending', title: '待审批任务', value: 7, color: '#f5222d', pop: false, modal: 'audit' }, + { key: 'auditDone', title: '已审批任务', value: 89, color: '#1677ff', pop: false }, + { key: 'notice', title: '通知消息', value: 12, color: '#fa8c16', pop: false, modal: 'notice' } + ]; + }, []); + + var myTodoList = useMemo(function () { + return [ + { id: '1', name: '交车任务 · 粤A12345 待确认交车单', time: '2026-02-27 09:00', path: 'web端/运维管理/车辆业务/交车管理.jsx' }, + { id: '2', name: '调拨任务 · 调拨单 DB-2026-009 待接收', time: '2026-02-26 15:20', path: 'web端/运维管理/车辆业务/调拨管理.jsx' }, + { id: '3', name: '异动任务 · 异动单待结束登记', time: '2026-02-26 11:00', path: 'web端/运维管理/车辆业务/异动管理-结束异动.jsx' }, + { id: '4', name: '年审任务 · 粤B11111 年审材料待上传', time: '2026-02-25 10:30', path: 'web端/运维管理/车辆业务/异动管理.jsx' }, + { id: '5', name: '商业险到期 · 粤C22334 续保跟进', time: '2026-02-24 14:00', path: 'web端/车辆管理.jsx' }, + { id: '6', name: '租赁账单生成 · 项目「华南物流」2月账单', time: '2026-02-24 09:00', path: 'web端/业务管理/租赁账单.jsx' }, + { id: '7', name: '提车应收款 · TK-2026-018 待提交', time: '2026-02-23 16:00', path: 'web端/财务管理/提车应收款.jsx' }, + { id: '8', name: '还车应结款 · HC-2026-006 待核对', time: '2026-02-23 11:00', path: 'web端/财务管理/还车应结款.jsx' }, + { id: '9', name: '氢费审核 · 加氢订单待审核', time: '2026-02-22 08:30', path: 'web端/加氢站管理/加氢订单.jsx' } + ]; + }, []); + + var noticeList = useMemo(function () { + return [ + { id: 'n1', time: '2026-02-27 08:00', type: '商业险到期提醒', content: '「粤A12345」商业险将在2026-04-15到期,请尽快处理' }, + { id: 'n2', time: '2026-02-26 18:00', type: '营运证到期提醒', content: '「粤B88888」营运证还有90天到期,请尽快更新营运证' }, + { id: 'n3', time: '2026-02-26 10:00', type: '行驶证到期提醒', content: '「浙A11111」行驶证还有90天到期,请尽快进行年审' }, + { id: 'n4', time: '2026-02-25 14:00', type: '租赁合同到期提醒', content: '「HT-2025-088」「某某产业园项目」还有30天到期,请尽快处理' }, + { id: 'n5', time: '2026-02-25 09:00', type: '租赁账单生成提醒', content: '「HT-2025-088」「某某产业园项目」「ZD-202602-031」已生成,请尽快处理' }, + { id: 'n6', time: '2026-02-24 11:00', type: '氢费余额不足提醒', content: '「某某物流」「华南干线项目」氢费余额已不足500元,请尽快通知客户处理' }, + { id: 'n7', time: '2026-02-24 08:30', type: '审批流程提醒', content: '「李四」「异动审核」审批节点已到达,请进行审批' } + ]; + }, []); + + var pendingAuditList = useMemo(function () { + return [ + { id: 'a1', arriveAt: '2026-02-27 09:15', flowType: '异动审核(运维)', startAt: '2026-02-26 14:00', starter: '张三', path: 'web端/运维管理/车辆业务/异动管理.jsx' }, + { id: 'a2', arriveAt: '2026-02-27 08:40', flowType: '调拨审核(运维)', startAt: '2026-02-25 10:00', starter: '王五', path: 'web端/运维管理/车辆业务/调拨管理.jsx' }, + { id: 'a3', arriveAt: '2026-02-26 16:00', flowType: '替换车审核(运维)', startAt: '2026-02-26 09:30', starter: '赵六', path: 'web端/运维管理/车辆业务/替换车管理.jsx' }, + { id: 'a4', arriveAt: '2026-02-26 11:20', flowType: '租赁账单审核(财务)', startAt: '2026-02-25 17:00', starter: '业管-陈七', path: 'web端/财务管理/租赁账单.jsx' }, + { id: 'a5', arriveAt: '2026-02-25 15:00', flowType: '提车应收款审核(财务)', startAt: '2026-02-24 11:00', starter: '业管-周八', path: 'web端/财务管理/提车应收款.jsx' }, + { id: 'a6', arriveAt: '2026-02-25 10:00', flowType: '还车应结款审核(财务)', startAt: '2026-02-23 16:00', starter: '业管-吴九', path: 'web端/财务管理/还车应结款.jsx' }, + { id: 'a7', arriveAt: '2026-02-24 14:00', flowType: '氢费账单审核(财务)', startAt: '2026-02-23 09:00', starter: '能源-郑十', path: 'web端/财务管理/氢费账单.jsx' }, + { id: 'a8', arriveAt: '2026-02-24 09:00', flowType: '租赁合同审核(法务·附件)', startAt: '2026-02-22 10:00', starter: '业管-钱一', path: 'web端/车辆租赁合同/车辆租赁合同.jsx' }, + { id: 'a9', arriveAt: '2026-02-23 11:00', flowType: 'CEO · 提车应收款', startAt: '2026-02-20 15:00', starter: '财务-孙二', path: 'web端/财务管理/提车应收款.jsx' } + ]; + }, []); + + var vehicleMonthBars = useMemo(function () { + return [ + { m: '3月', op: 62, idle: 18 }, { m: '4月', op: 65, idle: 16 }, { m: '5月', op: 68, idle: 15 }, + { m: '6月', op: 70, idle: 14 }, { m: '7月', op: 72, idle: 13 }, { m: '8月', op: 75, idle: 12 }, + { m: '9月', op: 78, idle: 12 }, { m: '10月', op: 80, idle: 11 }, { m: '11月', op: 82, idle: 11 }, + { m: '12月', op: 85, idle: 10 }, { m: '1月', op: 88, idle: 10 }, { m: '2月', op: 90, idle: 9 } + ]; + }, []); + + var contractMonthBars = useMemo(function () { + return [ + { m: '3月', lease: 8, self: 3 }, { m: '4月', lease: 9, self: 3 }, { m: '5月', lease: 10, self: 4 }, + { m: '6月', lease: 10, self: 4 }, { m: '7月', lease: 11, self: 4 }, { m: '8月', lease: 12, self: 5 }, + { m: '9月', lease: 12, self: 5 }, { m: '10月', lease: 13, self: 5 }, { m: '11月', lease: 14, self: 6 }, + { m: '12月', lease: 14, self: 6 }, { m: '1月', lease: 15, self: 6 }, { m: '2月', lease: 16, self: 7 } + ]; + }, []); + + function renderBarGroup(items, k1, k2, c1, c2) { + var max = 1; + items.forEach(function (it) { max = Math.max(max, it[k1] + it[k2]); }); + return React.createElement('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 4, height: 140, paddingTop: 8, overflowX: 'auto' } }, + items.map(function (it) { + var h1 = Math.round((it[k1] / max) * 100); + var h2 = Math.round((it[k2] / max) * 100); + return React.createElement('div', { key: it.m, style: { display: 'flex', flexDirection: 'column', alignItems: 'center', minWidth: 26 } }, + React.createElement('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 2, height: 108 } }, + React.createElement('div', { style: { width: 9, height: h1, background: c1, borderRadius: 2 } }), + React.createElement('div', { style: { width: 9, height: h2, background: c2, borderRadius: 2 } }) + ), + React.createElement('span', { style: { fontSize: 10, color: 'rgba(0,0,0,0.45)', marginTop: 4 } }, it.m) + ); + }) + ); + } + + var quickByRole = useMemo(function () { + return { + ye: { + label: '业管', + items: [ + { t: '租赁合同', p: 'web端/车辆租赁合同/车辆租赁合同.jsx' }, + { t: '交车任务', p: 'web端/业务管理/交车任务.jsx' }, + { t: '提车应收款', p: 'web端/财务管理/提车应收款.jsx' }, + { t: '租赁账单', p: 'web端/业务管理/租赁账单.jsx' }, + { t: '还车应结款', p: 'web端/财务管理/还车应结款.jsx' }, + { t: '能源账户', p: 'web端/业务管理/能源账户.jsx' }, + { t: '氢费账单', p: 'web端/财务管理/氢费账单.jsx' }, + { t: '电费账单', p: 'web端/财务管理/电费账单.jsx' }, + { t: 'ETC账单', p: 'web端/业务管理/ETC管理.jsx' }, + { t: '保险管理', p: 'web端/业务管理/保险管理.jsx' }, + { t: '审批中心', p: 'web端/审批中心.jsx' } + ] + }, + yeEnergy: { + label: '业管-能源部', + items: [ + { t: '加氢订单管理', p: 'web端/加氢站管理/加氢订单.jsx' } + ] + }, + ops: { + label: '运维', + items: [ + { t: '车辆管理', p: 'web端/车辆管理.jsx' }, + { t: '证照管理', p: 'web端/运维管理/车辆业务/证照管理.jsx' }, + { t: '备车管理', p: 'web端/运维管理/车辆业务/备车管理.jsx' }, + { t: '交车管理', p: 'web端/运维管理/车辆业务/交车管理.jsx' }, + { t: '还车管理', p: 'web端/运维管理/车辆业务/还车管理.jsx' }, + { t: '替换车管理', p: 'web端/运维管理/车辆业务/替换车管理.jsx' }, + { t: '调拨管理', p: 'web端/运维管理/车辆业务/调拨管理.jsx' }, + { t: '异动管理', p: 'web端/运维管理/车辆业务/异动管理.jsx' }, + { t: '备件库管理', p: 'web端/运维管理/备件管理/备件库存.jsx' } + ] + }, + finance: { + label: '财务', + items: [ + { t: '提车应收款', p: 'web端/财务管理/提车应收款.jsx' }, + { t: '租赁账单', p: 'web端/财务管理/租赁账单.jsx' }, + { t: '还车应结款', p: 'web端/财务管理/还车应结款.jsx' }, + { t: '审批中心', p: 'web端/审批中心.jsx' }, + { t: '充值单管理', p: 'web端/财务管理/充值单管理.jsx' } + ] + }, + safety: { + label: '安全', + items: [ + { t: '违章管理', p: 'web端/安全管理/违章管理.jsx' }, + { t: '事故管理', p: 'web端/安全管理/事故管理.jsx' }, + { t: '司机管理', p: 'web端/安全管理/司机管理.jsx' }, + { t: '安全培训资料', p: 'web端/安全管理/安全培训资料.jsx' }, + { t: '安全培训记录', p: 'web端/安全管理/安全培训记录.jsx' } + ] + }, + legal: { + label: '法务', + items: [ + { t: '审批中心', p: 'web端/审批中心.jsx' } + ] + } + }; + }, []); + + var auditColumns = useMemo(function () { + return [ + { title: '流程到达时间', dataIndex: 'arriveAt', key: 'arriveAt', width: 150 }, + { title: '流程类型', dataIndex: 'flowType', key: 'flowType', ellipsis: true }, + { title: '发起时间', dataIndex: 'startAt', key: 'startAt', width: 140 }, + { title: '发起人', dataIndex: 'starter', key: 'starter', width: 100 }, + { + title: '操作', + key: 'act', + width: 88, + render: function (_, r) { + return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { protoNav(r.path); } }, '去处理'); + } + } + ]; + }, []); + + var noticeColumns = useMemo(function () { + return [ + { title: '时间', dataIndex: 'time', key: 'time', width: 150 }, + { title: '通知类型', dataIndex: 'type', key: 'type', width: 160 }, + { title: '内容', dataIndex: 'content', key: 'content', ellipsis: true } + ]; + }, []); + + function renderMetricCard(m) { + var inner = React.createElement(Card, { + size: 'small', + bordered: false, + style: Object.assign({}, cardStyle, { minHeight: 108, width: '100%', flex: 1, display: 'flex', flexDirection: 'column' }), + bodyStyle: { + padding: '16px 12px', + cursor: m.modal || m.pop ? 'pointer' : 'default', + flex: 1, + display: 'flex', + alignItems: 'flex-start', + justifyContent: 'center' + }, + onClick: function () { + if (m.modal === 'audit') setAuditModalOpen(true); + else if (m.modal === 'notice') setNoticeModalOpen(true); + } + }, + React.createElement(Statistic, { + title: React.createElement('span', { style: { fontSize: 13 } }, m.title), + value: m.value, + valueStyle: { color: m.color, fontWeight: 600, fontSize: 24 } + }) + ); + + if (m.pop) { + return React.createElement(Popover, { content: todoPopoverContent, title: '待办工作说明', trigger: 'click' }, + React.createElement('div', { style: { display: 'flex', flex: 1, width: '100%', minWidth: 0 } }, inner)); + } + return inner; + } + + var quickTabItems = useMemo(function () { + return ['ye', 'yeEnergy', 'ops', 'finance', 'safety', 'legal'].map(function (k) { + var g = quickByRole[k]; + return { + key: k, + label: g.label, + children: React.createElement(Row, { gutter: [12, 12] }, + g.items.map(function (it) { + return React.createElement(Col, { xs: 8, sm: 6, md: 4, lg: 3, key: it.t }, + React.createElement(Card, { + size: 'small', + hoverable: true, + bodyStyle: { padding: '12px 8px', textAlign: 'center' }, + onClick: function () { protoNav(it.p); } + }, + React.createElement('div', { + style: { + width: 40, height: 40, margin: '0 auto 8px', borderRadius: 8, + background: 'linear-gradient(135deg,#e6f4ff,#bae0ff)', lineHeight: '40px', fontSize: 18 + } + }, it.t.charAt(0)), + React.createElement('div', { style: { fontSize: 12, fontWeight: 500 } }, it.t) + ) + ); + }) + ) + }; + }); + }, [quickByRole]); + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8, marginBottom: 8 } }, + React.createElement(Space, null, + React.createElement('span', { style: { fontSize: 20, fontWeight: 600, color: 'rgba(0,0,0,0.85)' } }, '工作台'), + React.createElement(Tag, { color: 'default' }, '工作台') + ), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { setRequirementOpen(true); } }, '查看需求说明') + ), + + React.createElement(Modal, { + title: '需求说明', + open: requirementOpen, + width: 760, + onCancel: function () { setRequirementOpen(false); }, + footer: React.createElement(Button, { onClick: function () { setRequirementOpen(false); } }, '关闭'), + bodyStyle: { maxHeight: '72vh', overflow: 'auto' } + }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.65, color: 'rgba(0,0,0,0.85)' } }, requirementDoc)), + + React.createElement(Modal, { + title: '待审批任务', + open: auditModalOpen, + width: 900, + onCancel: function () { setAuditModalOpen(false); }, + footer: React.createElement(Button, { onClick: function () { setAuditModalOpen(false); } }, '关闭'), + destroyOnClose: true + }, + React.createElement(Table, { + size: 'small', + rowKey: 'id', + columns: auditColumns, + dataSource: pendingAuditList, + pagination: { pageSize: 8, showSizeChanger: false } + }) + ), + + React.createElement(Modal, { + title: '通知消息', + open: noticeModalOpen, + width: 860, + onCancel: function () { setNoticeModalOpen(false); }, + footer: React.createElement(Button, { onClick: function () { setNoticeModalOpen(false); } }, '关闭'), + destroyOnClose: true + }, + React.createElement(Table, { + size: 'small', + rowKey: 'id', + columns: noticeColumns, + dataSource: noticeList, + pagination: { pageSize: 8, showSizeChanger: false } + }) + ), + + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 16, marginBottom: 16, alignItems: 'stretch' } }, + topMetrics.map(function (m) { + return React.createElement('div', { key: m.key, style: { flex: '1 1 160px', minWidth: 148, maxWidth: '100%', display: 'flex' } }, renderMetricCard(m)); + }) + ), + + React.createElement(Row, { gutter: [16, 16], style: { marginBottom: 16 } }, + React.createElement(Col, { xs: 24, xl: 7 }, + React.createElement(Card, { + title: React.createElement(Space, null, '我的待办清单', React.createElement(Badge, { count: myTodoList.length, style: { backgroundColor: '#1677ff' } })), + size: 'small', + bordered: false, + style: cardStyle, + bodyStyle: { maxHeight: 520, overflow: 'auto' } + }, + React.createElement(List, { + size: 'small', + dataSource: myTodoList, + locale: { emptyText: '暂无待办' }, + renderItem: function (item) { + return React.createElement(List.Item, { + actions: [ + React.createElement(Button, { type: 'link', key: 'go', size: 'small', onClick: function () { protoNav(item.path); } }, '去处理') + ] + }, + React.createElement(List.Item.Meta, { + title: React.createElement('span', { style: { fontSize: 13 } }, item.name), + description: React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, '生成时间:' + item.time) + }) + ); + } + }) + ) + ), + React.createElement(Col, { xs: 24, xl: 10 }, + React.createElement(Card, { title: '数据统计', size: 'small', bordered: false, style: Object.assign({ marginBottom: 16 }, cardStyle) }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12, fontSize: 13 } }, '车辆数据统计'), + React.createElement(Row, { gutter: 8 }, + [ + { label: '车辆总数', v: 248 }, + { label: '自营车辆数', v: 32 }, + { label: '库存车辆数', v: 30 }, + { label: '待运营车辆数', v: 12 } + ].map(function (s) { + return React.createElement(Col, { span: 12, key: s.label }, + React.createElement(Card, { size: 'small', bodyStyle: { padding: '10px 8px' } }, + React.createElement(Statistic, { title: s.label, value: s.v, valueStyle: { fontSize: 18 } }) + ) + ); + }) + ), + React.createElement(Divider, { plain: true, style: { margin: '12px 0 8px', fontSize: 12 } }, '近12个月 · 运营车辆 / 闲置车辆'), + React.createElement('div', { style: { fontSize: 11, color: 'rgba(0,0,0,0.45)', marginBottom: 6 } }, '蓝色:运营 浅蓝:闲置(原型双柱示意)'), + renderBarGroup(vehicleMonthBars, 'op', 'idle', '#1677ff', '#91caff') + ), + React.createElement(Card, { title: '合同数据统计', size: 'small', bordered: false, style: cardStyle }, + React.createElement(Row, { gutter: 16 }, + React.createElement(Col, { span: 12 }, React.createElement(Statistic, { title: '租赁合同总数', value: 58, valueStyle: { color: '#722ed1' } })), + React.createElement(Col, { span: 12 }, React.createElement(Statistic, { title: '自营合同总数', value: 14, valueStyle: { color: '#52c41a' } })) + ), + React.createElement(Divider, { plain: true, style: { margin: '12px 0 8px', fontSize: 12 } }, '近12个月 · 租赁合同 / 自营合同'), + React.createElement('div', { style: { fontSize: 11, color: 'rgba(0,0,0,0.45)', marginBottom: 6 } }, '紫色:租赁 浅紫:自营(原型双柱示意)'), + renderBarGroup(contractMonthBars, 'lease', 'self', '#722ed1', '#b37feb') + ) + ), + React.createElement(Col, { xs: 24, xl: 7 }, + React.createElement(Card, { + title: React.createElement(Space, null, '我的通知清单', React.createElement(Badge, { count: noticeList.length })), + size: 'small', + bordered: false, + style: cardStyle, + extra: React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { setNoticeModalOpen(true); } }, '全部') + }, + React.createElement('div', { style: { maxHeight: 480, overflow: 'auto' } }, + React.createElement(List, { + size: 'small', + dataSource: noticeList.slice(0, 8), + renderItem: function (n) { + return React.createElement(List.Item, { style: { padding: '10px 0' } }, + React.createElement(List.Item.Meta, { + title: React.createElement(Space, { wrap: true, size: 4 }, + React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, n.time), + React.createElement(Tag, null, n.type) + ), + description: React.createElement('span', { style: { fontSize: 12, color: 'rgba(0,0,0,0.75)' } }, n.content) + }) + ); + } + }) + ) + ) + ) + ), + + React.createElement(Card, { + title: '快速入口(按角色)', + size: 'small', + bordered: false, + style: cardStyle, + extra: React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, '多角色时 Tab 切换;联调按权限过滤') + }, + React.createElement(Tabs, { + activeKey: roleTab, + onChange: setRoleTab, + items: quickTabItems + }) + ) + ) + ); +}; diff --git a/web端/运维管理/车辆业务/异动管理.新增.jsx b/web端/运维管理/车辆业务/异动管理-新增.jsx similarity index 94% rename from web端/运维管理/车辆业务/异动管理.新增.jsx rename to web端/运维管理/车辆业务/异动管理-新增.jsx index bb653d2..91ba2e0 100644 --- a/web端/运维管理/车辆业务/异动管理.新增.jsx +++ b/web端/运维管理/车辆业务/异动管理-新增.jsx @@ -1,5 +1,5 @@ // 【重要】必须使用 const Component 作为组件变量名 -// 运维管理 - 车辆业务 - 异动管理 - 新增 +// 运维管理 - 车辆业务 - 异动管理 - 新增(与「异动管理-编辑」同步维护) const Component = function () { var useState = React.useState; @@ -108,8 +108,7 @@ const Component = function () { '1.3.异动目的地:必填项,选择器,选项为:停车场、维修站、其他;', '2.4.目的地名称:必填项,如异动目的地为停车场,则此处为停车场(选择器),如异动目的地为维修站,则此处为维修站(选择器),如异动目的地为其他,则此处为输入框(自定义输入);', '2.5.异动类型:必填项,选择器,选项为:维修、保养、年审、其他;', - '2.6.预计异动里程:输入框,支持2位小数输入,后缀为km;', - '2.7.备注:文本域,支持自定义输入;', + '2.6.备注:文本域,支持自定义输入;', '', '2.车辆信息:', '2.1.车牌号:显示车牌号,支持输入框模糊搜索下拉匹配对应选项;', @@ -117,11 +116,12 @@ const Component = function () { '2.3.品牌:根据所选车辆品牌自动反写,默认提示为请先选择车辆;', '2.4.型号:根据所选车辆型号自动反写,默认提示为请先选择车辆;', '2.5.出发停车场:根据所选车辆出发时停车场自动反写,默认提示为请先选择车辆;', - '2.6.异动开始里程:必填项,输入框,精确至2位小数,后缀为km;', - '2.7.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', - '2.8.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh;', - '2.9.操作:删除;', - '2.10.新增一行:铺满整行;', + '2.6.预计异动里程:输入框,支持2位小数输入,后缀为km;', + '2.7.异动开始里程:必填项,输入框,精确至2位小数,后缀为km;', + '2.8.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', + '2.9.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh;', + '2.10.操作:删除;', + '2.11.新增一行:铺满整行;', '', '3.底部为提交审核、保存、取消;' ].join('\n'); @@ -133,7 +133,6 @@ const Component = function () { destinationName: undefined, destinationNameOther: '', changeType: undefined, - plannedMileageKm: '', remark: '' }); var form = formState[0]; @@ -153,6 +152,7 @@ const Component = function () { model: '', departParking: '', h2Unit: '', + plannedMileageKm: '', startMileageKm: '', startHydrogen: '', startElectricKwh: '' @@ -176,14 +176,16 @@ const Component = function () { var handlePlateChange = useCallback(function (index, plateNo) { var v = vehicleDb.find(function (x) { return x.plateNo === plateNo; }); - updateRow(index, { + var patch = { plateNo: plateNo, vehicleType: v ? v.vehicleType : '', brand: v ? v.brand : '', model: v ? v.model : '', departParking: v ? v.departParking : '', h2Unit: v ? v.h2Unit : '' - }); + }; + if (!v) patch.plannedMileageKm = ''; + updateRow(index, patch); }, [vehicleDb, updateRow]); var addRow = useCallback(function () { @@ -196,6 +198,7 @@ const Component = function () { model: '', departParking: '', h2Unit: '', + plannedMileageKm: '', startMileageKm: '', startHydrogen: '', startElectricKwh: '' @@ -216,6 +219,7 @@ const Component = function () { model: '', departParking: '', h2Unit: '', + plannedMileageKm: '', startMileageKm: '', startHydrogen: '', startElectricKwh: '' @@ -243,6 +247,7 @@ const Component = function () { for (var i = 0; i < (vehicles || []).length; i++) { var r = vehicles[i] || {}; if (!String(r.plateNo || '').trim()) continue; + if (!String(r.plannedMileageKm || '').trim()) e['row_' + i + '_plannedMileageKm'] = '请输入预计异动里程'; if (!String(r.startMileageKm || '').trim()) e['row_' + i + '_startMileageKm'] = '请输入异动开始里程'; if (!String(r.startHydrogen || '').trim()) e['row_' + i + '_startHydrogen'] = '请输入异动开始氢量'; if (!String(r.startElectricKwh || '').trim()) e['row_' + i + '_startElectricKwh'] = '请输入异动开始电量'; @@ -362,6 +367,21 @@ const Component = function () { return React.createElement(Input, { value: r.departParking ? r.departParking : '请先选择车辆', disabled: true }); } }, + { + title: React.createElement('span', null, reqStar, '预计异动里程'), + key: 'plannedMileageKm', + width: 160, + render: function (_, r, index) { + var k = 'row_' + index + '_plannedMileageKm'; + return React.createElement(Input, { + value: r.plannedMileageKm, + onChange: function (e) { updateRow(index, { plannedMileageKm: toFixed2Input(e.target.value) }); }, + placeholder: '0.00', + addonAfter: 'km', + status: errors[k] ? 'error' : undefined + }); + } + }, { title: React.createElement('span', null, reqStar, '异动开始里程'), key: 'startMileageKm', @@ -504,15 +524,6 @@ const Component = function () { }), errors.changeType ? React.createElement('div', { style: { marginTop: 4, color: '#ff4d4f', fontSize: 12 } }, errors.changeType) : null ), - React.createElement('div', { style: formItemStyle }, - React.createElement('div', { style: labelStyle }, '预计异动里程'), - React.createElement(Input, { - placeholder: '0.00', - value: form.plannedMileageKm, - onChange: function (e) { updateForm({ plannedMileageKm: toFixed2Input(e.target.value) }); }, - addonAfter: 'km' - }) - ), React.createElement('div', { style: Object.assign({}, formItemStyle, { gridColumn: 'span 3' }) }, React.createElement('div', { style: labelStyle }, '备注'), @@ -534,7 +545,7 @@ const Component = function () { dataSource: vehicles, size: 'small', pagination: false, - scroll: { x: 1250 } + scroll: { x: 1410 } }), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRow }, '新增一行') ), diff --git a/web端/运维管理/车辆业务/异动管理-查看.jsx b/web端/运维管理/车辆业务/异动管理-查看.jsx index 98225d7..8f77de5 100644 --- a/web端/运维管理/车辆业务/异动管理-查看.jsx +++ b/web端/运维管理/车辆业务/异动管理-查看.jsx @@ -82,7 +82,6 @@ const Component = function () { destinationType: '维修站', destinationName: '广州天河维修站', changeType: '维修', - plannedMileageKm: '45.50', remark: '车辆需进站检修制动系统,预计两日内完成。', approvalStatus: '审批中', endDateTime: getInitialDateTime('2026-02-22 17:45') @@ -99,6 +98,7 @@ const Component = function () { brand: '东风', model: 'DFH1180', departParking: '天河智慧停车场', + plannedMileageKm: '45.50', startMileageKm: '15230.12', startHydrogen: '28.30', h2Unit: 'MPa', @@ -159,6 +159,21 @@ const Component = function () { return React.createElement(Input, { value: rowVehicleSelected(r) ? (r.departParking || '-') : '请先选择车辆', disabled: true }); } }, + { + title: '预计异动里程', + key: 'plannedMileageKm', + width: 160, + render: function (_, r) { + if (!rowVehicleSelected(r)) { + return React.createElement(Input, { value: '', disabled: true, placeholder: placeholderSelectVehicle }); + } + return React.createElement(Input, { + value: fmtPlannedMileage(r.plannedMileageKm), + disabled: true, + addonAfter: 'km' + }); + } + }, { title: '异动开始里程', key: 'startMileageKm', @@ -256,8 +271,8 @@ const Component = function () { '页面布局与字段与「异动管理-结束异动」一致;另含「审批状态」只读展示。', '所有表单项、表格单元均为禁用只读,仅供查询展示。', '', - '1.异动情况:异动开始/预计结束日期、异动目的地、目的地名称、异动类型、预计异动里程、审批状态、异动结束时间、备注 — 全部只读。', - '2.车辆信息:与结束异动表列一致(含异动结束里程/氢量/电量);仅展示单辆车(表格 1 行)— 全部只读;无新增行与删除。', + '1.异动情况:第一行开始/预计结束/目的地;第二行目的地名称、异动类型、审批状态(第三列);第三行异动结束时间(首列);备注跨三列 — 全部只读(不含预计异动里程)。', + '2.车辆信息:在出发停车场后展示预计异动里程(用例值,只读);其余与结束异动表列一致(含开始/结束里程、氢量、电量);仅 1 行;无新增行与删除。', '3.底部仅「返回」按钮,返回异动管理列表(原型)。' ].join('\n'); @@ -323,14 +338,6 @@ const Component = function () { disabled: true }) ), - React.createElement('div', { style: formItemStyle }, - React.createElement('div', { style: labelStyle }, '预计异动里程'), - React.createElement(Input, { - value: fmtPlannedMileage(movementReadonly.plannedMileageKm), - disabled: true, - addonAfter: 'km' - }) - ), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '审批状态'), React.createElement(Select, { @@ -340,8 +347,6 @@ const Component = function () { disabled: true }) ), - React.createElement('div', { style: formItemStyle }), - React.createElement('div', { style: formItemStyle }), React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '异动结束时间'), React.createElement(DatePicker, { @@ -353,6 +358,8 @@ const Component = function () { inputReadOnly: true }) ), + React.createElement('div', { style: formItemStyle }), + React.createElement('div', { style: formItemStyle }), React.createElement('div', { style: Object.assign({}, formItemStyle, { gridColumn: 'span 3' }) }, React.createElement('div', { style: labelStyle }, '备注'), React.createElement(Input.TextArea, { @@ -371,7 +378,7 @@ const Component = function () { dataSource: vehicles, size: 'small', pagination: false, - scroll: { x: 1780 } + scroll: { x: 1940 } }) ), diff --git a/web端/运维管理/车辆业务/异动管理-结束异动.jsx b/web端/运维管理/车辆业务/异动管理-结束异动.jsx index d3c226b..c7e7ff0 100644 --- a/web端/运维管理/车辆业务/异动管理-结束异动.jsx +++ b/web端/运维管理/车辆业务/异动管理-结束异动.jsx @@ -95,7 +95,6 @@ const Component = function () { destinationType: '其他', destinationName: '车管所检测线', changeType: '年审', - plannedMileageKm: '8.20', remark: '年审上线检测,预计半日完成。' }; }, []); @@ -109,6 +108,7 @@ const Component = function () { brand: '比亚迪', model: '汉', departParking: '南山科技园停车场', + plannedMileageKm: '8.20', startMileageKm: '8020.50', startHydrogen: '55.00', h2Unit: '%', @@ -130,7 +130,7 @@ const Component = function () { }); }, []); - // 1.7 异动结束时间:必填,YYYY-MM-DD HH:mm + // 1.6 异动结束时间:必填,YYYY-MM-DD HH:mm var endDateTimeState = useState(getInitialDateTime('2026-02-24 11:30')); var endDateTime = endDateTimeState[0]; var setEndDateTime = endDateTimeState[1]; @@ -182,6 +182,21 @@ const Component = function () { return React.createElement(Input, { value: rowVehicleSelected(r) ? (r.departParking || '-') : '请先选择车辆', disabled: true }); } }, + { + title: '预计异动里程', + key: 'plannedMileageKm', + width: 160, + render: function (_, r) { + if (!rowVehicleSelected(r)) { + return React.createElement(Input, { value: '', disabled: true, placeholder: placeholderSelectVehicle }); + } + return React.createElement(Input, { + value: fmtPlannedMileage(r.plannedMileageKm), + disabled: true, + addonAfter: 'km' + }); + } + }, { title: '异动开始里程', key: 'startMileageKm', @@ -286,9 +301,8 @@ const Component = function () { '1.3.异动目的地:显示异动目的地,包括:停车场、维修站、其他;', '1.4.目的地名称:显示目的地名称,包括:停车场名称、维修站名称、其他;', '1.5.异动类型:显示异动类型,包括:维修、保养、年审、其他;', - '1.6.预计异动里程:显示预计异动里程,支持2位小数,后缀为km;', - '1.7.异动结束时间:必填项,日期选择器,格式为:YYYY-MM-DD HH:MM;', - '1.8.备注:显示备注信息;', + '1.6.异动结束时间:必填项,日期选择器,格式为:YYYY-MM-DD HH:MM;', + '1.7.备注:显示备注信息;', '', '2.车辆信息:', '2.1.车牌号:输入框(禁用),显示车牌号;', @@ -296,13 +310,14 @@ const Component = function () { '2.3.品牌:根据所选车辆品牌自动反写,默认提示为请先选择车辆;', '2.4.型号:根据所选车辆型号自动反写,默认提示为请先选择车辆;', '2.5.出发停车场:根据所选车辆出发时停车场自动反写,默认提示为请先选择车辆;', - '2.6.异动开始里程:输入框(禁用),精确至2位小数,后缀为km;', - '2.7.异动结束里程:必填项,输入框,精确至2位小数,后缀为km;', - '2.8.异动开始氢量:输入框(禁用),精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', - '2.9.异动结束氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', - '2.10.异动开始电量:输入框(禁用),精确至2位小数,后缀为kWh;', - '2.11.异动结束电量:必填项,输入框,精确至2位小数,后缀为kWh;', + '2.6.预计异动里程:显示预计异动里程,支持2位小数,后缀为km;', + '2.7.异动开始里程:输入框(禁用),精确至2位小数,后缀为km;', + '2.8.异动结束里程:必填项,输入框,精确至2位小数,后缀为km;', + '2.9.异动开始氢量:输入框(禁用),精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', + '2.10.异动结束氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', + '2.11.异动开始电量:输入框(禁用),精确至2位小数,后缀为kWh;', '2.12.异动结束电量:必填项,输入框,精确至2位小数,后缀为kWh;', + '2.13.异动结束电量:必填项,输入框,精确至2位小数,后缀为kWh;', '', '3.底部为提交、取消;', '3.1.提交:提交进行必填项校验,提交成功后计入历史记录;', @@ -401,14 +416,6 @@ const Component = function () { disabled: true }) ), - React.createElement('div', { style: formItemStyle }, - React.createElement('div', { style: labelStyle }, '预计异动里程'), - React.createElement(Input, { - value: fmtPlannedMileage(movementReadonly.plannedMileageKm), - disabled: true, - addonAfter: 'km' - }) - ), React.createElement('div', { style: formItemStyle }, labelWithRequired('异动结束时间', true), React.createElement(DatePicker, { @@ -438,7 +445,7 @@ const Component = function () { dataSource: vehicles, size: 'small', pagination: false, - scroll: { x: 1780 } + scroll: { x: 1940 } }) ), diff --git a/web端/运维管理/车辆业务/异动管理-编辑.jsx b/web端/运维管理/车辆业务/异动管理-编辑.jsx index 62e2969..0050cdb 100644 --- a/web端/运维管理/车辆业务/异动管理-编辑.jsx +++ b/web端/运维管理/车辆业务/异动管理-编辑.jsx @@ -1,5 +1,5 @@ // 【重要】必须使用 const Component 作为组件变量名 -// 运维管理 - 车辆业务 - 异动管理 - 编辑 +// 运维管理 - 车辆业务 - 异动管理 - 编辑(与「异动管理-新增」同步维护) const Component = function () { var useState = React.useState; @@ -37,6 +37,16 @@ const Component = function () { return s; } + function getInitialDateTime(str) { + try { + if (window.dayjs) return window.dayjs(str); + } catch (e1) {} + try { + if (window.moment) return window.moment(str, 'YYYY-MM-DD HH:mm'); + } catch (e2) {} + return null; + } + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; var labelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; var formItemStyle = { marginBottom: 12 }; @@ -101,6 +111,7 @@ const Component = function () { var requirementDocContent = [ '一个「数字化资产ONEOS运管平台」中的「异动管理」「编辑」模块', '#面包屑:运维管理-车辆业务-异动管理-编辑', + '(原型)进入页面预填用例数据,联调时由详情接口按异动 ID 回填。', '', '1.异动情况;', '1.1.异动开始日期:必填项,日期选择器,格式为:YYYY-MM-DD HH:MM;', @@ -108,8 +119,7 @@ const Component = function () { '1.3.异动目的地:必填项,选择器,选项为:停车场、维修站、其他;', '2.4.目的地名称:必填项,如异动目的地为停车场,则此处为停车场(选择器),如异动目的地为维修站,则此处为维修站(选择器),如异动目的地为其他,则此处为输入框(自定义输入);', '2.5.异动类型:必填项,选择器,选项为:维修、保养、年审、其他;', - '2.6.预计异动里程:输入框,支持2位小数输入,后缀为km;', - '2.7.备注:文本域,支持自定义输入;', + '2.6.备注:文本域,支持自定义输入;', '', '2.车辆信息:', '2.1.车牌号:显示车牌号,支持输入框模糊搜索下拉匹配对应选项;', @@ -117,24 +127,25 @@ const Component = function () { '2.3.品牌:根据所选车辆品牌自动反写,默认提示为请先选择车辆;', '2.4.型号:根据所选车辆型号自动反写,默认提示为请先选择车辆;', '2.5.出发停车场:根据所选车辆出发时停车场自动反写,默认提示为请先选择车辆;', - '2.6.异动开始里程:必填项,输入框,精确至2位小数,后缀为km;', - '2.7.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', - '2.8.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh;', - '2.9.操作:删除;', - '2.10.新增一行:铺满整行;', + '2.6.预计异动里程:输入框,支持2位小数输入,后缀为km;', + '2.7.异动开始里程:必填项,输入框,精确至2位小数,后缀为km;', + '2.8.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取;', + '2.9.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh;', + '2.10.操作:删除;', + '2.11.新增一行:铺满整行;', '', '3.底部为提交审核、保存、取消;' ].join('\n'); + // 原型:进入编辑页时由详情接口回填;以下为联调前用例数据 var formState = useState({ - startTime: null, - plannedEndTime: null, - destinationType: undefined, - destinationName: undefined, + startTime: getInitialDateTime('2026-02-20 09:30'), + plannedEndTime: getInitialDateTime('2026-02-22 18:00'), + destinationType: '维修站', + destinationName: '广州天河维修站', destinationNameOther: '', - changeType: undefined, - plannedMileageKm: '', - remark: '' + changeType: '维修', + remark: '车辆需进站检修制动系统,预计两日内完成。' }); var form = formState[0]; var setForm = formState[1]; @@ -143,19 +154,33 @@ const Component = function () { var errors = errorsState[0]; var setErrors = errorsState[1]; - var rowIdRef = React.useRef(2); + var rowIdRef = React.useRef(3); var vehiclesState = useState([ { id: 1, - plateNo: undefined, - vehicleType: '', - brand: '', - model: '', - departParking: '', - h2Unit: '', - startMileageKm: '', - startHydrogen: '', - startElectricKwh: '' + plateNo: '粤A12345', + vehicleType: '厢式货车', + brand: '东风', + model: 'DFH1180', + departParking: '天河智慧停车场', + h2Unit: 'MPa', + plannedMileageKm: '45.50', + startMileageKm: '15230.12', + startHydrogen: '28.30', + startElectricKwh: '68.40' + }, + { + id: 2, + plateNo: '浙A11111', + vehicleType: 'SUV', + brand: '小鹏', + model: 'P7', + departParking: '西湖景区停车场', + h2Unit: '%', + plannedMileageKm: '120.00', + startMileageKm: '12010.00', + startHydrogen: '60.00', + startElectricKwh: '55.20' } ]); var vehicles = vehiclesState[0]; @@ -176,14 +201,16 @@ const Component = function () { var handlePlateChange = useCallback(function (index, plateNo) { var v = vehicleDb.find(function (x) { return x.plateNo === plateNo; }); - updateRow(index, { + var patch = { plateNo: plateNo, vehicleType: v ? v.vehicleType : '', brand: v ? v.brand : '', model: v ? v.model : '', departParking: v ? v.departParking : '', h2Unit: v ? v.h2Unit : '' - }); + }; + if (!v) patch.plannedMileageKm = ''; + updateRow(index, patch); }, [vehicleDb, updateRow]); var addRow = useCallback(function () { @@ -196,6 +223,7 @@ const Component = function () { model: '', departParking: '', h2Unit: '', + plannedMileageKm: '', startMileageKm: '', startHydrogen: '', startElectricKwh: '' @@ -216,6 +244,7 @@ const Component = function () { model: '', departParking: '', h2Unit: '', + plannedMileageKm: '', startMileageKm: '', startHydrogen: '', startElectricKwh: '' @@ -243,6 +272,7 @@ const Component = function () { for (var i = 0; i < (vehicles || []).length; i++) { var r = vehicles[i] || {}; if (!String(r.plateNo || '').trim()) continue; + if (!String(r.plannedMileageKm || '').trim()) e['row_' + i + '_plannedMileageKm'] = '请输入预计异动里程'; if (!String(r.startMileageKm || '').trim()) e['row_' + i + '_startMileageKm'] = '请输入异动开始里程'; if (!String(r.startHydrogen || '').trim()) e['row_' + i + '_startHydrogen'] = '请输入异动开始氢量'; if (!String(r.startElectricKwh || '').trim()) e['row_' + i + '_startElectricKwh'] = '请输入异动开始电量'; @@ -362,6 +392,21 @@ const Component = function () { return React.createElement(Input, { value: r.departParking ? r.departParking : '请先选择车辆', disabled: true }); } }, + { + title: React.createElement('span', null, reqStar, '预计异动里程'), + key: 'plannedMileageKm', + width: 160, + render: function (_, r, index) { + var k = 'row_' + index + '_plannedMileageKm'; + return React.createElement(Input, { + value: r.plannedMileageKm, + onChange: function (e) { updateRow(index, { plannedMileageKm: toFixed2Input(e.target.value) }); }, + placeholder: '0.00', + addonAfter: 'km', + status: errors[k] ? 'error' : undefined + }); + } + }, { title: React.createElement('span', null, reqStar, '异动开始里程'), key: 'startMileageKm', @@ -504,15 +549,6 @@ const Component = function () { }), errors.changeType ? React.createElement('div', { style: { marginTop: 4, color: '#ff4d4f', fontSize: 12 } }, errors.changeType) : null ), - React.createElement('div', { style: formItemStyle }, - React.createElement('div', { style: labelStyle }, '预计异动里程'), - React.createElement(Input, { - placeholder: '0.00', - value: form.plannedMileageKm, - onChange: function (e) { updateForm({ plannedMileageKm: toFixed2Input(e.target.value) }); }, - addonAfter: 'km' - }) - ), React.createElement('div', { style: Object.assign({}, formItemStyle, { gridColumn: 'span 3' }) }, React.createElement('div', { style: labelStyle }, '备注'), @@ -534,7 +570,7 @@ const Component = function () { dataSource: vehicles, size: 'small', pagination: false, - scroll: { x: 1250 } + scroll: { x: 1410 } }), React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addRow }, '新增一行') ), diff --git a/web端/运维管理/车辆业务/异动管理.jsx b/web端/运维管理/车辆业务/异动管理.jsx index 62985e4..bcd18d5 100644 --- a/web端/运维管理/车辆业务/异动管理.jsx +++ b/web端/运维管理/车辆业务/异动管理.jsx @@ -586,7 +586,7 @@ const Component = function () { }, []); var handleAdd = useCallback(function () { - message.info('新增异动(原型,联调时跳转新建页或打开表单)'); + message.info('跳转异动-新增页(原型,表单与编辑页一致,联调时按路由区分新建/编辑)'); }, []); function fmtNowYMDHM() { @@ -747,7 +747,7 @@ const Component = function () { }, '结束异动')); } if (canShowMovementEdit(r.approvalStatus)) { - nodes.push(React.createElement(Button, { key: 'edit', type: 'link', size: 'small', onClick: function () { message.info('跳转异动-编辑页(原型)ID:' + r.id); } }, '编辑')); + nodes.push(React.createElement(Button, { key: 'edit', type: 'link', size: 'small', onClick: function () { message.info('跳转异动-编辑页(原型,表单与新增页一致)ID:' + r.id); } }, '编辑')); } return React.createElement(React.Fragment, null, nodes); } diff --git a/web端/需求说明/运维管理-车务管理/异动-新增 b/web端/需求说明/运维管理-车务管理/异动-新增 index 96c6512..a0decfd 100644 --- a/web端/需求说明/运维管理-车务管理/异动-新增 +++ b/web端/需求说明/运维管理-车务管理/异动-新增 @@ -7,8 +7,7 @@ 1.3.异动目的地:必填项,选择器,选项为:停车场、维修站、其他; 2.4.目的地名称:必填项,如异动目的地为停车场,则此处为停车场(选择器),如异动目的地为维修站,则此处为维修站(选择器),如异动目的地为其他,则此处为输入框(自定义输入); 2.5.异动类型:必填项,选择器,选项为:维修、保养、年审、其他; -2.6.预计异动里程:输入框,支持2位小数输入,后缀为km; -2.7.备注:文本域,支持自定义输入; +2.6.备注:文本域,支持自定义输入; 2.车辆信息: 2.1.车牌号:显示车牌号,支持输入框模糊搜索下拉匹配对应选项; @@ -16,10 +15,11 @@ 2.3.品牌:根据所选车辆品牌自动反写,默认提示为请先选择车辆; 2.4.型号:根据所选车辆型号自动反写,默认提示为请先选择车辆; 2.5.出发停车场:根据所选车辆出发时停车场自动反写,默认提示为请先选择车辆; -2.6.异动开始里程:必填项,输入框,精确至2位小数,后缀为km; -2.7.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取; -2.8.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh; -2.9.操作:删除; -2.10.新增一行:铺满整行; +2.6.预计异动里程:输入框,支持2位小数输入,后缀为km; +2.7.异动开始里程:必填项,输入框,精确至2位小数,后缀为km; +2.8.异动开始氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取; +2.9.异动开始电量:必填项,输入框,精确至2位小数,后缀为kWh; +2.10.操作:删除; +2.11.新增一行:铺满整行; 3.底部为提交审核、保存、取消; \ No newline at end of file diff --git a/web端/需求说明/运维管理-车务管理/异动管理 b/web端/需求说明/运维管理-车务管理/异动管理 index 5a50f4b..ee492e8 100644 --- a/web端/需求说明/运维管理-车务管理/异动管理 +++ b/web端/需求说明/运维管理-车务管理/异动管理 @@ -7,7 +7,8 @@ 1.3.车牌号:选择器,输入框支持模糊搜索下拉匹配结果; 1.4.右侧为查询、重置按钮; -2.列表: +2.列表:分为2个tab:进行中、历史记录; +#进行中: 2.1.异动开始日期:显示车辆异动开始日期,格式为:YYYY-MM-DD HH:MM; 2.2.异动预计结束日期:显示车辆异动结束日期,格式为:YYYY-MM-DD HH:MM; 2.3.审批状态:分为:待提交、审批中、审批完成、驳回、撤回; @@ -18,11 +19,33 @@ 2.8.车牌号:显示车牌号; 2.9.异动开始里程(km):显示车辆异动开始里程; 2.10.异动开始电量(kWh):显示车辆异动开始电量; -2.11.异动开始氢量;显示车辆异动开始氢量,带单位,单位根据型号参数表获取,分为%和MPa; +2.11.异动开始氢量:显示车辆异动开始氢量,带单位,单位根据型号参数表获取,分为%和MPa; 2.12.创建人:显示发起人姓名; 2.13.创建时间:显示发起时间,格式为:YYYY-MM-DD HH:MM; -2.14.操作:查看、编辑; +2.14.操作:查看、编辑、撤回、结束异动; 2.14.1.查看:点击跳转异动-查看页; 2.14.2.编辑:点击跳转异动-编辑页,只有审批状态为:待提交、驳回、撤回时显示编辑; + 2.14.3.撤回:点击进行二次确认,只有审批状态为:审批中才能操作撤回,确认后撤回该数据并将审批状态修改为:撤回; + 2.14.4.结束异动:当审批状态为审批完成后显示结束异动,点击跳转异动-结束异动页; + +#历史记录: +3.1.异动开始日期:显示车辆异动开始日期,格式为:YYYY-MM-DD HH:MM; +3.2.异动预计结束日期:显示车辆异动结束日期,格式为:YYYY-MM-DD HH:MM; +3.3.审批状态:分为:待提交、审批中、审批完成、驳回、撤回; +3.4.异动目的地:显示车辆异动目的地,选项包括:停车场、维修站、其他; +3.5.目的地名称:显示车辆异动目的地名称; +3.6.异动类型:显示车辆异动类型,选项包括:维修、保养、年审、其他; +3.7.预计异动里程(km):显示车辆预计异动里程; +3.8.车牌号:显示车牌号; +3.9.异动开始里程(km):显示车辆异动开始里程; +3.10.异动结束里程(km):显示车辆异动结束里程; +3.11.异动开始电量(kWh):显示车辆异动开始电量; +3.12.异动结束电量(kWh):显示车辆异动结束电量; +3.13.异动开始氢量:显示车辆异动开始氢量,带单位,单位根据型号参数表获取,分为%和MPa; +3.14.异动结束氢量:显示车辆异动结束氢量,带单位,单位根据型号参数表获取,分为%和MPa; +3.15.创建人:显示发起人姓名; +3.16.创建时间:显示发起时间,格式为:YYYY-MM-DD HH:MM; +3.17.操作:查看; + 3.14.1.查看:点击跳转异动-查看页; 3.列表右下方为分页功能,支持单页显示条数设置; \ No newline at end of file diff --git a/web端/需求说明/运维管理-车务管理/异动管理-结束异动 b/web端/需求说明/运维管理-车务管理/异动管理-结束异动 new file mode 100644 index 0000000..15faf23 --- /dev/null +++ b/web端/需求说明/运维管理-车务管理/异动管理-结束异动 @@ -0,0 +1,30 @@ +一个「数字化资产ONEOS运管平台」中的「异动管理」「结束异动」模块 +#面包屑:运维管理-车辆业务-异动管理-结束异动 + +1.异动情况: +1.1.异动开始日期:显示异动开始日期:YYYY-MM-DD HH:MM; +1.2.异动预计结束日期:显示异动预计结束日期,格式为:YYYY-MM-DD HH:MM; +1.3.异动目的地:显示异动目的地,包括:停车场、维修站、其他; +1.4.目的地名称:显示目的地名称,包括:停车场名称、维修站名称、其他; +1.5.异动类型:显示异动类型,包括:维修、保养、年审、其他; +1.6.异动结束时间:必填项,日期选择器,格式为:YYYY-MM-DD HH:MM; +1.7.备注:显示备注信息; + +2.车辆信息: +2.1.车牌号:输入框(禁用),显示车牌号; +2.2.车辆类型:根据所选车辆类型自动反写,默认提示为请先选择车辆; +2.3.品牌:根据所选车辆品牌自动反写,默认提示为请先选择车辆; +2.4.型号:根据所选车辆型号自动反写,默认提示为请先选择车辆; +2.5.出发停车场:根据所选车辆出发时停车场自动反写,默认提示为请先选择车辆; +2.6.预计异动里程:显示预计异动里程,支持2位小数,后缀为km; +2.7.异动开始里程:输入框(禁用),精确至2位小数,后缀为km; +2.8.异动结束里程:必填项,输入框,精确至2位小数,后缀为km; +2.9.异动开始氢量:输入框(禁用),精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取; +2.10.异动结束氢量:必填项,输入框,精确至2位小数,后缀为%或MPa,根据所选车辆型号中获取; +2.11.异动开始电量:输入框(禁用),精确至2位小数,后缀为kWh; +2.12.异动结束电量:必填项,输入框,精确至2位小数,后缀为kWh; +2.13.异动结束电量:必填项,输入框,精确至2位小数,后缀为kWh; + +3.底部为提交、取消; +3.1.提交:提交进行必填项校验,提交成功后计入历史记录; +3.2.取消:点击取消,返回列表; \ No newline at end of file