From 351688006e80df8cb39fe6e7d19b2939eeaa2cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Tue, 9 Jun 2026 18:07:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(miniapp):=20=E6=96=B0=E5=A2=9E=E5=B0=8F?= =?UTF-8?q?=E7=BE=9A=E7=BE=9A=E5=B0=8F=E7=A8=8B=E5=BA=8F=E4=B8=BB=E4=BD=93?= =?UTF-8?q?=E4=B8=8E=E5=AE=A1=E6=89=B9=E4=B8=AD=E5=BF=83=E7=AD=89=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E5=B9=B6=E9=87=8D=E6=9E=84=E5=B9=B4=E5=AE=A1?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E4=B8=BA=E6=94=AF=E6=8C=81=E5=B5=8C=E5=85=A5?= =?UTF-8?q?XLL=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- ONE-OS小程序/审批中心.jsx | 1864 +++++++++++ ONE-OS小程序/小羚羚.jsx | 5356 ++++++++++++++++++++++++++++++ ONE-OS小程序/年审管理.jsx | 260 +- ONE-OS小程序/提车应收款-审批.jsx | 1131 +++++++ ONE-OS小程序/还车应结款-审批.jsx | 96 + 5 files changed, 8622 insertions(+), 85 deletions(-) create mode 100644 ONE-OS小程序/审批中心.jsx create mode 100644 ONE-OS小程序/小羚羚.jsx create mode 100644 ONE-OS小程序/提车应收款-审批.jsx create mode 100644 ONE-OS小程序/还车应结款-审批.jsx diff --git a/ONE-OS小程序/审批中心.jsx b/ONE-OS小程序/审批中心.jsx new file mode 100644 index 0000000..3caca44 --- /dev/null +++ b/ONE-OS小程序/审批中心.jsx @@ -0,0 +1,1864 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// ONE-OS 小程序 - 审批中心(我发起的 / 我的待办 / 我的已办 / 我的抄送) + +const { useState, useMemo, useCallback, useRef, useEffect } = React; +const moment = window.moment || window.dayjs; + +const COLOR_PRIMARY = '#16D1A1'; +const COLOR_PRIMARY_DEEP = '#00BFA5'; +const COLOR_PRIMARY_SOFT = 'rgba(22, 209, 161, 0.12)'; +const COLOR_TEXT = '#1D2129'; +const COLOR_TEXT_SEC = '#4E5969'; +const COLOR_MUTED = '#86909C'; +const COLOR_LINE = '#E5E6EB'; +const COLOR_BG = '#FFFFFF'; +const COLOR_PAGE = '#F2F3F5'; +const COLOR_WARN = '#FF7D00'; +const COLOR_DANGER = '#F53F3F'; +const COLOR_SUCCESS = '#00B42A'; +const FONT_FAMILY = + '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", STHeiti, sans-serif'; + +const resolveAntdBundle = () => { + const raw = window.antd; + if (!raw) return {}; + if (raw.default && typeof raw.default === 'object') return { ...raw, ...raw.default }; + return raw; +}; + +const antd = resolveAntdBundle(); +const message = antd.message || { info: () => {}, success: () => {}, warning: () => {} }; +const Drawer = antd.Drawer; +const Modal = antd.Modal; + +const FallbackTag = ({ children, className, style, color }) => { + const palette = { + error: { c: COLOR_DANGER, bg: 'rgba(245, 63, 63, 0.1)' }, + warning: { c: COLOR_WARN, bg: 'rgba(255, 125, 0, 0.1)' }, + success: { c: COLOR_SUCCESS, bg: 'rgba(0, 180, 42, 0.1)' }, + default: { c: COLOR_MUTED, bg: 'rgba(134, 144, 156, 0.12)' }, + processing: { c: COLOR_PRIMARY_DEEP, bg: COLOR_PRIMARY_SOFT }, + }; + const p = palette[color] || palette.processing; + return ( + + {children} + + ); +}; + +const Tag = antd.Tag || FallbackTag; + +const MOCK_CURRENT_USER = '张明辉'; + +const APPROVAL_FLOW_TYPES = [ + '合同审批', + '提车应收款', + '租赁账单', + '还车应结款', + '氢费对账单(对站)', + '氢费对账单(对客)', + '车辆调拨', + '车辆异动', +]; + +const TAB_ITEMS = [ + { key: 'initiated', label: '我发起的', short: '发起' }, + { key: 'todo', label: '我的待办', short: '待办' }, + { key: 'done', label: '我的已办', short: '已办' }, + { key: 'cc', label: '我的抄送', short: '抄送' }, +]; + +/** 流程类型主题色(左侧色条 + 图标底) */ +const FLOW_THEME = { + 合同审批: { accent: '#2563EB', soft: 'rgba(37, 99, 235, 0.12)' }, + 提车应收款: { accent: '#F97316', soft: 'rgba(249, 115, 22, 0.12)' }, + 租赁账单: { accent: '#0EA5E9', soft: 'rgba(14, 165, 233, 0.12)' }, + 还车应结款: { accent: '#8B5CF6', soft: 'rgba(139, 92, 246, 0.12)' }, + '氢费对账单(对站)': { accent: '#10B981', soft: 'rgba(16, 185, 129, 0.12)' }, + '氢费对账单(对客)': { accent: '#059669', soft: 'rgba(5, 150, 105, 0.12)' }, + 车辆调拨: { accent: COLOR_PRIMARY_DEEP, soft: COLOR_PRIMARY_SOFT }, + 车辆异动: { accent: '#14B8A6', soft: 'rgba(20, 184, 166, 0.12)' }, +}; + +const buildMockTasks = () => [ + { id: 'ap-1', flowType: '合同审批', bizNo: 'HT-ZL-2025-088', summary: '嘉兴氢能示范项目 · 正式合同', initiator: '张明辉', initiateTime: '2026-05-28 09:15', arriveTime: '2026-05-28 14:20', finishTime: '', currentNode: '法务审核', currentAssignee: '王法务', status: '审批中', ccUsers: ['李晓彤'], handledBy: [] }, + { id: 'ap-2', flowType: '提车应收款', bizNo: 'TC-2026-0312', summary: '上海迅杰物流 · 3 台提车收款', customerName: '上海迅杰物流有限公司', projectName: '上海氢能城际物流项目', vehicleCount: 3, actualAmount: '186800.00', initiator: '李晓彤', initiateTime: '2026-05-30 10:00', arriveTime: '2026-05-30 11:30', finishTime: '', currentNode: '财务审核', currentAssignee: '张明辉', status: '审批中', ccUsers: ['张明辉', '陈高伟'], handledBy: [] }, + { id: 'ap-3', flowType: '租赁账单', bizNo: 'ZD-2026-06-001', summary: '2026年6月 · 粤B58888F 等 5 车', initiator: '陈高伟', initiateTime: '2026-06-01 08:40', arriveTime: '2026-06-01 09:10', finishTime: '', currentNode: '业管主管', currentAssignee: '张明辉', status: '审批中', ccUsers: [], handledBy: [] }, + { id: 'ap-4', flowType: '还车应结款', bizNo: 'HC-2026-0520', summary: '沪A03561F 还车结算', plateNo: '沪A03561F', customerName: '上海迅杰物流有限公司', projectName: '上海氢能城际物流项目', pendingSettle: '927.50', depositAmount: '5000.00', refundTotal: '4072.50', payTotal: '0.00', actualRent: '0.00', initiator: '张明辉', initiateTime: '2026-05-20 16:00', arriveTime: '2026-05-21 09:00', finishTime: '2026-05-21 15:30', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['李晓彤'], handledBy: ['张明辉', '财务-赵敏'] }, + { id: 'ap-5', flowType: '氢费对账单(对站)', bizNo: 'H2-ST-202605', summary: '平湖加氢站 · 2026年5月对账', initiator: '能源部-周工', initiateTime: '2026-05-25 11:00', arriveTime: '2026-05-26 08:30', finishTime: '2026-05-26 17:00', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['张明辉'], handledBy: ['张明辉'] }, + { id: 'ap-6', flowType: '氢费对账单(对客)', bizNo: 'H2-CU-202605', summary: '嘉兴某某物流 · 5月氢费账单', initiator: '李晓彤', initiateTime: '2026-05-27 14:20', arriveTime: '2026-05-28 09:00', finishTime: '', currentNode: 'CEO审批', currentAssignee: 'CEO办公室', status: '审批中', ccUsers: ['张明辉', '陈高伟'], handledBy: ['张明辉'] }, + { id: 'ap-8', flowType: '车辆调拨', bizNo: 'DB-2026-018', summary: '粤B58888F · 深圳 → 杭州', initiator: '王东东', initiateTime: '2026-06-01 08:00', arriveTime: '2026-06-01 08:45', finishTime: '', currentNode: '运维主管', currentAssignee: '张明辉', status: '审批中', ccUsers: ['张明辉'], handledBy: [] }, + { id: 'ap-9', flowType: '车辆异动', bizNo: 'YD-2026-042', summary: '浙F06900F · 保养至检测站', initiator: '张明辉', initiateTime: '2026-05-18 13:20', arriveTime: '2026-05-18 14:00', finishTime: '2026-05-18 16:10', currentNode: '—', currentAssignee: '', status: '已驳回', ccUsers: ['李晓彤'], handledBy: ['运维主管-刘强'] }, + { id: 'ap-10', flowType: '合同审批', bizNo: 'HT-ZL-2024-066', summary: '上海迅杰物流 · 续签合同', initiator: '张明辉', initiateTime: '2026-04-10 10:00', arriveTime: '2026-04-10 11:00', finishTime: '2026-04-12 09:30', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['李晓彤', '王法务'], handledBy: ['法务-王法务', 'CEO办公室'] }, + { id: 'ap-11', flowType: '租赁账单', bizNo: 'ZD-2026-05-008', summary: '2026年5月 · 批量租赁账单', initiator: '陈高伟', initiateTime: '2026-05-05 09:00', arriveTime: '2026-05-05 09:30', finishTime: '2026-05-05 18:00', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['张明辉'], handledBy: ['张明辉'] }, + { id: 'ap-12', flowType: '提车应收款', bizNo: 'TC-2026-0228', summary: '嘉兴某某物流 · 2 台提车', customerName: '嘉兴某某物流有限公司', projectName: '嘉兴氢能示范项目', vehicleCount: 2, actualAmount: '98600.00', initiator: '张明辉', initiateTime: '2026-02-28 15:00', arriveTime: '2026-03-01 09:00', finishTime: '2026-03-01 11:20', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['财务-赵敏'], handledBy: ['财务-赵敏'] }, + { id: 'ap-13', flowType: '车辆调拨', bizNo: 'DB-2026-003', summary: '苏E33333 · 苏州 → 南京', initiator: '王东东', initiateTime: '2026-03-15 10:30', arriveTime: '2026-03-15 11:00', finishTime: '', currentNode: '业务审批', currentAssignee: '李晓彤', status: '审批中', ccUsers: ['张明辉'], handledBy: ['张明辉'] }, + { id: 'ap-14', flowType: '还车应结款', bizNo: 'HC-2026-0418', summary: '粤B58888F 还车应结', plateNo: '粤B58888F', customerName: '嘉兴某某物流有限公司', projectName: '嘉兴腾4.5T租赁', pendingSettle: '927.50', depositAmount: '5000.00', refundTotal: '4072.50', payTotal: '0.00', actualRent: '0.00', initiator: '李晓彤', initiateTime: '2026-04-18 09:00', arriveTime: '2026-04-18 09:30', finishTime: '', currentNode: '财务审核', currentAssignee: '张明辉', status: '审批中', ccUsers: ['张明辉'], handledBy: ['业管主管-陈高伟'] }, + { id: 'ap-16', flowType: '氢费对账单(对站)', bizNo: 'H2-ST-202604', summary: '嘉兴港区加氢站 · 4月对账', initiator: '能源部-周工', initiateTime: '2026-04-20 10:00', arriveTime: '2026-04-21 09:00', finishTime: '2026-04-22 11:00', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['张明辉', '陈高伟'], handledBy: ['能源部-周工'], ccTime: '2026-04-21 09:05' }, + { id: 'ap-17', flowType: '车辆异动', bizNo: 'YD-2026-028', summary: '沪A03561F · 年审至检测站', initiator: '王东东', initiateTime: '2026-02-24 08:00', arriveTime: '2026-02-24 08:30', finishTime: '2026-02-24 12:00', currentNode: '—', currentAssignee: '', status: '已通过', ccUsers: ['张明辉'], handledBy: ['张明辉'], ccTime: '2026-02-24 08:35' }, +]; + +const PAGE_STYLE = ` +.ac-mini-root { + min-height: 100dvh; + background: linear-gradient(165deg, #e8ebef 0%, ${COLOR_PAGE} 40%); + display: flex; + justify-content: center; + padding: 16px 12px 32px; + box-sizing: border-box; + font-family: ${FONT_FAMILY}; + -webkit-font-smoothing: antialiased; +} +.ac-phone { + width: 100%; + max-width: 390px; + min-height: 844px; + background: ${COLOR_PAGE}; + border-radius: 28px; + overflow: hidden; + box-shadow: 0 24px 48px rgba(15, 23, 42, 0.14), 0 0 0 1px rgba(15, 23, 42, 0.05); + display: flex; + flex-direction: column; +} +.ac-chrome { flex-shrink: 0; background: ${COLOR_BG}; } +.ac-status-bar { + height: 44px; + padding: 14px 24px 0; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; +} +.ac-status-time { font-size: 15px; font-weight: 600; color: ${COLOR_TEXT}; letter-spacing: -0.02em; } +.ac-mp-navbar { + height: 48px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 16px; + border-bottom: 1px solid rgba(0,0,0,.05); + position: relative; + background: ${COLOR_BG}; +} +.ac-mp-navbar-title { font-size: 17px; font-weight: 700; color: ${COLOR_TEXT}; } +.ac-tab-seg { + display: flex; + gap: 6px; + padding: 10px 14px 8px; + background: ${COLOR_BG}; + flex-shrink: 0; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} +.ac-tab-seg::-webkit-scrollbar { display: none; } +.ac-tab-seg-btn { + flex: 1 0 auto; + min-width: 72px; + min-height: 44px; + padding: 8px 10px; + border: none; + border-radius: 10px; + background: ${COLOR_PAGE}; + color: ${COLOR_MUTED}; + font-size: 13px; + font-weight: 500; + cursor: pointer; + touch-action: manipulation; + transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease; + position: relative; +} +.ac-tab-seg-btn:active { transform: scale(0.97); } +.ac-tab-seg-btn.active { + background: ${COLOR_BG}; + color: ${COLOR_PRIMARY_DEEP}; + font-weight: 700; + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); +} +.ac-tab-seg-btn:focus-visible { + outline: 2px solid ${COLOR_PRIMARY}; + outline-offset: 2px; +} +.ac-tab-count { + display: block; + font-size: 11px; + font-weight: 600; + margin-top: 2px; + opacity: 0.85; + font-variant-numeric: tabular-nums; +} +.ac-toolbar { + padding: 0 14px 10px; + flex-shrink: 0; + background: ${COLOR_BG}; + border-bottom: 1px solid ${COLOR_LINE}; +} +.ac-search-wrap { + display: flex; + align-items: center; + gap: 8px; + min-height: 44px; + padding: 0 12px; + background: ${COLOR_PAGE}; + border-radius: 12px; + border: 1px solid transparent; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} +.ac-search-wrap:focus-within { + border-color: rgba(22, 209, 161, 0.45); + box-shadow: 0 0 0 3px rgba(22, 209, 161, 0.12); + background: ${COLOR_BG}; +} +.ac-search-wrap svg { flex-shrink: 0; color: ${COLOR_MUTED}; } +.ac-search-input { + flex: 1; + border: none; + background: transparent; + font-size: 15px; + color: ${COLOR_TEXT}; + outline: none; + min-width: 0; +} +.ac-search-input::placeholder { color: ${COLOR_MUTED}; } +.ac-filter-scroll { + display: flex; + gap: 8px; + margin-top: 10px; + overflow-x: auto; + padding-bottom: 2px; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} +.ac-filter-scroll::-webkit-scrollbar { display: none; } +.ac-filter-chip { + flex-shrink: 0; + min-height: 36px; + padding: 0 14px; + border: 1px solid ${COLOR_LINE}; + background: ${COLOR_BG}; + border-radius: 999px; + font-size: 13px; + color: ${COLOR_TEXT_SEC}; + cursor: pointer; + touch-action: manipulation; + transition: all 0.2s ease; + white-space: nowrap; +} +.ac-filter-chip:active { transform: scale(0.96); } +.ac-filter-chip.active { + border-color: ${COLOR_PRIMARY}; + color: ${COLOR_PRIMARY_DEEP}; + background: ${COLOR_PRIMARY_SOFT}; + font-weight: 600; +} +.ac-filter-chip:focus-visible { + outline: 2px solid ${COLOR_PRIMARY}; + outline-offset: 2px; +} +.ac-filter-more { + border-style: dashed; + color: ${COLOR_PRIMARY_DEEP}; + font-weight: 600; +} +.ac-list-head { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px 4px; + font-size: 12px; + color: ${COLOR_MUTED}; +} +.ac-list { + flex: 1; + overflow-y: auto; + padding: 4px 14px 28px; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; +} +.ac-card { + position: relative; + background: ${COLOR_BG}; + border-radius: 14px; + padding: 14px 14px 12px 16px; + margin-bottom: 12px; + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.05); + border: 1px solid rgba(0,0,0,.04); + cursor: pointer; + touch-action: manipulation; + transition: transform 0.18s ease, box-shadow 0.18s ease; + animation: ac-card-in 0.35s ease both; +} +.ac-card::before { + content: ''; + position: absolute; + left: 0; + top: 12px; + bottom: 12px; + width: 3px; + border-radius: 0 3px 3px 0; + background: var(--ac-accent, ${COLOR_PRIMARY}); +} +.ac-card:active { transform: scale(0.985); box-shadow: 0 1px 4px rgba(15, 23, 42, 0.06); } +.ac-card:focus-visible { + outline: 2px solid ${COLOR_PRIMARY}; + outline-offset: 2px; +} +@keyframes ac-card-in { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} +@media (prefers-reduced-motion: reduce) { + .ac-card { animation: none; transition: none; } + .ac-card:active { transform: none; } + .ac-tab-seg-btn:active { transform: none; } + .ac-filter-chip:active { transform: none; } +} +.ac-card-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + margin-bottom: 8px; +} +.ac-card-title-row { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1; } +.ac-card-icon { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + background: var(--ac-icon-bg, ${COLOR_PRIMARY_SOFT}); + color: var(--ac-accent, ${COLOR_PRIMARY_DEEP}); +} +.ac-card-title { font-size: 16px; font-weight: 700; color: ${COLOR_TEXT}; line-height: 1.25; } +.ac-card-bizno { font-size: 12px; color: ${COLOR_MUTED}; margin-top: 3px; font-variant-numeric: tabular-nums; } +.ac-status-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; + flex-shrink: 0; +} +.ac-summary { + font-size: 14px; + color: ${COLOR_TEXT_SEC}; + margin-bottom: 10px; + line-height: 1.5; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} +.ac-meta-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px 12px; + padding-top: 8px; + border-top: 1px dashed ${COLOR_LINE}; +} +.ac-meta-item { min-width: 0; } +.ac-meta-label { font-size: 11px; color: ${COLOR_MUTED}; margin-bottom: 2px; } +.ac-meta-value { + font-size: 13px; + color: ${COLOR_TEXT}; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; +} +.ac-biz-grid { + margin-bottom: 10px; + padding-top: 0; + border-top: none; +} +.ac-biz-grid .ac-meta-value.ac-amount { + color: #F97316; + font-weight: 700; + font-variant-numeric: tabular-nums; + white-space: nowrap; +} +.ac-card-foot { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid ${COLOR_LINE}; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.ac-card-action { + min-height: 36px; + padding: 0 16px; + border: none; + border-radius: 8px; + background: linear-gradient(135deg, ${COLOR_PRIMARY} 0%, ${COLOR_PRIMARY_DEEP} 100%); + color: #fff; + font-size: 13px; + font-weight: 700; + cursor: pointer; + touch-action: manipulation; + box-shadow: 0 4px 12px rgba(0, 191, 165, 0.25); +} +.ac-card-action:active { opacity: 0.92; transform: scale(0.98); } +.ac-empty { + text-align: center; + padding: 56px 24px 32px; +} +.ac-empty-icon { + width: 64px; + height: 64px; + margin: 0 auto 16px; + border-radius: 50%; + background: ${COLOR_PAGE}; + display: flex; + align-items: center; + justify-content: center; + color: ${COLOR_MUTED}; +} +.ac-empty-title { font-size: 15px; font-weight: 600; color: ${COLOR_TEXT}; margin-bottom: 6px; } +.ac-empty-desc { font-size: 13px; color: ${COLOR_MUTED}; line-height: 1.55; } +.ac-drawer-types { + display: flex; + flex-direction: column; + gap: 8px; +} +.ac-drawer-type-btn { + min-height: 48px; + padding: 0 16px; + border: 1px solid ${COLOR_LINE}; + border-radius: 12px; + background: ${COLOR_BG}; + text-align: left; + font-size: 14px; + color: ${COLOR_TEXT}; + cursor: pointer; + touch-action: manipulation; +} +.ac-drawer-type-btn.active { + border-color: ${COLOR_PRIMARY}; + background: ${COLOR_PRIMARY_SOFT}; + color: ${COLOR_PRIMARY_DEEP}; + font-weight: 600; +} +.tc-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding-bottom: 88px; } +.tc-hero { margin: 12px 14px 0; padding: 18px 16px 16px; border-radius: 16px; background: linear-gradient(135deg, #F97316 0%, #EA580C 100%); color: #fff; box-shadow: 0 10px 28px rgba(249, 115, 22, 0.35); } +.tc-hero-label { font-size: 13px; opacity: 0.92; margin-bottom: 6px; } +.tc-hero-amount { font-size: 36px; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; } +.tc-hero-sub { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.22); display: flex; align-items: center; justify-content: space-between; gap: 12px; } +.tc-hero-compare { font-size: 12px; opacity: 0.9; line-height: 1.45; } +.tc-hero-detail-btn { flex-shrink: 0; min-height: 32px; padding: 0 12px; border: 1px solid rgba(255,255,255,.45); border-radius: 999px; background: rgba(255,255,255,.14); color: #fff; font-size: 12px; font-weight: 600; cursor: pointer; } +.tc-hero-meta { display: flex; gap: 16px; margin-top: 10px; font-size: 12px; opacity: 0.88; } +.tc-section { margin: 12px 14px 0; background: ${COLOR_BG}; border-radius: 14px; overflow: hidden; box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04); border: 1px solid rgba(0,0,0,.04); } +.tc-section-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px; border-bottom: 1px solid ${COLOR_LINE}; } +.tc-section-title { font-size: 15px; font-weight: 700; color: ${COLOR_TEXT}; } +.tc-section-badge { font-size: 11px; font-weight: 600; color: #F97316; background: rgba(249, 115, 22, 0.12); padding: 2px 8px; border-radius: 999px; } +.tc-kv-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; padding: 12px 14px 14px; } +.tc-kv-item { min-width: 0; } +.tc-kv-label { font-size: 11px; color: ${COLOR_MUTED}; margin-bottom: 3px; } +.tc-kv-value { font-size: 13px; color: ${COLOR_TEXT}; font-weight: 500; line-height: 1.4; word-break: break-all; } +.tc-kv-value--full { grid-column: 1 / -1; } +.tc-vehicle-card { margin: 0 14px 10px; padding: 12px 14px; background: ${COLOR_PAGE}; border-radius: 12px; border: 1px solid ${COLOR_LINE}; } +.tc-vehicle-card:last-child { margin-bottom: 14px; } +.tc-vehicle-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; } +.tc-vehicle-plate { font-size: 15px; font-weight: 700; color: ${COLOR_TEXT}; } +.tc-vehicle-model { font-size: 12px; color: ${COLOR_MUTED}; margin-top: 2px; } +.tc-vehicle-idx { font-size: 11px; font-weight: 700; color: #F97316; background: rgba(249, 115, 22, 0.12); padding: 2px 8px; border-radius: 999px; flex-shrink: 0; } +.tc-vehicle-amount-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding: 10px 12px; background: ${COLOR_BG}; border-radius: 10px; margin-bottom: 8px; } +.tc-vehicle-amount-label { font-size: 12px; color: ${COLOR_MUTED}; } +.tc-vehicle-amount-val { font-size: 18px; font-weight: 800; color: #F97316; font-variant-numeric: tabular-nums; } +.tc-vehicle-kv { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 10px; } +.tc-vehicle-kv-item { font-size: 12px; color: ${COLOR_TEXT_SEC}; } +.tc-vehicle-kv-item span { color: ${COLOR_MUTED}; } +.tc-service-toggle { width: 100%; margin-top: 8px; min-height: 32px; border: none; background: transparent; color: ${COLOR_PRIMARY_DEEP}; font-size: 12px; font-weight: 600; cursor: pointer; text-align: left; padding: 0; } +.tc-service-list { margin-top: 8px; padding-top: 8px; border-top: 1px dashed ${COLOR_LINE}; } +.tc-service-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 8px; font-size: 12px; padding: 6px 0; border-bottom: 1px solid rgba(0,0,0,.04); } +.tc-service-name { color: ${COLOR_TEXT}; font-weight: 500; } +.tc-service-amt { color: #F97316; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; } +.tc-service-sub { grid-column: 1 / -1; color: ${COLOR_MUTED}; font-size: 11px; } +.tc-timeline { padding: 4px 14px 14px; } +.tc-step { display: flex; gap: 12px; position: relative; padding-bottom: 16px; } +.tc-step:not(:last-child)::before { content: ''; position: absolute; left: 9px; top: 22px; bottom: 0; width: 2px; background: ${COLOR_LINE}; } +.tc-step-dot { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; margin-top: 1px; } +.tc-step-dot.done { background: ${COLOR_SUCCESS}; color: #fff; } +.tc-step-dot.wait { background: rgba(249, 115, 22, 0.12); color: #F97316; border: 2px solid #F97316; box-sizing: border-box; } +.tc-step-title { font-size: 14px; font-weight: 600; color: ${COLOR_TEXT}; } +.tc-step-meta { font-size: 12px; color: ${COLOR_MUTED}; margin-top: 4px; line-height: 1.5; } +.tc-action-bar { position: absolute; left: 0; right: 0; bottom: 0; z-index: 20; display: flex; gap: 8px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px)); background: rgba(255,255,255,.96); border-top: 1px solid ${COLOR_LINE}; backdrop-filter: blur(8px); } +.tc-action-bar--view { justify-content: flex-end; } +.tc-btn { flex: 1; min-height: 44px; border-radius: 12px; font-size: 14px; font-weight: 700; cursor: pointer; border: none; touch-action: manipulation; } +.tc-btn-comment { flex: 0 0 auto; min-width: 64px; background: ${COLOR_BG}; color: ${COLOR_TEXT_SEC}; border: 1px solid ${COLOR_LINE}; } +.tc-btn-terminate { background: ${COLOR_PAGE}; color: ${COLOR_WARN}; border: 1px solid rgba(255, 125, 0, 0.3); } +.tc-btn-reject { background: ${COLOR_PAGE}; color: ${COLOR_DANGER}; border: 1px solid rgba(245, 63, 63, 0.25); } +.tc-btn-approve { background: linear-gradient(135deg, ${COLOR_PRIMARY} 0%, ${COLOR_PRIMARY_DEEP} 100%); color: #fff; box-shadow: 0 4px 14px rgba(0, 191, 165, 0.3); } +.tc-btn:active { opacity: 0.92; transform: scale(0.98); } +.tc-approval-form { display: flex; flex-direction: column; gap: 16px; padding: 4px 0 8px; } +.tc-form-field { display: flex; flex-direction: column; gap: 8px; } +.tc-form-label { font-size: 14px; color: ${COLOR_TEXT}; font-weight: 500; } +.tc-form-label-required::before { content: '*'; color: ${COLOR_DANGER}; margin-right: 4px; } +.tc-notify-group { display: flex; flex-wrap: wrap; gap: 16px; } +.tc-notify-item { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: ${COLOR_TEXT_SEC}; cursor: pointer; min-height: 32px; } +.tc-notify-item input { width: 16px; height: 16px; accent-color: ${COLOR_PRIMARY}; } +.tc-notify-item.disabled { opacity: 0.55; cursor: not-allowed; } +.tc-upload-btn { + display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 14px; + border: 1px dashed ${COLOR_LINE}; border-radius: 8px; background: ${COLOR_PAGE}; + color: ${COLOR_TEXT_SEC}; font-size: 13px; cursor: pointer; +} +.tc-upload-hint { font-size: 12px; color: ${COLOR_MUTED}; line-height: 1.55; margin-top: 4px; } +.tc-file-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; } +.tc-file-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px; background: ${COLOR_PAGE}; border-radius: 8px; font-size: 13px; color: ${COLOR_TEXT}; } +.tc-file-remove { border: none; background: transparent; color: ${COLOR_MUTED}; font-size: 12px; cursor: pointer; padding: 4px; } +.tc-select-person { + min-height: 44px; padding: 0 14px; border: 1px solid ${COLOR_LINE}; border-radius: 10px; + background: ${COLOR_BG}; color: ${COLOR_MUTED}; font-size: 14px; text-align: left; cursor: pointer; width: 100%; +} +.tc-select-person.has-value { color: ${COLOR_TEXT}; } +.tc-cc-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; } +.tc-cc-chip { + display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px; + background: ${COLOR_PRIMARY_SOFT}; color: ${COLOR_PRIMARY_DEEP}; font-size: 12px; font-weight: 600; +} +.tc-cc-chip button { border: none; background: transparent; color: inherit; cursor: pointer; padding: 0 2px; font-size: 14px; line-height: 1; } +.tc-textarea { + width: 100%; min-height: 96px; padding: 10px 12px; border: 1px solid ${COLOR_LINE}; border-radius: 10px; + font-size: 14px; color: ${COLOR_TEXT}; resize: vertical; box-sizing: border-box; font-family: inherit; outline: none; +} +.tc-textarea:focus { border-color: rgba(22, 209, 161, 0.55); box-shadow: 0 0 0 3px rgba(22, 209, 161, 0.12); } +.tc-textarea-counter { text-align: right; font-size: 12px; color: ${COLOR_MUTED}; margin-top: 4px; } +.tc-drawer-foot { display: flex; gap: 10px; padding-top: 12px; margin-top: 4px; border-top: 1px solid ${COLOR_LINE}; } +.tc-drawer-foot-btn { flex: 1; min-height: 44px; border-radius: 10px; font-size: 15px; font-weight: 600; cursor: pointer; border: none; } +.tc-drawer-foot-cancel { background: ${COLOR_BG}; color: ${COLOR_TEXT_SEC}; border: 1px solid ${COLOR_LINE}; } +.tc-drawer-foot-confirm { background: #1677ff; color: #fff; } +.tc-person-list { display: flex; flex-direction: column; gap: 8px; } +.tc-person-item { + min-height: 48px; padding: 0 14px; border: 1px solid ${COLOR_LINE}; border-radius: 10px; + background: ${COLOR_BG}; font-size: 14px; color: ${COLOR_TEXT}; text-align: left; cursor: pointer; +} +.tc-person-item.selected { border-color: ${COLOR_PRIMARY}; background: ${COLOR_PRIMARY_SOFT}; color: ${COLOR_PRIMARY_DEEP}; font-weight: 600; } +.tc-drawer-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid ${COLOR_LINE}; font-size: 14px; } +.tc-drawer-row-val.highlight { color: #F97316; font-weight: 700; } +.tc-drawer-total { margin-top: 8px; padding-top: 12px; border-top: 2px solid ${COLOR_LINE}; display: flex; justify-content: space-between; font-size: 15px; font-weight: 700; } +.tc-drawer-total span:last-child { color: #F97316; font-size: 18px; font-variant-numeric: tabular-nums; } +.ac-biz-grid .ac-meta-value.ac-amount-hc { color: #8B5CF6; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; } +.tc-hero--return { background: linear-gradient(135deg, #8B5CF6 0%, #7C3AED 100%); box-shadow: 0 10px 28px rgba(139, 92, 246, 0.35); } +.tc-section-badge--purple { color: #8B5CF6; background: rgba(139, 92, 246, 0.12); } +.hc-stat-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding: 12px 14px 14px; } +.hc-stat-card { padding: 12px; background: ${COLOR_PAGE}; border-radius: 10px; border: 1px solid ${COLOR_LINE}; } +.hc-stat-label { font-size: 11px; color: ${COLOR_MUTED}; margin-bottom: 6px; } +.hc-stat-val { font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums; color: ${COLOR_TEXT}; } +.hc-stat-val.warn { color: #F97316; } +.hc-stat-val.success { color: ${COLOR_SUCCESS}; } +.hc-stat-val.danger { color: ${COLOR_DANGER}; } +.hc-group-block { padding: 0 14px 12px; } +.hc-group-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 0; cursor: pointer; touch-action: manipulation; } +.hc-group-title { font-size: 14px; font-weight: 700; color: ${COLOR_TEXT}; } +.hc-group-meta { font-size: 11px; color: ${COLOR_MUTED}; margin-top: 2px; } +.hc-fee-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; background: ${COLOR_PAGE}; border-radius: 8px; margin-bottom: 6px; font-size: 13px; } +.hc-fee-row-name { color: ${COLOR_TEXT_SEC}; flex: 1; min-width: 0; } +.hc-fee-row-amt { font-weight: 700; color: #8B5CF6; font-variant-numeric: tabular-nums; flex-shrink: 0; } +.hc-sub-block { margin-top: 8px; padding-top: 8px; border-top: 1px dashed ${COLOR_LINE}; } +.hc-violation-card { margin: 0 0 8px; padding: 10px 12px; background: ${COLOR_PAGE}; border-radius: 10px; border: 1px solid ${COLOR_LINE}; font-size: 12px; color: ${COLOR_TEXT_SEC}; line-height: 1.55; } +.ac-mp-back { position: absolute; left: 8px; width: 40px; height: 40px; border: none; background: transparent; color: ${COLOR_TEXT}; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 2; } +.ac-mp-back:active { background: rgba(0,0,0,.05); } +.ac-phone--detail { position: relative; } +`; + +const IconSearch = () => ( + +); + +const IconChevron = () => ( + +); + +const IconBack = () => ( + +); + +const IconEmpty = () => ( + +); + +const FlowTypeIcon = ({ flowType }) => { + const common = { width: 20, height: 20, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }; + const map = { + 合同审批: , + 提车应收款: , + 租赁账单: , + 还车应结款: , + '氢费对账单(对站)': , + '氢费对账单(对客)': , + 车辆调拨: , + 车辆异动: , + }; + return map[flowType] || map['合同审批']; +}; + +const IphoneStatusBar = () => { + const time = moment ? moment().format('HH:mm') : '9:41'; + return ( +
+ {time} + +
+ ); +}; + +const formatMoney = (val) => { + const n = parseFloat(val); + if (Number.isNaN(n)) return val || '—'; + return `¥${n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; +}; + +const formatYuan = (val) => { + const n = parseFloat(val); + if (Number.isNaN(n)) return '—'; + return `${n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} 元`; +}; + +const buildPickupDetail = (task) => { + const isShanghai = task?.bizNo === 'TC-2026-0312'; + const projectInfo = isShanghai + ? { + collectCode: 'HT-ZL-2025-088TC0001', contractCode: 'HT-ZL-2025-088', contractType: '正式合同', + projectName: '上海氢能城际物流项目', customerName: '上海迅杰物流有限公司', + paymentMethod: '预付', paymentCycle: '6个月', contractStart: '2025-03-01', contractEnd: '2026-02-28', + businessDept: '业务2部', businessPerson: '李晓彤', + } + : { + collectCode: 'HT-ZL-2025-001TC0001', contractCode: 'HT-ZL-2025-001', contractType: '正式合同', + projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', + paymentMethod: '预付', paymentCycle: '6个月', contractStart: '2025-01-15', contractEnd: '2026-01-14', + businessDept: '业务1部', businessPerson: '张经理', + }; + const vehicles = isShanghai + ? [ + { key: 'v1', index: 1, brand: '东风', model: 'DFH1180', plateNo: '沪A88123', receivableRent: 62000, actualRent: '61200.00', rentRemark: '首期六期一次性付清', receivableDeposit: 12000, receivableService: 880, actualService: '860.00', discountAmount: '200.00', discountRemark: '首月优惠', serviceItems: [{ name: '代处理费用', receivable: 280, actual: '280.00', discount: '0.00', remark: '' }, { name: '保险上浮', receivable: 600, actual: '580.00', discount: '20.00', remark: '客户协商' }] }, + { key: 'v2', index: 2, brand: '福田', model: 'BJ1180', plateNo: '沪A88234', receivableRent: 58000, actualRent: '58000.00', rentRemark: '', receivableDeposit: 10000, receivableService: 500, actualService: '500.00', discountAmount: '0.00', discountRemark: '', serviceItems: [{ name: '保养费用', receivable: 500, actual: '500.00', discount: '0.00', remark: '含首保' }] }, + { key: 'v3', index: 3, brand: '重汽', model: 'ZZ1187', plateNo: '沪A88345', receivableRent: 64000, actualRent: '63600.00', rentRemark: '按合同约定', receivableDeposit: 12000, receivableService: 720, actualService: '700.00', discountAmount: '150.00', discountRemark: '批量提车减免', serviceItems: [{ name: '代处理费用', receivable: 220, actual: '220.00', discount: '0.00', remark: '' }, { name: '上牌服务', receivable: 500, actual: '480.00', discount: '20.00', remark: '已含上牌' }] }, + ] + : [ + { key: 'v1', index: 1, brand: '东风', model: 'DFH1180', plateNo: '浙A12345', receivableRent: 30000, actualRent: '29800.00', rentRemark: '首期六期一次性付清', receivableDeposit: 10000, receivableService: 700, actualService: '680.00', discountAmount: '200.00', discountRemark: '首月优惠', serviceItems: [{ name: '代处理费用', receivable: 200, actual: '200.00', discount: '0.00', remark: '' }, { name: '保险上浮', receivable: 500, actual: '480.00', discount: '20.00', remark: '客户协商' }] }, + { key: 'v2', index: 2, brand: '福田', model: 'BJ1180', plateNo: '浙A23456', receivableRent: 27000, actualRent: '27000.00', rentRemark: '', receivableDeposit: 8000, receivableService: 300, actualService: '300.00', discountAmount: '0.00', discountRemark: '', serviceItems: [{ name: '保养费用', receivable: 300, actual: '300.00', discount: '0.00', remark: '含首保' }] }, + ]; + const hasHydrogenPrepay = true; + const hydrogen = isShanghai + ? { receivable: '4200.00', actual: '4100.00', discount: '100.00', discountRemark: '预付款批量减免' } + : { receivable: '3580.00', actual: '3500.00', discount: '80.00', discountRemark: '预付款批量减免' }; + const approvalSteps = isShanghai + ? [ + { title: '业务部主管', department: '业务2部', status: '已通过', person: '李晓彤', approveTime: '2026-05-30 10:30' }, + { title: '财务部', department: '财务部', status: '待审批', person: '张明辉', approveTime: '—' }, + ] + : [ + { title: '业务部主管', department: '业务1部', status: '已通过', person: '张经理', approveTime: '2026-02-28 09:30' }, + { title: '财务部', department: '财务部', status: '已通过', person: '李财务', approveTime: '2026-02-28 10:15' }, + { title: '事业部主管', department: '事业部', status: '已通过', person: '王总', approveTime: '2026-03-01 11:20' }, + ]; + return { + projectInfo, vehicles, hasHydrogenPrepay, hydrogen, approvalSteps, + invoiceMethod: '先开票后付款', + invoiceRemark: isShanghai + ? '增值税专用发票,税率13%,开票项目:*现代服务*车辆租赁费;备注:上海氢能城际物流项目-提车首付款' + : '增值税专用发票,税率13%,开票项目:*现代服务*车辆租赁费;备注:嘉兴氢能示范项目-提车首付款', + }; +}; + +const calcPickupTotals = (vehicles, hasHydrogenPrepay, hydrogen) => { + let receivableRent = 0; let actualRent = 0; let receivableDeposit = 0; + let receivableService = 0; let actualService = 0; let discountTotal = 0; + vehicles.forEach((v) => { + receivableRent += Number(v.receivableRent) || 0; + actualRent += parseFloat(v.actualRent) || 0; + receivableDeposit += Number(v.receivableDeposit) || 0; + receivableService += Number(v.receivableService) || 0; + actualService += parseFloat(v.actualService) || 0; + discountTotal += parseFloat(v.discountAmount) || 0; + }); + const hydrogenReceivable = hasHydrogenPrepay ? (parseFloat(hydrogen.receivable) || 0) : 0; + const hydrogenActual = hasHydrogenPrepay ? (parseFloat(hydrogen.actual) || 0) : 0; + return { + receivableRent: receivableRent.toFixed(2), actualRent: actualRent.toFixed(2), + receivableDeposit: receivableDeposit.toFixed(2), receivableService: receivableService.toFixed(2), + actualService: actualService.toFixed(2), discountTotal: discountTotal.toFixed(2), + receivableTotal: (receivableRent + receivableDeposit + receivableService + hydrogenReceivable).toFixed(2), + actualTotal: (actualRent + receivableDeposit + actualService - discountTotal + hydrogenActual).toFixed(2), + }; +}; + +const statusMeta = (status) => { + if (status === '已通过') return { color: 'success', label: status }; + if (status === '已驳回') return { color: 'error', label: status }; + if (status === '已撤回') return { color: 'default', label: status }; + return { color: 'warning', label: status }; +}; + +const isPendingStatus = (status) => status === '审批中' || status === '待审批'; + +const MiniProgramChrome = ({ title, showBack, onBack }) => ( +
+ +
+ {showBack ? ( + + ) : null} + {title} +
+
+); + +const TcInfoRow = ({ label, value, full }) => ( +
+
{label}
+
{value || '—'}
+
+); + +const TcVehicleCard = ({ vehicle }) => { + const [serviceOpen, setServiceOpen] = useState(false); + const vehicleActual = ( + parseFloat(vehicle.actualRent || 0) + + parseFloat(vehicle.receivableDeposit || 0) + + parseFloat(vehicle.actualService || 0) + - parseFloat(vehicle.discountAmount || 0) + ).toFixed(2); + return ( +
+
+
+
{vehicle.plateNo || '待上牌'}
+
{vehicle.brand} · {vehicle.model}
+
+ #{vehicle.index} +
+
+ 本车实收合计 + {formatMoney(vehicleActual)} +
+
+
实收月租金 {formatYuan(vehicle.actualRent)}
+
应收保证金 {formatYuan(vehicle.receivableDeposit)}
+
实收服务费 {formatYuan(vehicle.actualService)}
+
减免金额 {formatYuan(vehicle.discountAmount)}
+
+ {(vehicle.serviceItems || []).length > 0 && ( + <> + + {serviceOpen && ( +
+ {(vehicle.serviceItems || []).map((s, i) => ( +
+ {s.name} + {formatMoney(s.actual)} + 应收 {formatYuan(s.receivable)} · 减免 {formatYuan(s.discount)} +
+ ))} +
+ )} + + )} +
+ ); +}; + +const APPROVAL_DECISION_META = { + approve: { title: '审批通过', success: '审批已通过(原型)' }, + reject: { title: '审批驳回', success: '已驳回(原型)' }, + terminate: { title: '审批终止', success: '已终止(原型)' }, +}; + +const MOCK_CC_CANDIDATES = ['李晓彤', '陈高伟', '王法务', '财务-赵敏']; +const UPLOAD_ACCEPT = '.png,.jpg,.jpeg,.doc,.docx,.xlsx,.xls,.ppt,.pdf'; +const UPLOAD_HINT = '请上传不超过 20MB 的 png, jpg, jpeg, doc, docx, xlsx, xls, ppt, pdf 格式文件'; + +const emptyDecisionForm = () => ({ + notifyEmail: false, + notifySms: false, + ccUsers: [], + opinion: '', + attachments: [], +}); + +const ApprovalDecisionDrawer = ({ open, actionType, onClose, onConfirm }) => { + const meta = APPROVAL_DECISION_META[actionType] || APPROVAL_DECISION_META.approve; + const [form, setForm] = useState(emptyDecisionForm); + const [ccPickerOpen, setCcPickerOpen] = useState(false); + const fileInputRef = useRef(null); + + useEffect(() => { + if (open) { + setForm(emptyDecisionForm()); + setCcPickerOpen(false); + } + }, [open, actionType]); + + const handleDrawerClose = () => { + if (ccPickerOpen) { + setCcPickerOpen(false); + return; + } + onClose(); + }; + + const toggleCcUser = (name) => { + setForm((p) => ({ + ...p, + ccUsers: p.ccUsers.includes(name) ? p.ccUsers.filter((n) => n !== name) : [...p.ccUsers, name], + })); + }; + + const handleUpload = (e) => { + const files = Array.from(e.target.files || []); + if (!files.length) return; + setForm((p) => ({ + ...p, + attachments: [ + ...p.attachments, + ...files.map((f) => ({ id: `${f.name}-${Date.now()}`, name: f.name, size: f.size })), + ], + })); + e.target.value = ''; + }; + + const handleConfirm = () => { + onConfirm?.({ actionType, ...form, notifyInternal: true }); + onClose(); + }; + + if (!Drawer) return null; + + return ( + + {ccPickerOpen ? ( + <> +
+ {MOCK_CC_CANDIDATES.map((name) => ( + + ))} +
+
+ + +
+ + ) : ( + <> +
+
+
通知方式
+
+ + + +
+
+
+
附件上传
+ + +
{UPLOAD_HINT}
+ {form.attachments.length > 0 && ( +
+ {form.attachments.map((f) => ( +
+ {f.name} + +
+ ))} +
+ )} +
+
+
抄送人
+ + {form.ccUsers.length > 0 && ( +
+ {form.ccUsers.map((name) => ( + + {name} + + + ))} +
+ )} +
+
+
审批意见
+