// 【重要】必须使用 const Component 作为组件变量名 // 加氢站管理 - 站点信息(列表 + KPI 分类 + 独立新建/编辑页 + 使用说明书) var H2_REGION_CASCADER_OPTIONS = [ { value: '浙江省', label: '浙江省', children: [{ value: '嘉兴市', label: '嘉兴市' }, { value: '杭州市', label: '杭州市' }, { value: '宁波市', label: '宁波市' }] }, { value: '上海市', label: '上海市', children: [{ value: '上海市', label: '上海市' }] }, { value: '江苏省', label: '江苏省', children: [{ value: '南京市', label: '南京市' }, { value: '苏州市', label: '苏州市' }] }, { value: '广东省', label: '广东省', children: [{ value: '广州市', label: '广州市' }, { value: '深圳市', label: '深圳市' }] } ]; var H2_STATION_KPI_CARDS = [ { key: 'all', type: 'total', title: '全部加氢站', desc: '纳入台账管理的全部站点' }, { key: 'balanceAlert', type: 'warning', title: '预付余额预警站点', desc: '预付余额低于所设提醒阈值的站点' }, { key: 'arrears', type: 'unuploaded', title: '已欠费站点', desc: '预付余额小于 0 的站点' }, { key: 'none', type: 'unuploaded', title: '无加氢站点', desc: '暂无加氢记录的站点' } ]; var H2_SIGNED_FILTER_CARDS = [ { key: 'yes', type: 'normal', title: '签约站点', desc: '已签约的加氢站点,点击快捷筛选' }, { key: 'no', type: 'unuploaded', title: '普通站点', desc: '尚未签约的加氢站点,点击快捷筛选' } ]; var H2_BUSINESS_STATUS_OPTIONS = [ { value: '营业中', label: '营业中' }, { value: '暂停营业', label: '暂停营业' }, { value: '停止营业', label: '停止营业' } ]; /** @see web端/styles/oneos-ant-table-global-fix.js — 与 ant-table-global-fix.css 保持同步 */ var ONEOS_ANT_TABLE_GLOBAL_FIX = [ '.ant-table-container .ant-table-header { margin-bottom: 0 !important; }', '.ant-table-container .ant-table-body { margin-top: 0 !important; }', '.ant-table-container .ant-table-body > table, .ant-table-content table { margin-top: 0 !important; }', '.ant-table-tbody > tr.ant-table-measure-row, .ant-table-tbody > tr.ant-table-measure-row > td, .ant-table-tbody > tr.ant-table-measure-row > th { display: none !important; height: 0 !important; max-height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; pointer-events: none !important; }' ]; var H2_PAGE_STYLE = ONEOS_ANT_TABLE_GLOBAL_FIX.concat([ '.h2-station-page { --h2-form-row-gap: 20px; --h2-form-col-gap: 24px; --h2-form-label-gap: 8px; padding: 24px 24px 80px; height: 100vh; display: flex; flex-direction: column; background: linear-gradient(165deg, #f1f5f9 0%, #f8fafc 50%, #f1f5f9 100%); overflow: hidden; box-sizing: border-box; }', '.h2-station-page .lc-filter-card.ant-card { border-radius: 16px !important; border: 1px solid #e2e8f0 !important; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.03) !important; margin-bottom: 16px; }', '.h2-station-page .lc-filter-card > .ant-card-head { border-bottom: 1px solid #f1f5f9 !important; min-height: auto; padding: 12px 20px !important; }', '.h2-station-page .lc-filter-card > .ant-card-head .ant-card-head-title { font-size: 15px !important; font-weight: 700 !important; color: #0f172a !important; padding: 0 !important; }', '.h2-station-page .lc-filter-card > .ant-card-body { padding: 16px 20px 20px !important; }', '.h2-station-page .lc-filter-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px 24px; align-items: center; }', '@media (max-width: 1280px) { .h2-station-page .lc-filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }', '@media (max-width: 640px) { .h2-station-page .lc-filter-grid { grid-template-columns: 1fr; } }', '.h2-station-page .lc-filter-field { display: flex; align-items: center; gap: 12px; min-width: 0; min-height: 32px; }', '.h2-station-page .lc-filter-field-label { flex: 0 0 88px; text-align: right; font-size: 13px; font-weight: 500; color: #475569; line-height: 32px; white-space: nowrap; }', '.h2-station-page .lc-filter-field-control { flex: 1; min-width: 0; }', '.h2-station-page .lc-filter-field-control .ant-input, .h2-station-page .lc-filter-field-control .ant-input-affix-wrapper, .h2-station-page .lc-filter-field-control .ant-select .ant-select-selector, .h2-station-page .lc-filter-field-control .ant-cascader .ant-select-selector { width: 100%; height: 32px !important; min-height: 32px !important; border-radius: 8px !important; box-sizing: border-box; }', '.h2-station-page .lc-filter-field-control .ant-input-affix-wrapper { display: inline-flex; align-items: center; padding-top: 0 !important; padding-bottom: 0 !important; }', '.h2-station-page .lc-filter-field-control .ant-input-affix-wrapper > input.ant-input { height: 30px; line-height: 30px; padding-top: 0; padding-bottom: 0; }', '.h2-station-page .lc-filter-field-control .ant-select-single .ant-select-selector { display: flex; align-items: center; }', '.h2-station-page .lc-filter-field-control .ant-select-single .ant-select-selector .ant-select-selection-search-input { height: 30px; }', '.h2-station-page .lc-filter-field-control .ant-select-single .ant-select-selector .ant-select-selection-item, .h2-station-page .lc-filter-field-control .ant-select-single .ant-select-selector .ant-select-selection-placeholder { line-height: 30px !important; }', '.h2-station-page .lc-filter-field-control .ant-cascader .ant-select-selector { display: flex; align-items: center; }', '.h2-station-page .lc-filter-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; padding-top: 16px; border-top: 1px solid #f1f5f9; }', '.h2-station-page .lc-alert-stats-row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }', '@media (max-width: 1400px) { .h2-station-page .lc-alert-stats-row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }', '@media (max-width: 900px) { .h2-station-page .lc-alert-stats-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }', '@media (max-width: 640px) { .h2-station-page .lc-alert-stats-row { grid-template-columns: 1fr; } }', '.h2-station-page .lc-alert-card { display: flex; align-items: center; gap: 14px; padding: 16px 30px 16px 16px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; position: relative; overflow: hidden; min-width: 0; min-height: 72px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-station-page .lc-alert-card-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }', '.h2-station-page .lc-alert-card-icon { flex-shrink: 0; width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: linear-gradient(145deg, #0d7a63 0%, #065f4a 100%); color: #fff; box-shadow: 0 4px 12px rgba(6, 95, 74, 0.28); }', '.h2-station-page .lc-alert-card-icon svg { display: block; flex-shrink: 0; }', '.h2-station-page .lc-alert-card-val { font-size: 24px; font-weight: 800; line-height: 1.1; color: #0f172a; font-variant-numeric: tabular-nums; }', '.h2-station-page .lc-alert-card-title { font-size: 13px; font-weight: 500; color: #64748b; line-height: 1.3; }', '.h2-station-page .lc-alert-card-tip-anchor { position: absolute; top: 8px; right: 8px; z-index: 2; line-height: 0; }', '.h2-station-page .lc-alert-card-tip { width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: #94a3b8; background: rgba(255,255,255,0.92); border: 1px solid #e2e8f0; cursor: help; line-height: 0; }', '.h2-station-page .lc-alert-card-tip:hover { color: #64748b; border-color: #cbd5e1; background: #fff; }', '.h2-station-page .lc-alert-card--total, .h2-station-page .lc-alert-card--normal, .h2-station-page .lc-alert-card--warning, .h2-station-page .lc-alert-card--unuploaded { background: #fff; border-color: #e2e8f0; }', '.h2-station-page .lc-alert-card--normal .lc-alert-card-val, .h2-station-page .lc-alert-card--warning .lc-alert-card-val, .h2-station-page .lc-alert-card--unuploaded .lc-alert-card-val { color: #0f172a; }', '.h2-station-page .lc-alert-card-clickable { cursor: pointer; transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease; }', '.h2-station-page .lc-alert-card-clickable:hover { box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08); }', '.h2-station-page .lc-alert-card-clickable:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-page .lc-alert-card-active { box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.25) !important; border-color: #10b981 !important; }', '.h2-station-page .lc-table-section { margin-bottom: 0; flex: 1; display: flex; flex-direction: column; min-height: 0; }', '.h2-station-page .lc-table-toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px 16px; margin-bottom: 8px; min-height: 32px; }', '.h2-station-page .lc-table-legend-outer { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; padding: 6px 4px; font-size: 12px; color: #64748b; }', '.h2-station-page .lc-table-legend-label { font-weight: 600; color: #64748b; }', '.h2-station-page .lc-table-legend-item { display: inline-flex; align-items: center; gap: 6px; }', '.h2-station-page .lc-table-legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }', '.h2-station-page .lc-table-toolbar-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }', '.h2-station-page .lc-table-card { background: #fff; border-radius: 16px; border: 1px solid #e2e8f0; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.03); overflow: hidden; flex: 1; overflow-y: auto; display: flex; flex-direction: column; }', '.h2-station-page .lc-list-table .ant-table-wrapper, .h2-station-page .lc-list-table .ant-table { width: 100% !important; }', '.h2-station-page .lc-table-card .ant-table-thead > tr > th { background: #f8fafc !important; color: #475569 !important; font-weight: 700 !important; font-size: 13px !important; border-bottom: 1px solid #e2e8f0 !important; padding: 12px 16px !important; }', '.h2-station-page .lc-table-card .ant-table-tbody > tr.ant-table-measure-row, .h2-station-page .lc-table-card .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-station-page .lc-table-card .ant-table-tbody > tr:not(.ant-table-measure-row) > td { padding: 12px 16px !important; font-size: 13px; }', '.h2-station-page .lc-table-card .ant-table-tbody > tr:not(.ant-table-measure-row):hover > td { background: #f8fafc !important; }', '.h2-station-page .lc-table-card .ant-pagination { margin: 0 !important; padding: 12px 16px !important; border-top: 1px solid #f1f5f9; }', '.h2-station-page .lc-action-btn { font-weight: 600 !important; color: #10b981 !important; padding: 0 !important; min-height: 44px; }', '.h2-station-page .lc-action-btn-danger { color: #ef4444 !important; }', '.h2-station-page .h2-action-more-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 8px; color: #64748b; cursor: pointer; transition: background 0.15s ease, color 0.15s ease; }', '.h2-station-page .h2-action-more-btn:hover { background: #f1f5f9; color: #334155; }', '.h2-station-page .h2-action-more-btn:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-page .h2-row-actions { display: inline-flex; align-items: center; gap: 4px; }', '.h2-station-page .lc-station-name { font-weight: 700; color: #0f172a; font-size: 13px; }', '.h2-station-page .lc-station-name-cell { min-width: 0; }', '.h2-station-page .lc-station-name-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }', '.h2-station-page .lc-station-name-row .lc-station-name { min-width: 0; }', '.h2-station-page .lc-station-address-line { margin-top: 4px; font-size: 12px; color: #64748b; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }', '.h2-station-page .lc-station-signed-tag { margin: 0 !important; border-radius: 6px !important; font-weight: 600 !important; flex-shrink: 0; line-height: 20px !important; }', '.h2-station-page .lc-refuel-kg-row { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; min-width: 0; }', '.h2-station-page .lc-refuel-freq-tag { margin: 0 !important; border-radius: 6px !important; font-weight: 600 !important; flex-shrink: 0; line-height: 20px !important; }', '.h2-station-page .lc-station-region { color: #64748b; font-size: 12px; margin-bottom: 2px; }', '.h2-station-page .lc-station-contact-name { font-weight: 600; color: #0f172a; font-size: 13px; line-height: 1.35; }', '.h2-station-page .lc-station-contact-phone { color: #64748b; font-size: 12px; margin-top: 2px; line-height: 1.35; font-variant-numeric: tabular-nums; }', '.h2-station-page .h2-region-address { display: flex; flex-direction: column; gap: 12px; width: 100%; }', '.h2-station-page .h2-region-address .ant-select, .h2-station-page .h2-region-address .ant-input { width: 100%; }', '.h2-station-page .h2-region-address--inline { flex-direction: row; align-items: center; gap: 8px; }', '.h2-station-page .h2-region-address--inline .h2-region-address-cascader { flex: 0 0 38%; max-width: 220px; min-width: 140px; }', '.h2-station-page .h2-region-address--inline .h2-region-address-detail { flex: 1; min-width: 0; }', '.h2-station-page .h2-create-radio-group { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; min-height: 32px; }', '.h2-station-page .h2-create-radio-group .ant-radio-wrapper { margin-inline-end: 0; font-size: 14px; color: #1d2129; }', '.h2-station-page .h2-contract-dates { display: flex; align-items: center; gap: 8px; width: 100%; flex-wrap: wrap; }', '.h2-station-page .h2-contract-dates-sep { color: #94a3b8; font-size: 13px; flex-shrink: 0; }', '.h2-station-page .h2-contract-upload { display: flex; flex-direction: column; gap: 10px; width: 100%; }', '.h2-station-page .h2-contract-upload-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }', '.h2-station-page .h2-contract-file-list { display: flex; flex-direction: column; gap: 6px; }', '.h2-station-page .h2-contract-file-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 10px; border-radius: 6px; background: #f8fafc; border: 1px solid #e2e8f0; font-size: 13px; }', '.h2-station-page .h2-contract-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #334155; }', '.h2-station-page .h2-contract-file-btns { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }', '.h2-station-page--create .h2-create-form .h2-create-upload-btn.ant-upload-wrapper { width: auto; }', '.h2-station-page--create .h2-create-form .h2-region-address { gap: 12px; margin-top: 0; }', '.h2-station-page .h2-option-btn-group, .h2-business-status-modal .h2-option-btn-group { display: flex; flex-wrap: wrap; gap: 8px; }', '.h2-station-page .h2-option-btn, .h2-business-status-modal .h2-option-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 36px; padding: 0 16px; border-radius: 8px; border: 1px solid #e2e8f0; background: #fff; color: #475569; font-size: 13px; font-weight: 600; cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease; }', '.h2-station-page .h2-option-btn:hover:not(:disabled), .h2-business-status-modal .h2-option-btn:hover:not(:disabled) { border-color: #86efac; color: #047857; }', '.h2-station-page .h2-option-btn:focus-visible, .h2-business-status-modal .h2-option-btn:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-page .h2-option-btn:disabled, .h2-business-status-modal .h2-option-btn:disabled { opacity: 0.55; cursor: not-allowed; }', '.h2-station-page .h2-option-btn--active, .h2-business-status-modal .h2-option-btn--active { box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08); }', '.h2-station-page .h2-option-btn--active.h2-option-btn--open, .h2-business-status-modal .h2-option-btn--active.h2-option-btn--open { border-color: #10b981; background: #ecfdf5; color: #047857; }', '.h2-station-page .h2-option-btn--active.h2-option-btn--pause, .h2-business-status-modal .h2-option-btn--active.h2-option-btn--pause { border-color: #f97316; background: #fff7ed; color: #c2410c; }', '.h2-station-page .h2-option-btn--active.h2-option-btn--stop, .h2-business-status-modal .h2-option-btn--active.h2-option-btn--stop { border-color: #ef4444; background: #fef2f2; color: #b91c1c; }', '.h2-station-page .h2-option-btn--active.h2-option-btn--allday, .h2-business-status-modal .h2-option-btn--active.h2-option-btn--allday { border-color: #10b981; background: #ecfdf5; color: #047857; }', '.h2-station-page .h2-option-btn--active.h2-option-btn--custom, .h2-business-status-modal .h2-option-btn--active.h2-option-btn--custom { border-color: #3b82f6; background: #eff6ff; color: #1d4ed8; }', '.h2-station-page .h2-business-hours, .h2-business-status-modal .h2-business-hours { display: flex; flex-direction: column; gap: 12px; width: 100%; }', '.h2-business-status-modal .h2-business-hours-custom-panel { margin-top: 4px; }', '.h2-station-page .h2-business-hours-custom-panel, .h2-business-status-modal .h2-business-hours-custom-panel { padding: 12px 14px; border-radius: 10px; background: #f8fafc; border: 1px solid #e2e8f0; }', '.h2-station-page .h2-business-hours-custom-panel__title, .h2-business-status-modal .h2-business-hours-custom-panel__title { margin-bottom: 10px; font-size: 12px; font-weight: 700; color: #475569; }', '.h2-station-page .h2-business-hours-range, .h2-business-status-modal .h2-business-hours-range { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; width: 100%; }', '.h2-station-page .h2-business-hours-range-item, .h2-business-status-modal .h2-business-hours-range-item { display: flex; flex-direction: column; gap: 6px; min-width: 0; }', '.h2-station-page .h2-business-hours-range-label, .h2-business-status-modal .h2-business-hours-range-label { font-size: 12px; color: #64748b; line-height: 1.2; }', '.h2-station-page--create .h2-create-form .h2-business-hours .ant-picker { width: 100% !important; height: 32px !important; border-radius: 2px !important; border: 1px solid #e5e6eb !important; background: #fff !important; }', '.h2-station-page--create .h2-create-form .h2-business-hours .ant-picker .ant-picker-input > input { font-size: 14px !important; color: #1d2129 !important; }', '.h2-station-page--create .h2-create-form .h2-business-hours .ant-picker:not(.ant-picker-disabled):hover { border-color: #c9cdd4 !important; background: #fff !important; }', '.h2-station-page--create .h2-create-form .h2-business-hours .ant-picker-focused { border-color: #10b981 !important; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2) !important; background: #fff !important; }', '.h2-station-page--create .h2-create-form .h2-business-hours--col4 { gap: 8px; max-width: 100%; }', '.h2-station-page--create .h2-create-form .h2-business-hours--col4 .ant-select { width: 100% !important; max-width: 100%; }', '.h2-station-page--create .h2-create-form .h2-business-hours--col4 .h2-business-hours-range { grid-template-columns: 1fr; gap: 8px; }', '.h2-station-page--create .h2-create-form .ant-col-6 .ant-form-item-control-input { max-width: 100%; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-region-address--stack-col4 { gap: 8px; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-region-address--stack-col4 .h2-region-address-cascader, .h2-station-page--create .h2-create-form--grid-4 .h2-region-address--stack-col4 .h2-region-address-detail { flex: none; max-width: 100%; width: 100%; min-width: 0; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-contract-panel { padding: 10px 12px; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-contract-upload-actions { flex-direction: column; align-items: stretch; gap: 6px; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-contract-upload-actions .ant-btn { width: 100%; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-contract-dates { flex-direction: column; align-items: stretch; gap: 6px; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-contract-dates-sep { text-align: center; line-height: 1; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-create-radio-group { gap: 8px 12px; }', '.h2-station-page--create .h2-supplier-mode-card--compact { flex-direction: column; align-items: stretch; justify-content: flex-start; min-height: 88px; padding: 12px; gap: 8px; }', '.h2-station-page--create .h2-supplier-mode-card--compact .h2-supplier-mode-card-icon { width: 32px; height: 32px; }', '.h2-station-page--create .h2-supplier-mode-card--compact .h2-supplier-mode-card-title { font-size: 13px; }', '.h2-station-page--create .h2-supplier-mode-card--compact .h2-supplier-mode-card-desc { font-size: 11px; line-height: 1.35; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-supplier-link-select { padding: 0; margin: 0; border: none; background: transparent; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-create-upload.ant-upload-wrapper .ant-upload-drag { padding: 10px 8px !important; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-create-upload.ant-upload-wrapper .ant-upload-drag .ant-upload-drag-icon { margin-bottom: 4px !important; }', '.h2-station-page--create .h2-create-form--grid-4 .h2-create-upload.ant-upload-wrapper .ant-upload-drag p { font-size: 12px !important; }', '.h2-station-page .h2-address-paste { display: flex; flex-direction: column; width: 100%; }', '.h2-station-page .ant-drawer-body .ant-form .ant-form-item { margin-bottom: 20px; }', '.h2-station-page .ant-drawer-body .ant-form .ant-form-item:last-child { margin-bottom: 0; }', '.h2-station-page .ant-drawer-body .ant-form .ant-form-item-label { padding-bottom: var(--h2-form-label-gap, 8px); }', '.h2-station-page .ant-drawer-body .ant-form .ant-form-item-label > label { min-height: 22px; line-height: 22px; }', '.h2-station-page .ant-drawer-body .ant-divider { margin: 4px 0 20px !important; }', '.h2-station-page .h2-address-paste .ant-input-textarea { font-size: 13px !important; line-height: 1.45 !important; resize: vertical; min-height: 56px; }', '.h2-station-page .h2-prd-modal .ant-modal-body { max-height: 70vh; overflow: auto; }', '.h2-station-page .h2-prd-content { padding: 8px 0; white-space: pre-wrap; font-size: 13px; line-height: 1.65; color: #475569; }', '.h2-station-page .h2-req-doc-panel { max-width: 100%; }', '.h2-station-page .h2-req-doc-panel h1:first-child { margin-top: 0; }', '.h2-station-page .h2-import-template-bar { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 14px 16px; margin-bottom: 14px; border-radius: 12px; background: linear-gradient(135deg, #ecfdf5 0%, #f8fafc 100%); border: 1px solid #bbf7d0; }', '.h2-station-page .h2-import-template-bar-text { font-size: 13px; color: #475569; line-height: 1.55; flex: 1; min-width: 0; }', '.h2-station-page .h2-import-preview { margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: #f8fafc; border: 1px solid #e2e8f0; font-size: 13px; color: #334155; }', '.h2-station-page .h2-import-error-list { margin: 8px 0 0; padding-left: 18px; max-height: 120px; overflow: auto; font-size: 12px; color: #b91c1c; }', '.h2-station-page .h2-status-log-section { margin-top: 20px; padding-top: 16px; border-top: 1px solid #f1f5f9; }', '.h2-station-page .h2-status-log-title { font-size: 14px; font-weight: 700; color: #0f172a; margin-bottom: 10px; }', '.h2-station-page .h2-status-log-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; padding: 8px 12px !important; }', '.h2-station-page .h2-status-log-table .ant-table-tbody > tr > td { font-size: 12px !important; padding: 8px 12px !important; }', '.h2-station-page .h2-refuel-drill-link { border: none; background: none; padding: 0; font-weight: 700; color: #059669; cursor: pointer; font-variant-numeric: tabular-nums; font-size: 13px; }', '.h2-station-page .h2-refuel-drill-link:hover { color: #047857; text-decoration: underline; }', '.h2-station-page .h2-refuel-drill-link:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; border-radius: 4px; }', '.h2-station-page .h2-ledger-totals-bar { display: flex; align-items: stretch; gap: 0; margin-bottom: 12px; border: 1px solid #bae6fd; border-radius: 10px; overflow: hidden; background: #f8fafc; box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04); }', '.h2-station-page .h2-ledger-totals-bar__title { display: flex; align-items: center; justify-content: center; min-width: 72px; padding: 10px 14px; font-size: 14px; font-weight: 700; color: #0f172a; background: #e8f4fc; border-right: 1px solid #bae6fd; flex-shrink: 0; }', '.h2-station-page .h2-ledger-totals-bar__items { display: flex; flex: 1; flex-wrap: wrap; }', '.h2-station-page .h2-ledger-totals-bar__item { flex: 1; min-width: 140px; padding: 8px 20px; border-right: 1px solid #e2e8f0; display: flex; flex-direction: column; justify-content: center; gap: 4px; }', '.h2-station-page .h2-ledger-totals-bar__item:last-child { border-right: none; }', '.h2-station-page .h2-ledger-totals-bar__label { font-size: 12px; color: rgba(15, 23, 42, 0.55); font-weight: 500; line-height: 1.2; }', '.h2-station-page .h2-ledger-totals-bar__value { font-size: 16px; font-weight: 700; color: #0f172a; font-variant-numeric: tabular-nums; line-height: 1.3; }', '.h2-refuel-drill-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-refuel-drill-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-refuel-drill-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-refuel-drill-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-refuel-drill-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-refuel-drill-modal .h2-refuel-drill-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-refuel-drill-modal .h2-refuel-drill-station-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #eff6ff 0%, #ecfdf5 50%, #f8fafc 100%); border: 1px solid #bfdbfe; border-radius: 12px; }', '.h2-refuel-drill-modal .h2-refuel-drill-station-card__name { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-refuel-drill-modal .h2-refuel-drill-station-card__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; white-space: nowrap; }', '.h2-refuel-drill-modal .h2-refuel-drill-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }', '@media (max-width: 860px) { .h2-refuel-drill-modal .h2-refuel-drill-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }', '@media (max-width: 520px) { .h2-refuel-drill-modal .h2-refuel-drill-stats { grid-template-columns: 1fr; } }', '.h2-refuel-drill-modal .h2-refuel-drill-stat { display: flex; flex-direction: column; justify-content: center; min-height: 78px; padding: 12px 14px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); min-width: 0; box-sizing: border-box; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat--count { border-left: 4px solid #3b82f6; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat--kg { border-left: 4px solid #10b981; background: linear-gradient(180deg, #fff 0%, #f0fdf4 100%); }', '.h2-refuel-drill-modal .h2-refuel-drill-stat--cost { border-left: 4px solid #f97316; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat--customer { border-left: 4px solid #6366f1; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 8px; line-height: 1.2; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.2; color: #0f172a; word-break: break-all; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__value--count { color: #2563eb; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__value--kg { color: #059669; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__value--cost { color: #ea580c; }', '.h2-refuel-drill-modal .h2-refuel-drill-stat__value--customer { color: #4f46e5; }', '.h2-refuel-drill-modal .h2-refuel-drill-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-refuel-drill-modal .h2-refuel-drill-table-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-refuel-drill-modal .h2-refuel-drill-table-head__left { display: flex; align-items: center; gap: 10px; min-width: 0; }', '.h2-refuel-drill-modal .h2-refuel-drill-table-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-refuel-drill-modal .h2-refuel-drill-table-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-refuel-drill-modal .h2-refuel-record-table { border-radius: 0 !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table { background: transparent !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-container { border: none !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-header { margin-bottom: 0 !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-body { margin-top: 0 !important; overflow-y: auto !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-body table { margin-top: 0 !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-tbody > tr.ant-table-measure-row, .h2-refuel-drill-modal .h2-refuel-record-table .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; border-bottom: 1px solid #e2e8f0 !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td { font-size: 13px !important; padding: 10px 12px !important; vertical-align: middle !important; border-bottom: 1px solid #f8fafc !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-tbody > tr:not(.ant-table-measure-row):last-child > td { border-bottom: none !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .ant-table-tbody > tr:hover > td { background: #eff6ff !important; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-seq { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 24px; border-radius: 6px; background: #f1f5f9; color: #64748b; font-size: 12px; font-weight: 700; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-time { color: #334155; font-variant-numeric: tabular-nums; font-size: 12px; white-space: nowrap; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-plate { font-weight: 700; color: #0f172a; font-variant-numeric: tabular-nums; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-kg { font-weight: 700; color: #059669; font-variant-numeric: tabular-nums; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-money { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-money--cost { color: #ea580c; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-money--customer { color: #4f46e5; font-weight: 700; }', '.h2-refuel-drill-modal .h2-refuel-record-table .h2-refuel-drill-order-no { display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: #475569; background: #f1f5f9; border: 1px solid #e2e8f0; padding: 2px 7px; border-radius: 6px; letter-spacing: 0.02em; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }', '.h2-station-page .h2-prepaid-balance-cell { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: nowrap; box-sizing: border-box; }', '.h2-station-page .h2-prepaid-balance-cell .h2-prepaid-balance-amount { flex: 0 0 auto; white-space: nowrap; border: none; background: none; padding: 0; font: inherit; font-weight: 700; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; font-variant-numeric: tabular-nums; }', '.h2-station-page .h2-prepaid-balance-cell .h2-prepaid-balance-amount:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; border-radius: 4px; }', '.h2-station-page .h2-prepaid-balance-cell .lc-station-signed-tag { flex-shrink: 0; }', '.h2-station-page .lc-table-card .ant-table-tbody > tr:not(.ant-table-measure-row) > td.h2-cell-prepaid-balance { overflow: visible !important; }', '.h2-balance-drill-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-balance-drill-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-balance-drill-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-balance-drill-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-balance-drill-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-balance-drill-modal .h2-balance-drill-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-balance-drill-modal .h2-balance-drill-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }', '@media (max-width: 860px) { .h2-balance-drill-modal .h2-balance-drill-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }', '@media (max-width: 520px) { .h2-balance-drill-modal .h2-balance-drill-stats { grid-template-columns: 1fr; } }', '.h2-balance-drill-modal .h2-balance-drill-stat { display: flex; flex-direction: column; justify-content: center; min-height: 78px; padding: 12px 14px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); min-width: 0; box-sizing: border-box; }', '.h2-balance-drill-modal .h2-balance-drill-stat--balance { border-left: 4px solid #10b981; background: linear-gradient(180deg, #fff 0%, #f0fdf4 100%); }', '.h2-balance-drill-modal .h2-balance-drill-stat--income { border-left: 4px solid #10b981; }', '.h2-balance-drill-modal .h2-balance-drill-stat--expense { border-left: 4px solid #f97316; }', '.h2-balance-drill-modal .h2-balance-drill-stat__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 8px; line-height: 1.2; }', '.h2-balance-drill-modal .h2-balance-trend-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-balance-drill-modal .h2-balance-trend-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-balance-drill-modal .h2-balance-trend-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-balance-drill-modal .h2-balance-trend-head__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-balance-drill-modal .h2-balance-trend-body { padding: 12px 14px 8px; }', '.h2-balance-drill-modal .h2-balance-trend-chart-wrap { position: relative; }', '.h2-balance-drill-modal .h2-balance-trend-svg { display: block; width: 100%; height: auto; }', '.h2-balance-drill-modal .h2-balance-trend-tooltip { position: absolute; z-index: 2; padding: 8px 10px; border-radius: 8px; background: rgba(15, 23, 42, 0.92); color: #f8fafc; font-size: 12px; line-height: 1.45; white-space: nowrap; pointer-events: none; box-shadow: 0 8px 20px rgba(15, 23, 42, 0.18); transform: translate(-50%, calc(-100% - 10px)); }', '.h2-balance-drill-modal .h2-balance-trend-tooltip__date { color: #cbd5e1; font-size: 11px; margin-bottom: 2px; }', '.h2-balance-drill-modal .h2-balance-trend-tooltip__balance { font-weight: 700; font-variant-numeric: tabular-nums; color: #fff; }', '.h2-balance-drill-modal .h2-balance-trend-tooltip__balance--negative { color: #fca5a5; }', '.h2-balance-drill-modal .h2-balance-trend-hit { fill: transparent; cursor: pointer; }', '.h2-balance-drill-modal .h2-balance-trend-point--active { r: 5.5; stroke-width: 2.5; }', '.h2-balance-drill-modal .h2-balance-trend-empty { padding: 36px 16px; text-align: center; font-size: 13px; color: #94a3b8; }', '.h2-balance-drill-modal .h2-balance-trend-legend { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 8px 14px 12px; border-top: 1px solid #f8fafc; }', '.h2-balance-drill-modal .h2-balance-trend-legend__item { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: #64748b; }', '.h2-balance-drill-modal .h2-balance-trend-legend__dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }', '.h2-balance-drill-modal .h2-balance-trend-legend__dot--line { background: #10b981; }', '.h2-balance-drill-modal .h2-balance-trend-legend__dot--area { background: rgba(16, 185, 129, 0.35); }', '.h2-balance-drill-modal .h2-balance-drill-stat__value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.2; color: #0f172a; word-break: break-all; }', '.h2-balance-drill-modal .h2-balance-drill-stat__value--income { color: #059669; }', '.h2-balance-drill-modal .h2-balance-drill-stat__value--expense { color: #ea580c; }', '.h2-balance-drill-modal .h2-balance-drill-stat__value--negative { color: #dc2626; }', '.h2-balance-drill-modal .h2-balance-drill-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-balance-drill-modal .h2-balance-drill-table-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-balance-drill-modal .h2-balance-drill-table-head__left { display: flex; align-items: center; gap: 10px; min-width: 0; }', '.h2-balance-drill-modal .h2-balance-drill-table-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-balance-drill-modal .h2-balance-drill-table-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-balance-drill-modal .h2-balance-record-table { border-radius: 0 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table { background: transparent !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-container { border: none !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-header { margin-bottom: 0 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-body { margin-top: 0 !important; overflow-y: auto !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-body table { margin-top: 0 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-tbody > tr.ant-table-measure-row, .h2-balance-drill-modal .h2-balance-record-table .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; border-bottom: 1px solid #e2e8f0 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td { font-size: 13px !important; padding: 10px 12px !important; vertical-align: middle !important; border-bottom: 1px solid #f8fafc !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-tbody > tr:not(.ant-table-measure-row):last-child > td { border-bottom: none !important; }', '.h2-balance-drill-modal .h2-balance-record-table .ant-table-tbody > tr:hover > td { background: #f0fdf4 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money { font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: -0.01em; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money--income { color: #059669; font-weight: 600; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money--expense { color: #ea580c; font-weight: 600; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money--balance { color: #0f172a; font-weight: 700; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money--negative { color: #dc2626 !important; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-money--muted { color: #cbd5e1; font-weight: 400; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-order-no { display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: #475569; background: #f1f5f9; border: 1px solid #e2e8f0; padding: 2px 7px; border-radius: 6px; letter-spacing: 0.02em; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }', '.h2-balance-drill-modal .h2-balance-record-table .h2-balance-seq { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 24px; border-radius: 6px; background: #f1f5f9; color: #64748b; font-size: 12px; font-weight: 700; }', '.h2-station-view-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-station-view-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-station-view-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-station-view-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-station-view-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-station-view-modal .h2-station-view-panel { display: flex; flex-direction: column; gap: 20px; }', '.h2-station-view-modal .h2-station-view-section__title { margin: 0 0 10px; padding-left: 10px; font-size: 14px; font-weight: 700; color: #334155; line-height: 1.35; border-left: 4px solid #10b981; }', '.h2-station-view-modal .h2-station-view-section .ant-descriptions { background: #fff; border-radius: 10px; overflow: hidden; }', '.h2-station-view-modal .h2-station-view-section .ant-descriptions-item-label { width: 132px !important; font-size: 12px !important; font-weight: 600 !important; color: #64748b !important; background: #fafbfc !important; }', '.h2-station-view-modal .h2-station-view-section .ant-descriptions-item-content { font-size: 13px !important; color: #0f172a !important; font-weight: 500 !important; word-break: break-word; }', '.h2-station-view-modal .h2-station-view-files { display: flex; flex-wrap: wrap; gap: 8px; }', '.h2-station-view-modal .h2-station-view-file-link { display: inline-flex; align-items: center; max-width: 100%; padding: 4px 10px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc; color: #059669; font-size: 13px; font-weight: 600; cursor: pointer; transition: background 0.2s ease, border-color 0.2s ease; }', '.h2-station-view-modal .h2-station-view-file-link:hover { background: #ecfdf5; border-color: #86efac; }', '.h2-station-view-modal .h2-station-view-file-link:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-view-modal .h2-station-view-license-count { font-size: 13px; color: #64748b; margin-bottom: 8px; }', '.h2-station-view-modal .h2-station-view-license-images { display: flex; flex-wrap: wrap; gap: 10px; }', '.h2-station-view-modal .h2-station-view-license-thumb { display: block; padding: 0; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; background: #f8fafc; cursor: pointer; transition: box-shadow 0.2s ease, border-color 0.2s ease; }', '.h2-station-view-modal .h2-station-view-license-thumb:hover { border-color: #86efac; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.15); }', '.h2-station-view-modal .h2-station-view-license-thumb img { display: block; width: 168px; height: 118px; object-fit: cover; }', '.h2-station-view-modal .h2-station-view-license-empty { font-size: 13px; color: #94a3b8; }', '.h2-file-preview-modal .ant-modal-body { padding: 0 !important; background: #0f172a; }', '.h2-file-preview-modal .h2-file-preview-body { display: flex; align-items: center; justify-content: center; min-height: 360px; max-height: 78vh; overflow: auto; padding: 12px; box-sizing: border-box; }', '.h2-file-preview-modal .h2-file-preview-body iframe { width: 100%; min-height: 70vh; border: none; background: #fff; border-radius: 8px; }', '.h2-file-preview-modal .h2-file-preview-body img { max-width: 100%; max-height: 76vh; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); }', '.h2-station-view-modal .h2-station-view-empty { padding: 20px 16px; text-align: center; font-size: 13px; color: #94a3b8; background: #fff; border: 1px dashed #e2e8f0; border-radius: 10px; }', '.h2-price-config-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-price-config-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-price-config-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-price-config-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-price-config-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-price-config-modal .h2-price-config-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-price-config-modal .h2-price-config-station-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #fff7ed 0%, #fffbeb 50%, #f8fafc 100%); border: 1px solid #fed7aa; border-radius: 12px; }', '.h2-price-config-modal .h2-price-config-station-card__name { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-price-config-modal .h2-price-config-station-card__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; white-space: nowrap; }', '.h2-price-config-modal .h2-price-config-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }', '@media (max-width: 640px) { .h2-price-config-modal .h2-price-config-stats { grid-template-columns: 1fr; } }', '.h2-price-config-modal .h2-price-config-stat { display: flex; flex-direction: column; justify-content: center; min-height: 78px; padding: 12px 14px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); min-width: 0; box-sizing: border-box; }', '.h2-price-config-modal .h2-price-config-stat--current { border-left: 4px solid #f97316; background: linear-gradient(180deg, #fff 0%, #fff7ed 100%); }', '.h2-price-config-modal .h2-price-config-stat--history { border-left: 4px solid #3b82f6; }', '.h2-price-config-modal .h2-price-config-stat--pending { border-left: 4px solid #6366f1; }', '.h2-price-config-modal .h2-price-config-stat__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 8px; line-height: 1.2; }', '.h2-price-config-modal .h2-price-config-stat__value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.2; color: #0f172a; word-break: break-all; }', '.h2-price-config-modal .h2-price-config-stat__value--current { color: #ea580c; }', '.h2-price-config-modal .h2-price-config-stat__value--history { color: #2563eb; }', '.h2-price-config-modal .h2-price-config-stat__value--pending { color: #4f46e5; }', '.h2-price-config-modal .h2-price-config-form-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-price-config-modal .h2-price-config-form-wrap .ant-form-item { margin-bottom: 14px; }', '.h2-price-config-modal .h2-price-config-form-wrap .ant-form-item:last-child { margin-bottom: 0; }', '.h2-price-config-modal .h2-price-config-form-wrap .ant-form-item-label > label { font-size: 13px; font-weight: 600; color: #334155; }', '.h2-price-config-modal .h2-price-config-form-wrap .ant-input, .h2-price-config-modal .h2-price-config-form-wrap .ant-picker { width: 100% !important; border-radius: 8px !important; }', '.h2-price-config-modal .h2-price-config-form-hint { margin-top: 10px; padding: 8px 12px; font-size: 12px; color: #64748b; line-height: 1.5; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; }', '.h2-price-config-modal .h2-price-config-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-price-config-modal .h2-price-config-table-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-price-config-modal .h2-price-config-table-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-price-config-modal .h2-price-config-table-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-price-config-modal .h2-price-config-record-table { border-radius: 0 !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table { background: transparent !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-container { border: none !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-header { margin-bottom: 0 !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-body { margin-top: 0 !important; overflow-y: auto !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-body table { margin-top: 0 !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-tbody > tr.ant-table-measure-row, .h2-price-config-modal .h2-price-config-record-table .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; border-bottom: 1px solid #e2e8f0 !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td { font-size: 13px !important; padding: 10px 12px !important; vertical-align: middle !important; border-bottom: 1px solid #f8fafc !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-tbody > tr:not(.ant-table-measure-row):last-child > td { border-bottom: none !important; }', '.h2-price-config-modal .h2-price-config-record-table .ant-table-tbody > tr:hover > td { background: #fff7ed !important; }', '.h2-price-config-modal .h2-price-config-record-table .h2-price-config-money { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }', '.h2-price-config-modal .h2-price-config-record-table .h2-price-config-money--before { color: #64748b; }', '.h2-price-config-modal .h2-price-config-record-table .h2-price-config-money--after { color: #ea580c; font-weight: 700; }', '.h2-business-status-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-business-status-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-business-status-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-business-status-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-business-status-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-business-status-modal .h2-business-status-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-business-status-modal .h2-business-status-station-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #ecfdf5 0%, #f0fdf4 50%, #f8fafc 100%); border: 1px solid #bbf7d0; border-radius: 12px; }', '.h2-business-status-modal .h2-business-status-station-card__name { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-business-status-modal .h2-business-status-station-card__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; white-space: nowrap; }', '.h2-business-status-modal .h2-business-status-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }', '@media (max-width: 640px) { .h2-business-status-modal .h2-business-status-stats { grid-template-columns: 1fr; } }', '.h2-business-status-modal .h2-business-status-stat { display: flex; flex-direction: column; justify-content: center; min-height: 78px; padding: 12px 14px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); min-width: 0; box-sizing: border-box; }', '.h2-business-status-modal .h2-business-status-stat--status { border-left: 4px solid #10b981; background: linear-gradient(180deg, #fff 0%, #f0fdf4 100%); }', '.h2-business-status-modal .h2-business-status-stat--hours { border-left: 4px solid #3b82f6; }', '.h2-business-status-modal .h2-business-status-stat__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 8px; line-height: 1.2; }', '.h2-business-status-modal .h2-business-status-stat__value { font-size: 16px; font-weight: 700; line-height: 1.35; color: #0f172a; word-break: break-word; }', '.h2-business-status-modal .h2-business-status-stat__value--hours { font-size: 15px; font-weight: 700; color: #2563eb; font-variant-numeric: tabular-nums; }', '.h2-business-status-modal .h2-business-status-form-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-business-status-modal .h2-business-status-form-wrap .ant-form-item { margin-bottom: 14px; }', '.h2-business-status-modal .h2-business-status-form-wrap .ant-form-item:last-child { margin-bottom: 0; }', '.h2-business-status-modal .h2-business-status-form-wrap .ant-form-item-label > label { font-size: 13px; font-weight: 600; color: #334155; }', '.h2-business-status-modal .h2-business-status-form-wrap .ant-picker { width: 100% !important; border-radius: 8px !important; }', '.h2-business-status-modal .h2-business-status-form-hint { margin-top: 10px; padding: 8px 12px; font-size: 12px; color: #64748b; line-height: 1.5; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; }', '.h2-business-status-modal .h2-business-status-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-business-status-modal .h2-business-status-table-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-business-status-modal .h2-business-status-table-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-business-status-modal .h2-business-status-table-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-business-status-modal .h2-business-status-record-table { border-radius: 0 !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table { background: transparent !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-container { border: none !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-header { margin-bottom: 0 !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-body { margin-top: 0 !important; overflow-y: auto !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-body table { margin-top: 0 !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-tbody > tr.ant-table-measure-row, .h2-business-status-modal .h2-business-status-record-table .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; border-bottom: 1px solid #e2e8f0 !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td { font-size: 13px !important; padding: 10px 12px !important; vertical-align: middle !important; border-bottom: 1px solid #f8fafc !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-tbody > tr:not(.ant-table-measure-row):last-child > td { border-bottom: none !important; }', '.h2-business-status-modal .h2-business-status-record-table .ant-table-tbody > tr:hover > td { background: #f0fdf4 !important; }', '.h2-statement-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-statement-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-statement-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-statement-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-statement-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-statement-modal .h2-statement-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-statement-modal .h2-statement-station-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 50%, #f8fafc 100%); border: 1px solid #ddd6fe; border-radius: 12px; }', '.h2-statement-modal .h2-statement-station-card__name { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-statement-modal .h2-statement-station-card__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; white-space: nowrap; }', '.h2-statement-modal .h2-statement-form-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-statement-modal .h2-statement-form-wrap .ant-form-item { margin-bottom: 0; }', '.h2-statement-modal .h2-statement-form-wrap .ant-form-item-label > label { font-size: 13px; font-weight: 600; color: #334155; }', '.h2-statement-modal .h2-statement-form-wrap .ant-input { border-radius: 8px !important; }', '.h2-statement-modal .h2-statement-date-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }', '@media (max-width: 520px) { .h2-statement-modal .h2-statement-date-grid { grid-template-columns: 1fr; } }', '.h2-statement-modal .h2-statement-form-hint { margin-top: 10px; padding: 8px 12px; font-size: 12px; color: #64748b; line-height: 1.5; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; }', '.h2-statement-modal .h2-statement-form-hint strong { color: #5b21b6; font-weight: 700; }', '.h2-statement-modal .h2-statement-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }', '@media (max-width: 640px) { .h2-statement-modal .h2-statement-stats { grid-template-columns: 1fr; } }', '.h2-statement-modal .h2-statement-stat { display: flex; flex-direction: column; justify-content: center; min-height: 78px; padding: 12px 14px; border-radius: 12px; border: 1px solid #e2e8f0; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); min-width: 0; box-sizing: border-box; }', '.h2-statement-modal .h2-statement-stat--count { border-left: 4px solid #7c3aed; }', '.h2-statement-modal .h2-statement-stat--kg { border-left: 4px solid #10b981; background: linear-gradient(180deg, #fff 0%, #f0fdf4 100%); }', '.h2-statement-modal .h2-statement-stat--cost { border-left: 4px solid #f97316; }', '.h2-statement-modal .h2-statement-stat__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 8px; line-height: 1.2; }', '.h2-statement-modal .h2-statement-stat__value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.2; color: #0f172a; word-break: break-all; }', '.h2-statement-modal .h2-statement-stat__value--count { color: #6d28d9; }', '.h2-statement-modal .h2-statement-stat__value--kg { color: #059669; }', '.h2-statement-modal .h2-statement-stat__value--cost { color: #ea580c; }', '.h2-statement-modal .h2-statement-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-statement-modal .h2-statement-table-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }', '.h2-statement-modal .h2-statement-table-head__left { display: flex; align-items: center; gap: 10px; min-width: 0; }', '.h2-statement-modal .h2-statement-table-head__title { font-size: 13px; font-weight: 700; color: #334155; }', '.h2-statement-modal .h2-statement-table-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-statement-modal .h2-statement-record-table { border-radius: 0 !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table { background: transparent !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-container { border: none !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-header { margin-bottom: 0 !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-body { margin-top: 0 !important; overflow-y: auto !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-body table { margin-top: 0 !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-tbody > tr.ant-table-measure-row, .h2-statement-modal .h2-statement-record-table .ant-table-tbody > tr.ant-table-measure-row > td { height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; line-height: 0 !important; font-size: 0 !important; overflow: hidden !important; visibility: hidden !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; border-bottom: 1px solid #e2e8f0 !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td { font-size: 13px !important; padding: 10px 12px !important; vertical-align: middle !important; border-bottom: 1px solid #f8fafc !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-tbody > tr:not(.ant-table-measure-row):last-child > td { border-bottom: none !important; }', '.h2-statement-modal .h2-statement-record-table .ant-table-tbody > tr:hover > td { background: #f5f3ff !important; }', '.h2-statement-modal .h2-statement-last-end { margin-top: 10px; padding: 8px 12px; font-size: 12px; color: #475569; line-height: 1.5; background: #faf5ff; border: 1px solid #e9d5ff; border-radius: 8px; font-variant-numeric: tabular-nums; }', '.h2-statement-modal .h2-statement-last-end strong { color: #6d28d9; font-weight: 700; }', '.h2-statement-modal .h2-statement-settlement-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); display: flex; flex-direction: column; gap: 14px; }', '.h2-statement-modal .h2-statement-settlement-wrap .ant-form-item { margin-bottom: 0; }', '.h2-statement-modal .h2-statement-settlement-wrap .ant-form-item-label > label { font-size: 13px; font-weight: 600; color: #334155; }', '.h2-statement-modal .h2-statement-receipt-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }', '@media (max-width: 520px) { .h2-statement-modal .h2-statement-receipt-grid { grid-template-columns: 1fr; } }', '.h2-statement-modal .h2-statement-receipt-amount-input .ant-input { font-variant-numeric: tabular-nums; }', '.h2-statement-modal .h2-statement-receipt-amount-input .ant-input-prefix { color: #64748b; font-weight: 600; margin-inline-end: 4px; }', '.h2-statement-modal .h2-statement-settlement-wrap .ant-picker { width: 100% !important; border-radius: 8px !important; }', '.h2-statement-modal .h2-statement-invoice-upload .h2-contract-upload-actions { gap: 0; }', '.h2-statement-modal .h2-statement-invoice-upload .h2-statement-upload-btn.ant-btn { display: inline-flex !important; align-items: center; gap: 6px; height: 32px !important; padding: 0 14px !important; border-radius: 8px !important; font-size: 13px !important; font-weight: 600 !important; border: 1px solid #e2e8f0 !important; color: #475569 !important; background: #fff !important; box-shadow: none !important; }', '.h2-statement-modal .h2-statement-invoice-upload .h2-statement-upload-btn.ant-btn:hover { border-color: #10b981 !important; color: #059669 !important; background: #f0fdf4 !important; }', '.h2-statement-modal .h2-statement-invoice-upload .h2-statement-upload-btn.ant-btn:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-recharge-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-recharge-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-recharge-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-recharge-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-recharge-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-recharge-modal .h2-recharge-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-recharge-modal .h2-recharge-hint { margin: 0; padding: 10px 12px; font-size: 12px; color: #64748b; line-height: 1.55; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; }', '.h2-recharge-modal .h2-recharge-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }', '.h2-recharge-modal .h2-recharge-toolbar__count { font-size: 13px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-recharge-modal .h2-recharge-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-recharge-modal .h2-recharge-record-table .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; padding: 10px 12px !important; }', '.h2-recharge-modal .h2-recharge-record-table .ant-table-tbody > tr > td { font-size: 13px !important; padding: 8px 10px !important; vertical-align: middle !important; }', '.h2-recharge-modal .h2-recharge-readonly { font-size: 13px; color: #334155; line-height: 1.45; word-break: break-all; }', '.h2-recharge-modal .h2-recharge-readonly--muted { color: #94a3b8; }', '.h2-recharge-modal .h2-recharge-readonly-input.ant-input[disabled] { background: #f8fafc !important; color: #334155 !important; cursor: default !important; border: 1px solid #e2e8f0 !important; }', '.h2-alert-station-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-alert-station-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-alert-station-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-alert-station-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-alert-station-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-alert-station-modal .h2-alert-station-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-alert-station-modal .h2-alert-station-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #fff7ed 0%, #fffbeb 50%, #f8fafc 100%); border: 1px solid #fed7aa; border-radius: 12px; }', '.h2-alert-station-modal .h2-alert-station-head__title { font-size: 14px; font-weight: 700; color: #0f172a; }', '.h2-alert-station-modal .h2-alert-station-head__count { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; }', '.h2-alert-station-modal .h2-alert-station-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-statement-modal .h2-statement-detail-receipt-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-statement-modal .h2-statement-detail-receipt-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; }', '@media (max-width: 520px) { .h2-statement-modal .h2-statement-detail-receipt-grid { grid-template-columns: 1fr; } }', '.h2-statement-modal .h2-statement-detail-receipt-item__label { font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 6px; }', '.h2-statement-modal .h2-statement-detail-receipt-item__value { font-size: 14px; font-weight: 700; color: #0f172a; font-variant-numeric: tabular-nums; }', '.h2-statement-modal .h2-statement-detail-receipt-item__value--amount { color: #ea580c; }', '.h2-statement-modal .h2-statement-detail-receipt-files { margin-top: 12px; padding-top: 12px; border-top: 1px solid #f1f5f9; }', '.h2-statement-modal .h2-statement-detail-receipt-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }', '.h2-statement-modal .h2-statement-detail-receipt-file-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 12px; border-radius: 8px; background: #f8fafc; border: 1px solid #e2e8f0; }', '.h2-statement-modal .h2-statement-detail-receipt-file-name { font-size: 13px; color: #334155; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }', '.h2-statement-modal .h2-statement-balance-readonly { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 32px; padding: 6px 12px; border-radius: 8px; background: #f8fafc; border: 1px solid #e2e8f0; }', '.h2-statement-modal .h2-statement-balance-readonly__amount { font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }', '.h2-statement-modal .h2-statement-balance-readonly__amount--positive { color: #059669; }', '.h2-statement-modal .h2-statement-balance-readonly__amount--negative { color: #dc2626; }', '.h2-statement-history-modal .ant-modal-content { border-radius: 16px !important; overflow: hidden; box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.18) !important; }', '.h2-statement-history-modal .ant-modal-header { padding: 18px 24px 14px !important; border-bottom: 1px solid #f1f5f9 !important; margin-bottom: 0 !important; }', '.h2-statement-history-modal .ant-modal-title { font-size: 17px !important; font-weight: 700 !important; color: #0f172a !important; }', '.h2-statement-history-modal .ant-modal-body { padding: 16px 24px 20px !important; background: #f8fafc; }', '.h2-statement-history-modal .ant-modal-footer { padding: 12px 24px 18px !important; border-top: 1px solid #f1f5f9 !important; background: #fff; }', '.h2-statement-history-modal .h2-statement-history-panel { display: flex; flex-direction: column; gap: 14px; }', '.h2-statement-history-modal .h2-statement-history-station-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 50%, #f8fafc 100%); border: 1px solid #ddd6fe; border-radius: 12px; }', '.h2-statement-history-modal .h2-statement-history-station-card__name { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-statement-history-modal .h2-statement-history-station-card__meta { font-size: 12px; color: #64748b; font-variant-numeric: tabular-nums; white-space: nowrap; }', '.h2-statement-history-modal .h2-statement-history-table-wrap { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); }', '.h2-statement-history-modal .h2-statement-history-table-wrap .ant-table-thead > tr > th { background: #f8fafc !important; font-size: 12px !important; font-weight: 700 !important; color: #475569 !important; }', '.h2-statement-history-modal .h2-statement-history-table-wrap .ant-table-tbody > tr:hover > td { background: #f5f3ff !important; }', '.h2-statement-history-modal .h2-statement-history-balance { font-weight: 700; font-variant-numeric: tabular-nums; }', '.h2-statement-history-modal .h2-statement-history-balance--negative { color: #dc2626; }', '.h2-statement-history-modal .h2-statement-history-balance--positive { color: #059669; }', '.h2-station-page .h2-ledger-totals-bar__value--income { color: #059669 !important; }', '.h2-station-page .h2-ledger-totals-bar__value--expense { color: #ea580c !important; }', '.h2-station-page .h2-ledger-totals-bar__value--negative { color: #dc2626 !important; }', '.h2-station-page.h2-station-page--create { padding: 0 0 96px; height: auto; min-height: 100dvh; overflow: auto; }', '.h2-station-page--create .h2-create-shell { width: 100%; max-width: none; margin: 0; padding: 24px 24px 0; box-sizing: border-box; }', '.h2-station-page.h2-station-page--edit { padding: 0 0 96px; height: auto; min-height: 100dvh; overflow: auto; }', '.h2-station-page--edit .h2-create-shell { width: 100%; max-width: none; margin: 0; padding: 24px 24px 0; box-sizing: border-box; }', '.h2-account-bind-readonly-wrap { padding: 12px 14px; border-radius: 10px; background: #f8fafc; border: 1px solid #e2e8f0; }', '.h2-account-bind-readonly-item { padding: 10px 12px; border-radius: 8px; background: #fff; border: 1px solid #e2e8f0; margin-bottom: 8px; }', '.h2-account-bind-readonly-item:last-child { margin-bottom: 0; }', '.h2-account-bind-readonly-item__name { font-size: 14px; font-weight: 700; color: #0f172a; margin-bottom: 4px; }', '.h2-account-bind-readonly-item__meta { font-size: 12px; color: #64748b; }', '.h2-station-page--create .h2-create-pagehead { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; }', '.h2-station-page--create .h2-create-pagehead-left { display: flex; align-items: center; gap: 12px; min-width: 0; }', '.h2-station-page--create .h2-create-pagehead-title { margin: 0; font-size: 18px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-station-page--create .h2-create-pagehead-desc { margin: 2px 0 0; font-size: 13px; color: #64748b; line-height: 1.45; }', '.h2-station-page--create .h2-create-card.ant-card { margin-bottom: 16px !important; }', '.h2-station-page--create .h2-create-form--grid .h2-create-form-grid { width: 100%; display: flex; flex-direction: column; gap: var(--h2-form-row-gap); }', '.h2-station-page--create .h2-create-form--grid .h2-create-form-grid > .ant-row { margin-bottom: 0 !important; }', '.h2-station-page--create .h2-create-form--grid .ant-form-item-label { padding: 0 0 var(--h2-form-label-gap) !important; min-height: 22px; }', '.h2-station-page--create .h2-create-form--grid .ant-form-item-label > label { display: inline-flex; align-items: center; min-height: 22px; line-height: 22px; font-size: 13px; font-weight: 500; color: #475569; height: auto; }', '.h2-station-page--create .h2-create-form--grid .ant-row { width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }', '.h2-station-page--create .h2-create-form--grid .ant-col { min-width: 0; }', '.h2-station-page--create .h2-create-form--grid .ant-form-item { margin-bottom: 0; width: 100%; }', '.h2-station-page--create .h2-create-form--grid .ant-col > .ant-form-item { display: flex; flex-direction: column; }', '.h2-station-page--create .h2-create-form--grid .ant-form-item-control { flex: 1; width: 100%; }', '.h2-station-page--create .h2-create-form--grid .ant-form-item-control-input, .h2-station-page--create .h2-create-form--grid .ant-form-item-control-input-content { width: 100%; max-width: 100%; }', '.h2-station-page--create .h2-create-form--grid .h2-create-input, .h2-station-page--create .h2-create-form--grid .ant-select, .h2-station-page--create .h2-create-form--grid .ant-cascader, .h2-station-page--create .h2-create-form--grid .ant-picker { width: 100% !important; max-width: 100% !important; }', '.h2-station-page--create .h2-create-form--grid .h2-address-paste, .h2-station-page--create .h2-create-form--grid .h2-contract-panel, .h2-station-page--create .h2-create-form--grid .h2-create-upload { width: 100%; }', '.h2-station-page--create .h2-create-subsection-title { margin: 4px 0 0; padding: 12px 0 10px; font-size: 13px; font-weight: 600; color: #475569; border-bottom: 1px solid #f1f5f9; width: 100%; box-sizing: border-box; }', '.h2-station-page--create .h2-supplier-mode-picker-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; width: 100%; }', '.h2-station-page--create .h2-supplier-mode-picker-row .h2-supplier-mode-card { height: 100%; min-height: 88px; }', '@media (max-width: 768px) { .h2-station-page--create .h2-supplier-mode-picker-row { grid-template-columns: 1fr; gap: 12px; } }', '.h2-station-page--create .h2-create-panel { padding: 16px 18px; border-radius: 12px; background: #f8fafc; border: 1px solid #e8ecf0; }', '.h2-station-page--create .h2-create-panel + .h2-create-panel, .h2-station-page--create .h2-create-panel + .ant-row, .h2-station-page--create .ant-row + .h2-create-panel { margin-top: 16px; }', '.h2-station-page--create .h2-create-panel-head { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; font-size: 13px; font-weight: 700; color: #334155; }', '.h2-station-page--create .h2-create-panel-head::before { content: ""; width: 4px; height: 14px; border-radius: 2px; background: linear-gradient(180deg, #10b981, #6ee7b7); flex-shrink: 0; }', '.h2-station-page--create .h2-create-panel--address .h2-region-address--inline { gap: 12px; }', '.h2-station-page--create .h2-create-panel--address .h2-region-address--inline .h2-region-address-cascader { flex: 0 0 42%; max-width: 280px; }', '.h2-station-page--create .h2-create-panel--address .h2-address-paste { margin-top: 12px; padding-top: 12px; border-top: 1px dashed #e2e8f0; }', '.h2-station-page--create .h2-create-panel--contract .h2-create-radio-group { padding: 10px 14px; background: #fff; border-radius: 10px; border: 1px solid #e2e8f0; }', '.h2-station-page--create .h2-create-contract-fields { display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 14px; }', '@media (min-width: 640px) { .h2-station-page--create .h2-create-contract-fields { grid-template-columns: 1fr 1fr; } }', '.h2-station-page--create .h2-supplier-link-panel { padding: 16px 18px; border-radius: 12px; background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 100%); border: 1px solid #bfdbfe; }', '.h2-station-page--create .h2-account-bind-layout { display: grid; grid-template-columns: minmax(280px, 1fr) minmax(0, 1.65fr); gap: 24px; align-items: start; width: 100%; }', '@media (max-width: 960px) { .h2-station-page--create .h2-account-bind-layout { grid-template-columns: 1fr; } }', '.h2-station-page--create .h2-account-bind-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }', '.h2-station-page--create .h2-account-bind-side .ant-form-item { margin-bottom: 0 !important; }', '.h2-station-page--create .h2-account-bind-side__hint { margin: 0; font-size: 12px; color: #94a3b8; line-height: 1.55; }', '.h2-station-page--create .h2-account-bind-selected-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }', '.h2-station-page--create .h2-account-bind-selected-label { font-size: 13px; color: #64748b; }', '.h2-station-page--create .h2-account-bind-selected-count.ant-tag { margin: 0 !important; border-radius: 6px !important; font-weight: 600 !important; line-height: 20px !important; padding: 0 8px !important; }', '.h2-station-page--create .h2-account-bind-preview-area { min-width: 0; padding: 14px 16px; border-radius: 12px; background: #f8fafc; border: 1px solid #e8ecf0; min-height: 180px; box-sizing: border-box; }', '.h2-station-page--create .h2-account-bind-preview-title { font-size: 14px; font-weight: 600; color: #334155; margin-bottom: 12px; }', '.h2-station-page--create .h2-account-bind-preview-cards { display: flex; flex-wrap: wrap; gap: 12px; }', '.h2-station-page--create .h2-account-bind-preview-empty { font-size: 13px; color: #94a3b8; line-height: 1.5; padding: 24px 8px; text-align: center; }', '.h2-station-page--create .h2-account-bind-user-card { position: relative; display: flex; align-items: flex-start; gap: 12px; padding: 12px 34px 12px 12px; width: 220px; max-width: 100%; border: 1px solid #e2e8f0; border-radius: 10px; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); box-sizing: border-box; }', '.h2-station-page--create .h2-account-bind-user-card__avatar { flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(145deg, #10b981 0%, #059669 100%); color: #fff; display: inline-flex; align-items: center; justify-content: center; }', '.h2-station-page--create .h2-account-bind-user-card__body { flex: 1; min-width: 0; }', '.h2-station-page--create .h2-account-bind-user-card__name-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }', '.h2-station-page--create .h2-account-bind-user-card__name { font-size: 14px; font-weight: 700; color: #0f172a; line-height: 1.3; }', '.h2-station-page--create .h2-account-bind-user-card__role.ant-tag { margin: 0 !important; border-radius: 4px !important; font-size: 11px !important; font-weight: 600 !important; line-height: 18px !important; padding: 0 6px !important; }', '.h2-station-page--create .h2-account-bind-user-card__meta { margin-top: 6px; font-size: 12px; color: #94a3b8; line-height: 1.5; }', '.h2-station-page--create .h2-account-bind-user-card__meta + .h2-account-bind-user-card__meta { margin-top: 2px; }', '.h2-station-page--create .h2-account-bind-user-card__remove { position: absolute; top: 8px; right: 8px; width: 20px; height: 20px; border: none; background: transparent; color: #94a3b8; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; padding: 0; font-size: 14px; line-height: 1; }', '.h2-station-page--create .h2-account-bind-user-card__remove:hover { color: #64748b; background: #f1f5f9; }', '.h2-station-page--create .h2-account-bind-user-card__remove:focus-visible { outline: 2px solid #10b981; outline-offset: 1px; }', '.h2-station-page--create .h2-account-bind-hint { margin: 0; font-size: 12px; color: #64748b; line-height: 1.55; }', '.h2-station-page--create .h2-create-readonly-input.ant-input[disabled] { background: #f8fafc !important; color: #64748b !important; cursor: default !important; border: 1px dashed #e2e8f0 !important; }', '.h2-station-page--create .h2-station-type-field { display: flex; flex-direction: column; gap: 8px; width: 100%; }', '.h2-station-page--create .h2-supplier-tabs { width: 100%; }', '.h2-station-page--create .h2-supplier-tabs__nav { display: flex; align-items: flex-end; gap: 32px; border-bottom: 1px solid #e2e8f0; margin-bottom: var(--h2-form-row-gap); }', '.h2-station-page--create .h2-supplier-tabs__item { position: relative; border: none; background: transparent; padding: 0 0 12px; font-size: 14px; font-weight: 500; color: #64748b; cursor: pointer; line-height: 1.4; }', '.h2-station-page--create .h2-supplier-tabs__item:hover { color: #059669; }', '.h2-station-page--create .h2-supplier-tabs__item--active { color: #059669; font-weight: 600; }', '.h2-station-page--create .h2-supplier-tabs__item--active::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: #10b981; border-radius: 1px 1px 0 0; }', '.h2-station-page--create .h2-supplier-tabs__panel { width: 100%; }', '.h2-station-page--create .h2-supplier-linked-preview { margin-top: var(--h2-form-row-gap); }', '.h2-station-page--create .h2-supplier-none-hint { margin: 0; padding: 20px 16px; text-align: center; font-size: 13px; color: #94a3b8; line-height: 1.55; background: #f8fafc; border: 1px dashed #e2e8f0; border-radius: 10px; }', '.h2-station-page--create .h2-create-upload--readonly.ant-upload-wrapper .ant-upload-drag { cursor: default !important; pointer-events: none; opacity: 0.92; }', '.h2-station-page--create .h2-supplier-new-stack { display: flex; flex-direction: column; gap: 16px; }', '.h2-station-page--create .h2-create-form--visual .ant-form-item-label > label { font-weight: 500; color: #334155; }', '.h2-station-page--create .h2-create-form--visual .h2-create-input.ant-input:not(.ant-input-disabled), .h2-station-page--create .h2-create-form--visual .ant-select:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form--visual .ant-cascader:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form--visual .h2-create-input.ant-picker:not(.ant-picker-disabled) { border-radius: 10px !important; height: 40px !important; min-height: 40px !important; border-color: #e2e8f0 !important; }', '.h2-station-page--create .h2-create-form--visual .ant-input { padding: 8px 14px !important; }', '.h2-station-page--create .h2-create-form--visual .ant-select-selection-item { line-height: 38px !important; }', '.h2-station-page--create .h2-create-form--visual .ant-input-lg { height: 44px !important; font-size: 15px !important; border-radius: 10px !important; }', '.h2-station-page--create .h2-create-panel .ant-form-item + .ant-form-item, .h2-station-page--create .h2-create-panel .ant-form-item + .ant-row, .h2-station-page--create .h2-create-panel .ant-row + .ant-form-item, .h2-station-page--create .h2-create-panel .ant-form-item + .h2-address-paste, .h2-station-page--create .h2-create-panel .ant-row + .h2-address-paste { margin-top: 12px; }', '.h2-station-page--create .h2-create-form--visual .h2-create-radio-group { min-height: 40px; }', '.h2-station-page--create .h2-create-form--visual .ant-form-item-control-input { max-width: 100%; }', '.h2-station-page--create .h2-create-back-only, .h2-station-page--edit .h2-create-back-only { display: flex; align-items: center; margin-bottom: 16px; }', '.h2-station-page--create .h2-create-back-btn, .h2-station-page--edit .h2-create-back-btn { display: inline-flex !important; align-items: center; gap: 6px; height: 32px; padding: 0 12px !important; border-radius: 8px !important; font-weight: 500; color: #475569 !important; border: 1px solid #e2e8f0 !important; background: #fff !important; }', '.h2-station-page--create .h2-create-back-btn:hover, .h2-station-page--edit .h2-create-back-btn:hover { color: #059669 !important; border-color: #10b981 !important; background: #f0fdf4 !important; }', '.h2-station-page--create .h2-create-back-btn:focus-visible, .h2-station-page--edit .h2-create-back-btn:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-page .h2-create-card.ant-card { border-radius: 16px !important; border: 1px solid #e2e8f0 !important; box-shadow: 0 4px 24px -6px rgba(15, 23, 42, 0.06) !important; margin-bottom: 0 !important; transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease; }', '.h2-station-page--create .h2-create-card.ant-card:hover { box-shadow: 0 4px 24px -6px rgba(15, 23, 42, 0.06) !important; transform: none; }', '.h2-station-page .h2-create-card > .ant-card-head { border-bottom: 1px solid #f1f5f9 !important; min-height: auto; padding: 16px 22px !important; background: linear-gradient(180deg, #fafbfc 0%, #fff 100%); }', '.h2-station-page .h2-create-card > .ant-card-head .ant-card-head-title { font-size: 15px !important; font-weight: 700 !important; color: #0f172a !important; padding: 0 !important; }', '.h2-station-page .h2-create-card > .ant-card-body { padding: 20px 24px 24px !important; display: flex; flex-direction: column; gap: 16px; }', '@media (max-width: 640px) { .h2-station-page--create .h2-create-card > .ant-card-body { padding: 16px 16px 20px !important; } }', '.h2-station-page .h2-card-title-bar { display: inline-flex; align-items: center; gap: 10px; }', '.h2-station-page .h2-card-title-bar::before { content: ""; width: 3px; height: 16px; border-radius: 2px; background: linear-gradient(180deg, #10b981, #34d399); flex-shrink: 0; }', '.h2-station-page .h2-card-title-icon { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 10px; background: #ecfdf5; color: #059669; flex-shrink: 0; }', '.h2-station-page .h2-form-section { margin-bottom: 0; }', '.h2-station-page .h2-form-section + .h2-form-section { margin-top: var(--h2-form-row-gap); }', '.h2-station-page .h2-form-section-body { display: flex; flex-direction: column; gap: var(--h2-form-row-gap); }', '.h2-station-page .h2-form-section-body > .ant-row { margin: 0 !important; align-items: flex-start; }', '.h2-station-page .h2-form-section-body .ant-form-item { margin-bottom: 0 !important; width: 100%; }', '.h2-station-page .h2-form-section-body .ant-col { display: flex; flex-direction: column; min-width: 0; }', '.h2-station-page .h2-form-section-body .ant-col > .ant-form-item { flex: 1; min-width: 0; }', '.h2-station-page--create .h2-create-form .h2-form-section-body .h2-supplier-mode-card { height: 100%; box-sizing: border-box; }', '.h2-station-page .h2-contract-panel { padding: 14px 16px; border-radius: 10px; background: linear-gradient(180deg, #fafbfc 0%, #fff 100%); border: 1px solid #e8ecf0; }', '.h2-station-page--create .h2-create-topbar-main { display: flex; align-items: center; gap: 14px; min-width: 0; }', '.h2-station-page--create .h2-create-page-title { min-width: 0; }', '.h2-station-page--create .h2-create-page-title h1 { margin: 0; font-size: 16px; font-weight: 700; color: #0f172a; line-height: 1.35; }', '.h2-station-page--create .h2-create-page-title p { margin: 2px 0 0; font-size: 12px; color: #94a3b8; line-height: 1.4; }', '.h2-station-page--create .h2-create-form .ant-picker-range { width: 100% !important; max-width: 100%; }', '.h2-station-page--create .h2-create-form .ant-picker-range .ant-picker-input > input { font-size: 13px !important; }', '.h2-station-page .h2-supplier-mode-card-body { flex: 1; min-width: 0; }', '.h2-station-page--create .h2-create-form .ant-row { width: 100%; }', '.h2-station-page--create .h2-create-form .ant-col-6 { min-width: 0; }', '.h2-station-page--create .h2-create-form .ant-form-item { margin-bottom: 0; }', '.h2-station-page--create .h2-create-form .ant-form-item-label { padding: 0 0 var(--h2-form-label-gap); min-height: 22px; }', '.h2-station-page--create .h2-create-form .ant-form-item-label > label { display: inline-flex; align-items: center; min-height: 22px; line-height: 22px; font-size: 14px; font-weight: 400; color: #1d2129; height: auto; }', '.h2-station-page--create .h2-create-form .ant-form-item-extra { margin-top: 6px; font-size: 12px; line-height: 1.45; }', '.h2-station-page--create .h2-create-form .ant-form-item-label > label::after { display: none !important; }', '.h2-station-page--create .h2-create-input.ant-input:not(.ant-input-disabled):not(:disabled), .h2-station-page--create .h2-create-form .ant-select:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form .ant-cascader:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form .h2-create-input.ant-picker:not(.ant-picker-disabled) { height: 32px !important; min-height: 32px !important; border-radius: 2px !important; font-size: 14px !important; border: 1px solid #e5e6eb !important; background: #fff !important; color: #1d2129 !important; }', '.h2-station-page--create .h2-create-form .h2-create-input.ant-picker:not(.ant-picker-disabled):hover { border-color: #c9cdd4 !important; background: #fff !important; }', '.h2-station-page--create .h2-create-form .h2-create-input.ant-picker-focused { border-color: #10b981 !important; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2) !important; background: #fff !important; }', '.h2-station-page .h2-supplier-mode-card { font-family: inherit; width: 100%; box-sizing: border-box; }', '.h2-station-page--create .h2-create-form .ant-input { padding: 4px 12px !important; }', '.h2-station-page--create .h2-create-form .ant-input-textarea:not(.ant-input-disabled):not(:disabled), .h2-station-page--create .h2-create-form .h2-address-paste .ant-input-textarea:not(.ant-input-disabled) { height: auto !important; min-height: auto !important; border-radius: 2px !important; background: #fff !important; color: #1d2129 !important; border: 1px solid #e5e6eb !important; }', '.h2-station-page--create .h2-create-form .ant-input:not(:disabled):not(.ant-input-disabled):hover, .h2-station-page--create .h2-create-form .ant-select:not(.ant-select-disabled):hover .ant-select-selector, .h2-station-page--create .h2-create-form .ant-cascader:not(.ant-select-disabled):hover .ant-select-selector { background: #fff !important; border-color: #c9cdd4 !important; }', '.h2-station-page--create .h2-create-form .ant-input:focus, .h2-station-page--create .h2-create-form .ant-input-focused, .h2-station-page--create .h2-create-form .ant-select-focused .ant-select-selector, .h2-station-page--create .h2-create-form .ant-cascader-focused .ant-select-selector { background: #fff !important; border-color: #10b981 !important; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2) !important; }', '.h2-station-page--create .h2-create-form .ant-input-disabled, .h2-station-page--create .h2-create-form .ant-input:disabled, .h2-station-page--create .h2-create-form .ant-select-disabled .ant-select-selector, .h2-station-page--create .h2-create-form .ant-cascader.ant-select-disabled .ant-select-selector { background: #f2f3f5 !important; color: rgba(0, 0, 0, 0.25) !important; cursor: not-allowed !important; }', '.h2-station-page--create .h2-create-form .ant-select-selection-item { line-height: 30px !important; }', '.h2-station-page--create .h2-signed-toggle { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; padding: 14px 16px; border-radius: 12px; background: #f8fafc; border: 1px solid #e2e8f0; margin-bottom: 4px; }', '.h2-station-page--create .h2-signed-toggle-label { font-size: 13px; font-weight: 600; color: #334155; }', '.h2-station-page--create .h2-signed-panel { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 0.28s ease-out, opacity 0.22s ease-out; }', '.h2-station-page--create .h2-signed-panel--open { grid-template-rows: 1fr; opacity: 1; }', '.h2-station-page--create .h2-signed-panel-inner { overflow: hidden; min-height: 0; }', '.h2-station-page--create .h2-signed-panel-content { padding-top: 12px; }', '.h2-station-page--create .h2-create-upload.ant-upload-wrapper .ant-upload-drag { border-radius: 12px !important; border: 1px dashed #cbd5e1 !important; background: #fafbfc !important; padding: 16px 12px !important; transition: border-color 0.2s ease, background 0.2s ease; }', '.h2-station-page--create .h2-create-upload.ant-upload-wrapper .ant-upload-drag:hover { border-color: #10b981 !important; background: #f0fdf4 !important; }', '.h2-station-page--create .h2-create-upload-hint { font-size: 12px; color: #64748b; margin-top: 4px; }', '.h2-station-page .h2-supplier-mode-bar { margin-bottom: 0; }', '.h2-station-page .h2-supplier-mode-card { position: relative; display: flex; align-items: flex-start; gap: 12px; width: 100%; padding: 14px 16px; min-height: 72px; border-radius: 12px; border: 2px solid #e2e8f0; background: #fff; cursor: pointer; text-align: left; transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease; }', '.h2-station-page .h2-supplier-mode-card:hover { border-color: #86efac; background: #fafffe; }', '.h2-station-page .h2-supplier-mode-card:active { transform: scale(0.99); }', '.h2-station-page .h2-supplier-mode-card:focus-visible { outline: 2px solid #10b981; outline-offset: 2px; }', '.h2-station-page .h2-supplier-mode-card--active { border-color: #10b981; background: linear-gradient(135deg, #ecfdf5 0%, #fff 70%); box-shadow: 0 4px 16px -4px rgba(16, 185, 129, 0.25); }', '.h2-station-page .h2-supplier-mode-card-icon { flex-shrink: 0; width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: #f1f5f9; color: #64748b; transition: background 0.2s ease, color 0.2s ease; }', '.h2-station-page .h2-supplier-mode-card--active .h2-supplier-mode-card-icon { background: #d1fae5; color: #047857; }', '.h2-station-page .h2-supplier-mode-card-title { font-size: 14px; font-weight: 700; color: #0f172a; line-height: 1.3; }', '.h2-station-page .h2-supplier-mode-card-desc { font-size: 12px; color: #64748b; margin-top: 4px; line-height: 1.45; }', '.h2-station-page .h2-supplier-mode-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin-bottom: 16px; padding: 10px 14px; border-radius: 10px; background: #f8fafc; border: 1px solid #e2e8f0; }', '.h2-station-page .h2-supplier-type-tag { margin: 0 !important; border-radius: 6px !important; font-weight: 600 !important; }', '.h2-station-page .h2-supplier-link-select { margin-bottom: 16px; padding: 14px 16px; border-radius: 12px; background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 100%); border: 1px solid #bfdbfe; }', '.h2-station-page .h2-create-footer { position: fixed; bottom: 0; left: 0; right: 0; z-index: 100; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(10px); border-top: 1px solid #e2e8f0; box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.06); }', '.h2-station-page .h2-create-footer-inner { width: 100%; max-width: none; margin: 0; padding: 14px 24px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; box-sizing: border-box; }', '.h2-station-page .h2-create-footer-hint { font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 10px; min-width: 0; }', '.h2-station-page .h2-create-footer-progress { flex: 1; min-width: 120px; max-width: 200px; }', '.h2-station-page .h2-create-footer-progress .ant-progress-text { font-size: 12px !important; font-weight: 700; color: #059669 !important; }', '.h2-station-page .h2-create-footer-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }', '.h2-station-page .h2-create-footer-actions .ant-btn { min-height: 44px; padding: 0 20px; border-radius: 10px; font-weight: 600; }', '.h2-station-page .h2-create-footer-actions .ant-btn-primary { min-width: 120px; box-shadow: 0 4px 14px -2px rgba(16, 185, 129, 0.45); }', '.h2-station-page .h2-create-footer-actions .ant-btn-primary:not(:disabled):hover { box-shadow: 0 6px 18px -2px rgba(16, 185, 129, 0.5); transform: translateY(-1px); }', '.h2-station-page .h2-field-readonly .ant-input, .h2-station-page .h2-field-readonly .ant-select-selector, .h2-station-page .h2-field-readonly .ant-input-textarea { background: #f1f5f9 !important; color: #64748b !important; cursor: default !important; border-style: dashed !important; }', '@media (prefers-reduced-motion: no-preference) { .h2-station-page--create .h2-create-card { animation: h2CreateCardIn 0.35s ease-out backwards; } .h2-station-page--create .h2-create-card + .h2-create-card { animation-delay: 0.06s; } }', '@keyframes h2CreateCardIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }', '@media (prefers-reduced-motion: reduce) { .h2-station-page--create .h2-create-card { animation: none; } .h2-station-page--create .h2-signed-panel { transition: none; } }', '.h2-station-page--create .h2-create-form--list .ant-input:not(.ant-input-disabled):not(:disabled), .h2-station-page--create .h2-create-form--list .ant-input-affix-wrapper:not(.ant-input-affix-wrapper-disabled), .h2-station-page--create .h2-create-form--list .ant-select:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-cascader:not(.ant-select-disabled) .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-picker:not(.ant-picker-disabled) { border-radius: 8px !important; border: 1px solid #e2e8f0 !important; min-height: 32px !important; height: 32px !important; font-size: 13px !important; background: #fff !important; color: #0f172a !important; box-shadow: none !important; }', '.h2-station-page--create .h2-create-form--list .ant-input-textarea:not(.ant-input-disabled) { border-radius: 8px !important; border: 1px solid #e2e8f0 !important; height: auto !important; min-height: auto !important; font-size: 13px !important; }', '.h2-station-page--create .h2-create-form--list .ant-input:not(:disabled):hover, .h2-station-page--create .h2-create-form--list .ant-input-affix-wrapper:not(.ant-input-affix-wrapper-disabled):hover, .h2-station-page--create .h2-create-form--list .ant-select:not(.ant-select-disabled):hover .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-cascader:not(.ant-select-disabled):hover .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-picker:not(.ant-picker-disabled):hover { border-color: #cbd5e1 !important; }', '.h2-station-page--create .h2-create-form--list .ant-input:focus, .h2-station-page--create .h2-create-form--list .ant-input-focused, .h2-station-page--create .h2-create-form--list .ant-input-affix-wrapper-focused, .h2-station-page--create .h2-create-form--list .ant-select-focused .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-cascader-focused .ant-select-selector, .h2-station-page--create .h2-create-form--list .ant-picker-focused { border-color: #10b981 !important; box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2) !important; }', '.h2-station-page--create .h2-create-form--list .ant-form-item-label > label { font-size: 13px; font-weight: 500; color: #475569; }', '.h2-station-page--create .h2-create-form--list .ant-radio-wrapper .ant-radio-checked .ant-radio-inner { border-color: #10b981; background-color: #10b981; }', '.h2-station-page--create .h2-create-pagehead { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; padding: 12px 20px; background: #fff; border-radius: 16px; border: 1px solid #e2e8f0; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.03); }', '.h2-station-page--create .h2-create-card.ant-card { border-radius: 16px !important; }', '.h2-station-page .h2-card-title-bar--step::before { display: none !important; }', '.h2-station-page .h2-card-step-badge { width: 28px; height: 28px; border-radius: 50%; background: linear-gradient(145deg, #10b981 0%, #059669 100%); color: #fff; font-size: 14px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; box-shadow: 0 2px 8px rgba(16, 185, 129, 0.32); }', '.h2-station-page .h2-card-title-text { font-size: 15px; font-weight: 700; color: #0f172a; }', '.h2-station-page--create .h2-create-form > .h2-create-card + .h2-create-card, .h2-station-page--edit .h2-create-form > .h2-create-card + .h2-create-card { margin-top: 16px; }', '.h2-station-page--create .h2-create-footer-inner, .h2-station-page--edit .h2-create-footer-inner { justify-content: flex-end; }' ]).join('\n'); var H2_CURRENT_OPERATOR = '系统管理员'; /** 原型登录角色:admin 平台管理员可改系统账号;station 加氢站账号不可改 */ var H2_CURRENT_USER_ROLE = 'admin'; function h2IsAdminUser() { if (typeof window !== 'undefined' && window.H2_STATION_PAGE_USER_ROLE) { return String(window.H2_STATION_PAGE_USER_ROLE).toLowerCase() === 'admin'; } return H2_CURRENT_USER_ROLE === 'admin'; } function h2OperateTimestamp() { return new Date().toISOString().slice(0, 16).replace('T', ' '); } function h2CreateBusinessStatusLog(beforeStatus, afterStatus, operator) { return { id: 'bsl-' + Date.now() + '-' + Math.floor(Math.random() * 1000), operateTime: h2OperateTimestamp(), operator: operator || H2_CURRENT_OPERATOR, beforeStatus: beforeStatus || '—', afterStatus: afterStatus || '—' }; } function h2CreateCostPriceLog(beforePrice, afterPrice, effectiveTime, operator) { return { id: 'cpl-' + Date.now() + '-' + Math.floor(Math.random() * 1000), operator: operator || H2_CURRENT_OPERATOR, operateTime: h2OperateTimestamp(), beforeCostPrice: beforePrice, afterCostPrice: afterPrice, effectiveTime: effectiveTime }; } function h2ParseDateTimeMs(val) { if (!val) return NaN; var s = String(val).trim(); if (!s) return NaN; return new Date(s.indexOf('T') >= 0 ? s : s.replace(' ', 'T')).getTime(); } function h2ToDateTimeDayjs(text) { var dayjs = window.dayjs; if (!dayjs || !text) return null; var s = String(text).trim(); if (!s) return null; var d = dayjs(s.indexOf('T') >= 0 ? s : s.replace(' ', 'T')); if (!(d.isValid && d.isValid())) d = dayjs(s); return (d.isValid && d.isValid()) ? d : null; } function h2ToDateDayjs(text) { var dayjs = window.dayjs; if (!dayjs || !text) return null; var s = String(text).trim(); if (!s) return null; var d = dayjs(s); return (d.isValid && d.isValid()) ? d : null; } function h2SanitizeReceiptAmountInput(text) { var s = String(text || '').replace(/[^\d.]/g, ''); var parts = s.split('.'); if (parts.length > 2) s = parts[0] + '.' + parts.slice(1).join(''); var dot = s.indexOf('.'); if (dot >= 0 && s.length - dot - 1 > 2) s = s.slice(0, dot + 3); return s; } function h2FormatReceiptAmountInput(text) { var s = String(text || '').trim(); if (!s) return ''; var n = parseFloat(s); if (isNaN(n) || n < 0) return ''; return n.toFixed(2); } function h2ResolveCurrentCostPrice(record) { var logs = (record && record.costPriceLogs) || []; var now = Date.now(); var best = null; var i; for (i = 0; i < logs.length; i++) { var log = logs[i]; var t = h2ParseDateTimeMs(log.effectiveTime); if (isNaN(t) || t > now) continue; if (!best || t >= best.t) { best = { t: t, price: log.afterCostPrice }; } } if (best && best.price != null) return best.price; return record && record.costUnitPrice != null ? record.costUnitPrice : null; } function h2ApplyDueCostPriceToRecord(record) { var resolved = h2ResolveCurrentCostPrice(record); if (resolved == null) return record; if (record.costUnitPrice === resolved) return record; return Object.assign({}, record, { costUnitPrice: resolved }); } function h2ApplyDueCostPricesToList(records) { var changed = false; var next = records.map(function (r) { var updated = h2ApplyDueCostPriceToRecord(r); if (updated !== r) changed = true; return updated; }); return { records: next, changed: changed }; } function h2FormatCostPriceDisplay(v) { if (v == null || v === '') return '—'; return h2FormatYuanNum(v) + ' 元/kg'; } function h2CountPendingCostPriceLogs(logs) { var now = Date.now(); var count = 0; var i; for (i = 0; i < (logs || []).length; i++) { var t = h2ParseDateTimeMs(logs[i].effectiveTime); if (!isNaN(t) && t > now) count += 1; } return count; } var H2_IMPORT_TEMPLATE_HEADERS = [ '加氢站名称', '省', '市', '详细地址', '是否签约', '签约开始时间', '签约结束时间', '营业状态', '营业时间', '联系人', '联系电话' ]; var H2_IMPORT_TEMPLATE_SAMPLE = [ ['苏州新区加氢站(导入示例)', '江苏省', '苏州市', '工业园区星湖街100号', '是', '2025-01-01', '2027-12-31', '营业中', '08:00-20:00', '周八', '13900001111'] ]; var H2_IMPORT_HEADER_MAP = { '加氢站名称': 'name', '站点名称': 'name', '名称': 'name', '省': 'province', '省份': 'province', '市': 'city', '城市': 'city', '详细地址': 'detail', '地址详情': 'detail', '是否签约': 'isSigned', '签约状态': 'isSigned', '签约开始时间': 'contractStart', '签约开始': 'contractStart', '签约结束时间': 'contractEnd', '签约结束': 'contractEnd', '营业状态': 'businessStatus', '营业时间': 'businessHours', '联系人': 'contact', '联系电话': 'phone', '电话': 'phone' }; function h2CsvEscape(v) { var s = String(v == null ? '' : v); if (/[",\n\r]/.test(s)) return '"' + s.replace(/"/g, '""') + '"'; return s; } function h2DownloadCsv(filename, headers, rows) { var lines = [headers.map(h2CsvEscape).join(',')].concat( (rows || []).map(function (r) { return r.map(h2CsvEscape).join(','); }) ); var blob = new Blob(['\uFEFF' + lines.join('\n')], { type: 'text/csv;charset=utf-8;' }); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; a.click(); URL.revokeObjectURL(a.href); } function h2ParseCsvLine(line) { var out = []; var cur = ''; var inQuote = false; var i; for (i = 0; i < line.length; i++) { var ch = line.charAt(i); if (inQuote) { if (ch === '"') { if (line.charAt(i + 1) === '"') { cur += '"'; i++; } else inQuote = false; } else cur += ch; } else if (ch === '"') inQuote = true; else if (ch === ',') { out.push(cur.trim()); cur = ''; } else cur += ch; } out.push(cur.trim()); return out; } function h2ParseSignedLabel(val) { var s = String(val || '').trim(); if (!s) return false; var lower = s.toLowerCase(); if (s === '是' || s === '已签约' || lower === 'yes' || lower === 'y' || lower === 'true' || lower === '1') return true; if (s === '否' || s === '未签约' || lower === 'no' || lower === 'n' || lower === 'false' || lower === '0') return false; return false; } function h2NormalizeBusinessStatus(val) { var s = String(val || '').trim(); var i; for (i = 0; i < H2_BUSINESS_STATUS_OPTIONS.length; i++) { if (H2_BUSINESS_STATUS_OPTIONS[i].value === s) return s; } if (s.indexOf('暂停') >= 0) return '暂停营业'; if (s.indexOf('停止') >= 0 || s.indexOf('停业') >= 0) return '停止营业'; if (s.indexOf('营业') >= 0) return '营业中'; return ''; } function h2ParseImportCsv(text) { var raw = String(text || '').replace(/^\uFEFF/, ''); var lines = raw.split(/\r?\n/).filter(function (ln) { return ln.trim().length > 0; }); if (lines.length < 2) return { rows: [], headerError: '文件至少需要表头行与一行数据' }; var headers = h2ParseCsvLine(lines[0]).map(function (h) { return String(h || '').trim().replace(/^\uFEFF/, ''); }); var fieldIdx = {}; var hi; for (hi = 0; hi < headers.length; hi++) { var key = H2_IMPORT_HEADER_MAP[headers[hi]]; if (key && fieldIdx[key] == null) fieldIdx[key] = hi; } if (fieldIdx.name == null) return { rows: [], headerError: '缺少必填列「加氢站名称」' }; var rows = []; var li; for (li = 1; li < lines.length; li++) { var cells = h2ParseCsvLine(lines[li]); if (!cells.some(function (c) { return String(c || '').trim(); })) continue; var get = function (field) { var idx = fieldIdx[field]; return idx == null || idx < 0 ? '' : String(cells[idx] == null ? '' : cells[idx]).trim(); }; rows.push({ lineNo: li + 1, name: get('name'), province: get('province'), city: get('city'), detail: get('detail'), isSigned: h2ParseSignedLabel(get('isSigned')), contractStart: get('contractStart'), contractEnd: get('contractEnd'), businessStatus: h2NormalizeBusinessStatus(get('businessStatus')), businessHours: get('businessHours'), contact: get('contact'), phone: get('phone') }); } return { rows: rows, headerError: null }; } function h2ValidateImportRow(row, existingNames) { var errors = []; if (!(row.name || '').trim()) errors.push('加氢站名称不能为空'); else if (existingNames[(row.name || '').trim()]) errors.push('站点名称「' + row.name + '」已存在'); if (!(row.province || '').trim()) errors.push('省不能为空'); if (!(row.city || '').trim()) errors.push('市不能为空'); if (!(row.detail || '').trim()) errors.push('详细地址不能为空'); if (!(row.contact || '').trim()) errors.push('联系人不能为空'); if (!(row.phone || '').trim()) errors.push('联系电话不能为空'); if (!(row.businessStatus || '').trim()) errors.push('营业状态无效,请填写:营业中 / 暂停营业 / 停止营业'); if (row.isSigned) { if (!row.contractStart) errors.push('已签约站点需填写签约开始时间'); if (!row.contractEnd) errors.push('已签约站点需填写签约结束时间'); } return errors; } function h2ImportRowToRecord(row, id) { var region = [row.province, row.city]; var form = { name: row.name, address: { region: region, detail: row.detail }, isSigned: row.isSigned, contractStart: row.isSigned ? (row.contractStart || '') : '', contractEnd: row.isSigned ? (row.contractEnd || '') : '', contractFiles: [], businessStatus: row.businessStatus || '营业中', businessHours: row.businessHours || '', contact: row.contact, phone: row.phone }; var record = h2FormToRecord(form, id); record.businessStatusLogs = []; return record; } function h2SvgIcon(paths, size) { var s = size || 18; return React.createElement('svg', { width: s, height: s, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true }, paths.map(function (p, i) { if (p.tag === 'circle') return React.createElement('circle', { key: i, cx: p.cx, cy: p.cy, r: p.r }); if (p.tag === 'line') return React.createElement('line', { key: i, x1: p.x1, y1: p.y1, x2: p.x2, y2: p.y2 }); if (p.tag === 'rect') return React.createElement('rect', { key: i, x: p.x, y: p.y, width: p.width, height: p.height, rx: p.rx }); return React.createElement('path', { key: i, d: p.d }); })); } var H2_ICONS = { station: h2SvgIcon([{ d: 'M3 21h18M5 21V7l8-4v18M19 21V11l-6-4' }]), high: h2SvgIcon([{ d: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z' }], 16), low: h2SvgIcon([{ d: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z' }], 16), none: h2SvgIcon([{ d: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z' }, { tag: 'line', x1: 12, y1: 9, x2: 12, y2: 13 }, { tag: 'line', x1: 12, y1: 17, x2: 12.01, y2: 17 }], 16), doc: h2SvgIcon([{ d: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z' }, { d: 'M14 2v6h6' }, { tag: 'line', x1: 16, y1: 13, x2: 8, y2: 13 }, { tag: 'line', x1: 16, y1: 17, x2: 8, y2: 17 }], 14), upload: h2SvgIcon([{ d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }, { d: 'M17 8l-5-5-5 5' }, { tag: 'line', x1: 12, y1: 3, x2: 12, y2: 15 }], 14), download: h2SvgIcon([{ d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }, { d: 'M7 10l5 5 5-5' }, { tag: 'line', x1: 12, y1: 15, x2: 12, y2: 3 }], 14), empty: h2SvgIcon([{ tag: 'circle', cx: 12, cy: 12, r: 10 }, { tag: 'line', x1: 8, y1: 12, x2: 16, y2: 12 }], 40), back: h2SvgIcon([{ tag: 'line', x1: 19, y1: 12, x2: 5, y2: 12 }, { d: 'M12 19l-7-7 7-7' }], 16), mapPin: h2SvgIcon([{ tag: 'path', d: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z' }, { tag: 'circle', cx: 12, cy: 10, r: 3 }], 16), user: h2SvgIcon([{ tag: 'path', d: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2' }, { tag: 'circle', cx: 12, cy: 7, r: 4 }], 16), building: h2SvgIcon([{ d: 'M3 21h18M5 21V7l8-4v18M19 21V11l-6-4' }], 18), truck: h2SvgIcon([{ d: 'M1 3h15v13H1zM16 8h4l3 3v5h-7V8z' }, { tag: 'circle', cx: 5.5, cy: 18.5, r: 2.5 }, { tag: 'circle', cx: 18.5, cy: 18.5, r: 2.5 }], 16), link: h2SvgIcon([{ tag: 'path', d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }, { tag: 'path', d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }], 16), plus: h2SvgIcon([{ tag: 'line', x1: 12, y1: 5, x2: 12, y2: 19 }, { tag: 'line', x1: 5, y1: 12, x2: 19, y2: 12 }], 16), file: h2SvgIcon([{ d: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z' }, { d: 'M14 2v6h6' }], 16) }; function h2MoreIcon() { return React.createElement('svg', { viewBox: '0 0 16 16', width: 16, height: 16, fill: 'currentColor', 'aria-hidden': true }, React.createElement('circle', { cx: 8, cy: 3, r: 1.5 }), React.createElement('circle', { cx: 8, cy: 8, r: 1.5 }), React.createElement('circle', { cx: 8, cy: 13, r: 1.5 }) ); } function h2DeriveFrequencyByRefuelCount(refuelCount) { var n = typeof refuelCount === 'number' ? refuelCount : parseInt(refuelCount, 10); if (!n || n <= 0) return 'none'; if (n >= 3) return 'high'; return 'low'; } function h2ResolveBalanceAlertThreshold(record) { if (!record || record.balanceAlertThreshold == null || record.balanceAlertThreshold === '') return null; var n = parseFloat(record.balanceAlertThreshold); return isNaN(n) || n <= 0 ? null : n; } function h2IsArrearsStation(record) { return h2NumOrZero(record && record.prepaidBalance) < 0; } function h2IsBalanceAlertStation(record) { if (h2IsArrearsStation(record)) return false; var threshold = h2ResolveBalanceAlertThreshold(record); if (threshold == null) return false; return h2NumOrZero(record.prepaidBalance) < threshold; } function h2CreateRechargeLineId() { return 'rcl-' + Date.now() + '-' + Math.floor(Math.random() * 10000); } function h2BuildRechargeTransferPurpose(date) { var d = date || new Date(); var y = d.getFullYear(); var m = String(d.getMonth() + 1).padStart(2, '0'); var day = String(d.getDate()).padStart(2, '0'); return '羚牛预付氢费款' + y + '-' + m + '-' + day; } function h2CreateEmptyRechargeLine() { return { id: h2CreateRechargeLineId(), stationId: undefined, stationName: '', currentBalance: '', payAmount: '', companyName: '', bankAccount: '', bankName: '', transferPurpose: h2BuildRechargeTransferPurpose() }; } function h2BuildRechargeLineFromStation(record, payAmount) { var supplier = h2ResolveStationSupplier(record); return { id: h2CreateRechargeLineId(), stationId: record.id, stationName: record.name || '', currentBalance: h2FormatYuanNum(record.prepaidBalance), payAmount: payAmount != null && payAmount !== '' ? payAmount : '', companyName: supplier ? (supplier.name || '') : '', bankAccount: supplier ? (supplier.bankAccount || '') : '', bankName: supplier ? (supplier.bankName || '') : '', transferPurpose: h2BuildRechargeTransferPurpose() }; } function h2EnrichStationListRows(records) { return (records || []).map(function (r) { var stats = h2CalcRefuelStats(r.name); return Object.assign({}, r, { refuelCount: stats.count, refuelTotalKg: stats.totalKg, refuelFreqKey: h2DeriveFrequencyByRefuelCount(stats.count) }); }); } function h2RenderRechargeAutoField(value, hasStation, placeholder) { if (!hasStation) { return React.createElement('span', { className: 'h2-recharge-readonly h2-recharge-readonly--muted' }, '—'); } var text = value != null && value !== '' ? String(value) : ''; if (!text) { return React.createElement('span', { className: 'h2-recharge-readonly h2-recharge-readonly--muted' }, placeholder || '—'); } var InputComp = window.antd && window.antd.Input; if (!InputComp) { return React.createElement('span', { className: 'h2-recharge-readonly' }, text); } return React.createElement(InputComp, { className: 'h2-recharge-readonly-input', value: text, disabled: true, style: { width: '100%', borderRadius: 8 } }); } function h2FormatRegion(region) { if (!region || !region.length) return '—'; return region.join('-'); } function h2FormatStationAddressLine(record) { var regionText = h2FormatRegion(record && record.region); var detail = String((record && record.addressDetail) || '').trim(); if (regionText === '—' && !detail) return '—'; if (regionText === '—') return detail; if (!detail) return regionText; return regionText + ' ' + detail; } function h2MatchRegionFilter(recordRegion, filterRegion) { if (!filterRegion || !filterRegion.length) return true; var rr = recordRegion || []; if (filterRegion[0] && rr[0] !== filterRegion[0]) return false; if (filterRegion[1] && rr[1] !== filterRegion[1]) return false; return true; } function h2EmptyListFilters() { return { name: '', signed: undefined, region: undefined, businessStatus: undefined }; } /** 签约站点是否已上传合同附件 */ function h2HasUploadedContract(record) { var files = record && record.contractFiles; if (!files || !files.length) return false; var i; for (i = 0; i < files.length; i++) { var f = files[i]; if (!f) continue; if ((f.name || '').trim()) return true; if (f.url || (f.response && (f.response.url || f.response.fileUrl))) return true; } return false; } /** 列表签约站点标签文案与样式 */ function h2RenderStationSignedTag(record) { var Tag = window.antd && window.antd.Tag; if (!Tag || !record || !record.isSigned) return null; var uploaded = h2HasUploadedContract(record); return React.createElement(Tag, { color: uploaded ? 'success' : 'warning', className: 'lc-station-signed-tag' }, uploaded ? '签约站点' : '签约站点-未上传合同'); } /** 距签约结束日天数:正=剩余,负=已过期 */ function h2DaysUntilContractEnd(dateStr) { if (!dateStr) return null; var parts = String(dateStr).trim().split('-'); if (parts.length < 3) return null; var y = parseInt(parts[0], 10); var m = parseInt(parts[1], 10) - 1; var d = parseInt(parts[2], 10); if (isNaN(y) || isNaN(m) || isNaN(d)) return null; var end = new Date(y, m, d, 23, 59, 59, 999); var today = new Date(); today.setHours(0, 0, 0, 0); return Math.ceil((end.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); } function h2RenderContractRemainTag(contractEnd) { var days = h2DaysUntilContractEnd(contractEnd); if (days == null) return null; var Tag = window.antd && window.antd.Tag; if (!Tag) return null; var tagProps = { style: { marginTop: 4, borderRadius: 6, fontWeight: 600, fontSize: 11 } }; if (days < 0) { return React.createElement(Tag, Object.assign({}, tagProps, { color: 'error', 'aria-label': '已过期 ' + Math.abs(days) + ' 天' }), '已过期 ' + Math.abs(days) + ' 天'); } if (days <= 30) { return React.createElement(Tag, Object.assign({}, tagProps, { color: 'warning', 'aria-label': '剩余 ' + days + ' 天' }), '剩余 ' + days + ' 天'); } return React.createElement(Tag, Object.assign({}, tagProps, { color: 'success', 'aria-label': '剩余 ' + days + ' 天' }), '剩余 ' + days + ' 天'); } function h2BuildFullAddress(region, detail) { var base = h2FormatRegion(region); if (!detail) return base === '—' ? '' : base; return base === '—' ? detail : base + detail; } function h2EmptyAddressValue() { return { region: [], detail: '' }; } /** 从完整地址文本解析省/市与详细地址(基于站点省市区选项) */ function h2ParseAddressText(rawText) { var text = String(rawText || '').trim(); if (!text) return { region: [], detail: '', matched: false }; var compact = text.replace(/\s+/g, ''); var provinces = H2_REGION_CASCADER_OPTIONS.slice().sort(function (a, b) { return String(b.value || b.label || '').length - String(a.value || a.label || '').length; }); var matchedProvince = null; var provinceStart = -1; var matchedProvinceLen = 0; for (var i = 0; i < provinces.length; i++) { var pName = provinces[i].value || provinces[i].label; var aliases = [pName]; if (pName.slice(-1) === '省') aliases.push(pName.slice(0, -1)); if (pName.slice(-1) === '市') aliases.push(pName.replace(/市$/, '')); var ai; for (ai = 0; ai < aliases.length; ai++) { var alias = aliases[ai]; var idx = compact.indexOf(alias); if (idx !== -1 && (provinceStart === -1 || idx < provinceStart || (idx === provinceStart && alias.length > matchedProvinceLen))) { provinceStart = idx; matchedProvince = provinces[i]; matchedProvinceLen = alias.length; } } } if (!matchedProvince) { return { region: [], detail: text, matched: false }; } var provinceName = matchedProvince.value || matchedProvince.label; var rest = compact.slice(provinceStart + matchedProvinceLen).replace(/^[\s\-—,,、·]+/, ''); var cities = matchedProvince.children || []; var cityMatch = h2FindCityInAddressRest(rest, cities); if (!cityMatch && cities.length === 1) { return { region: [provinceName, cities[0].value || cities[0].label], detail: rest.replace(/^[\s\-—,,、·]+/, ''), matched: true }; } if (!cityMatch) { return { region: [provinceName], detail: rest || text, matched: true, partial: true }; } var cityName = cityMatch.city.value || cityMatch.city.label; var detail = rest.slice(cityMatch.end); detail = detail.replace(/^[\s\-—,,、·省市区县]+/, ''); if (!detail && cityMatch.prefix) detail = cityMatch.prefix.replace(/^[\s\-—,,、·省市区县]+/, ''); return { region: [provinceName, cityName], detail: detail, matched: true }; } function h2StripRegionPrefixFromDetail(region, detail) { var text = String(detail || '').trim(); if (!text || !region || region.length < 2) return text; var cityName = region[1] || ''; var cityPlain = cityName.replace(/市$/, ''); if (cityName && text.indexOf(cityName) === 0) text = text.slice(cityName.length); else if (cityPlain && text.indexOf(cityPlain) === 0) text = text.slice(cityPlain.length); return text.replace(/^[\s\-—,,、·省市区县]+/, ''); } function h2FindCityInAddressRest(rest, cities) { if (!rest || !cities || !cities.length) return null; var sorted = cities.slice().sort(function (a, b) { return String(b.value || b.label || '').length - String(a.value || a.label || '').length; }); var i; for (i = 0; i < sorted.length; i++) { var cn = sorted[i].value || sorted[i].label; if (rest.indexOf(cn) === 0) return { city: sorted[i], prefix: '', end: cn.length }; } for (i = 0; i < sorted.length; i++) { var cn2 = sorted[i].value || sorted[i].label; var idx = rest.indexOf(cn2); if (idx > 0 && idx <= 8) return { city: sorted[i], prefix: rest.slice(0, idx), end: idx + cn2.length }; } return null; } /** 地址解析:失焦后自动识别并回填省/市与详细地址 */ function AddressPasteInput(props) { var antd = window.antd; var Input = antd.Input; var message = antd.message; var _paste = React.useState(''); var pasteText = _paste[0]; var setPasteText = _paste[1]; var disabled = props.disabled; var inputClassName = props.inputClassName || ''; var runParse = function (raw) { raw = (raw || '').trim(); if (!raw) return; var result = h2ParseAddressText(raw); if (!result.matched) { message.warning('未能识别省/市,请将内容填入详细地址'); if (props.onParsed) props.onParsed({ region: [], detail: raw }); return; } if (result.partial || !result.region || result.region.length < 2) { message.warning('已识别省份,请补全城市信息'); } var region = result.region || []; var detail = h2StripRegionPrefixFromDetail(region, result.detail || ''); if (props.onParsed) props.onParsed({ region: region, detail: detail }); }; return React.createElement('div', { className: 'h2-address-paste' }, React.createElement(Input.TextArea, { className: inputClassName, value: pasteText, disabled: disabled, placeholder: props.placeholder || '填写完整地址自动解析省市和详细地址', autoSize: { minRows: 2, maxRows: 4 }, onChange: function (e) { setPasteText(e.target.value); }, onBlur: function () { if (disabled) return; runParse(pasteText); } }) ); } var H2_BUSINESS_LICENSE_OCR_MOCKS = [ { test: function (name) { return /嘉兴|南湖|氢能供应/i.test(name || ''); }, name: '嘉兴氢能供应有限公司', taxId: '91330400MA2ABCDEF1', communicationAddress: '浙江省嘉兴市南湖区科技大道66号' }, { test: function (name) { return /临平|杭州/i.test(name || ''); }, name: '杭州临平氢能运营有限公司', taxId: '91330110MA3LINPING1', communicationAddress: '浙江省杭州市临平区东湖街道能源路16号' }, { test: function (name) { return /羚牛|宝山|上海/i.test(name || ''); }, name: '上海羚牛氢能科技有限公司', taxId: '91310100MA1XYZ7890', communicationAddress: '上海市宝山区富联路200号' }, { test: function (name) { return /苏州|星湖/i.test(name || ''); }, name: '苏州工业园区氢源科技有限公司', taxId: '91320500MA1ABCDEF2', communicationAddress: '江苏省苏州市工业园区星湖街328号' }, { test: function () { return true; }, name: '浙江羚牛氢能科技有限公司', taxId: '91330481MA2JXXXXXX', communicationAddress: '浙江省嘉兴市平湖市乍浦镇杭州湾大道1号' } ]; function h2MatchBusinessLicenseOcrMock(file) { var fileName = (file && file.name) || ''; var i; for (i = 0; i < H2_BUSINESS_LICENSE_OCR_MOCKS.length; i++) { if (H2_BUSINESS_LICENSE_OCR_MOCKS[i].test(fileName)) { return H2_BUSINESS_LICENSE_OCR_MOCKS[i]; } } return H2_BUSINESS_LICENSE_OCR_MOCKS[H2_BUSINESS_LICENSE_OCR_MOCKS.length - 1]; } function h2ApplyBusinessLicenseOcr(ctx, ocrData) { if (!ocrData || !ctx || !ctx.updateSupplier) return; var commAddr = String(ocrData.communicationAddress || '').trim(); var parsed = h2ParseAddressText(commAddr); var region = parsed.region || []; var detail = commAddr ? h2StripRegionPrefixFromDetail(region, parsed.detail || commAddr) : ''; var supplierPatch = { name: (ocrData.name || '').trim(), taxId: (ocrData.taxId || '').trim(), address: detail, mailingAddress: commAddr || detail, businessAddress: commAddr || detail }; if (region.length >= 2) { supplierPatch.city = region.slice(); supplierPatch.region = h2GetRegionByProvince(region[0]); } ctx.updateSupplier(supplierPatch); if (ctx.updateStation && ctx.supplierMode === 'new' && region.length >= 2) { ctx.updateStation({ address: { region: region.slice(), detail: detail } }); } } function h2TriggerBusinessLicenseOcr(file, ctx) { var antd = window.antd; var message = antd && antd.message; var hide = message && message.loading('营业执照识别中…', 0); window.setTimeout(function () { if (hide) hide(); var ocrData = h2MatchBusinessLicenseOcrMock(file); h2ApplyBusinessLicenseOcr(ctx, ocrData); if (message) { message.success('识别完成:已填写供应商名称、纳税人识别号与通讯地址(未写入注册地址)'); } }, 720); } function h2KpiCardIcon(key) { var size = 22; if (key === 'all') { return React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.75, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true }, React.createElement('path', { d: 'M4 20h16' }), React.createElement('path', { d: 'M7 20v-9h4v9' }), React.createElement('path', { d: 'M14 20V8l5-3v15' }), React.createElement('text', { x: 8.5, y: 13.5, fill: 'currentColor', stroke: 'none', fontSize: 5.5, fontWeight: 700, fontFamily: 'system-ui, -apple-system, sans-serif' }, 'H₂') ); } if (key === 'yes') { return h2SvgIcon([ { d: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z' }, { d: 'M14 2v6h6' }, { tag: 'circle', cx: 17.5, cy: 17.5, r: 3.5 }, { d: 'M16 17.5l1 1 2-2' } ], size); } if (key === 'no') { return h2SvgIcon([ { d: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z' }, { d: 'M14 2v6h6' }, { tag: 'circle', cx: 17.5, cy: 17.5, r: 3.5 }, { tag: 'line', x1: 16, y1: 16, x2: 19, y2: 19 }, { tag: 'line', x1: 19, y1: 16, x2: 16, y2: 19 } ], size); } if (key === 'balanceAlert') { return h2SvgIcon([ { d: 'M12 2v4' }, { d: 'M12 18v4' }, { d: 'M4.93 4.93l2.83 2.83' }, { d: 'M16.24 16.24l2.83 2.83' }, { d: 'M2 12h4' }, { d: 'M18 12h4' }, { d: 'M4.93 19.07l2.83-2.83' }, { d: 'M16.24 7.76l2.83-2.83' }, { tag: 'circle', cx: 12, cy: 12, r: 4 } ], size); } if (key === 'arrears') { return h2SvgIcon([ { d: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z' }, { tag: 'line', x1: 12, y1: 9, x2: 12, y2: 13 }, { tag: 'line', x1: 12, y1: 17, x2: 12.01, y2: 17 } ], size); } if (key === 'none') { return h2SvgIcon([ { d: 'M6 20h12' }, { d: 'M9 20v-6h2c1.2 0 2.2 1 2.2 2.2S12.2 18.4 11 18.4H9' }, { tag: 'circle', cx: 12, cy: 11, r: 7.5 }, { tag: 'line', x1: 6.8, y1: 6.8, x2: 17.2, y2: 17.2 } ], size); } return h2KpiCardIcon('all'); } function h2KpiIcon(key) { return h2KpiCardIcon(key); } function h2SignedFilterIcon(key) { return h2KpiCardIcon(key === 'yes' ? 'yes' : 'no'); } /** 地址组件:省/市级联 + 详细地址(无子标题,挂在「地址」表单项下) */ function RegionAddressInput(props) { var antd = window.antd; var Cascader = antd.Cascader; var Input = antd.Input; var value = props.value || h2EmptyAddressValue(); var onChange = props.onChange; var disabled = props.disabled; var inputClassName = props.inputClassName || ''; var layout = props.layout || 'stack'; var detailPlaceholder = props.detailPlaceholder || '请输入街道、门牌号等详细地址'; var regionPlaceholder = props.regionPlaceholder || '请选择省 / 市'; var fieldStyle = props.fieldStyle || { width: '100%', borderRadius: 8 }; var rootCls = 'h2-region-address' + (layout === 'inline' ? ' h2-region-address--inline' : '') + (props.className ? ' ' + props.className : ''); return React.createElement('div', { className: rootCls }, React.createElement(Cascader, { className: inputClassName + (layout === 'inline' ? ' h2-region-address-cascader' : ''), options: H2_REGION_CASCADER_OPTIONS, value: value.region && value.region.length ? value.region : undefined, onChange: function (v) { onChange && onChange({ region: v || [], detail: value.detail || '' }); }, placeholder: regionPlaceholder, style: layout === 'inline' ? { width: '100%', borderRadius: fieldStyle.borderRadius || 8 } : fieldStyle, disabled: disabled, allowClear: !disabled }), React.createElement(Input, { className: inputClassName + (layout === 'inline' ? ' h2-region-address-detail' : ''), value: value.detail || '', onChange: function (e) { onChange && onChange({ region: value.region || [], detail: e.target.value }); }, placeholder: detailPlaceholder, disabled: disabled, maxLength: 200, style: layout === 'inline' ? { width: '100%', borderRadius: fieldStyle.borderRadius || 8 } : fieldStyle }) ); } /** 供应商通讯地址:省/市 + 详细地址(横向嵌入) */ function SupplierCityAddressInput(props) { var antd = window.antd; var Cascader = antd.Cascader; var Input = antd.Input; var city = props.city; var address = props.address || ''; var onChange = props.onChange; var disabled = props.disabled; var inputClassName = props.inputClassName || ''; var onCityChange = props.onCityChange; var layout = props.layout || 'inline'; var rootCls = 'h2-region-address' + (layout === 'inline' ? ' h2-region-address--inline' : '') + (props.className ? ' ' + props.className : ''); return React.createElement('div', { className: rootCls }, React.createElement(Cascader, { className: inputClassName + (layout === 'inline' ? ' h2-region-address-cascader' : ''), options: H2_REGION_CASCADER_OPTIONS, value: city && city.length ? city : undefined, onChange: onCityChange, placeholder: '请选择省 / 市', style: { width: '100%' }, disabled: disabled, allowClear: !disabled }), React.createElement(Input, { className: inputClassName + (layout === 'inline' ? ' h2-region-address-detail' : ''), value: address, disabled: disabled, placeholder: '请输入详细地址', maxLength: 200, style: { width: '100%' }, onChange: function (e) { onChange && onChange(e.target.value); } }) ); } function h2GetUploadFileUrl(file) { if (!file) return ''; if (file.url) return file.url; if (file.thumbUrl) return file.thumbUrl; if (file.originFileObj && typeof URL !== 'undefined' && URL.createObjectURL) { try { return URL.createObjectURL(file.originFileObj); } catch (e) { return ''; } } return ''; } function h2DownloadUploadFile(file) { var url = h2GetUploadFileUrl(file); if (!url) return; var a = document.createElement('a'); a.href = url; a.download = file.name || '附件'; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } function h2IsPdfFileName(name) { return /\.pdf$/i.test(String(name || '')); } function h2EscapeSvgText(text) { return String(text || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function h2BuildPrototypeDocPreviewSvg(title, variant) { var accent = variant === 'license' ? '#10b981' : '#3b82f6'; var heading = variant === 'license' ? '证照预览' : '附件预览'; var label = h2EscapeSvgText(String(title || '附件').slice(0, 28)); var svg = '' + '' + '' + '' + '' + '' + heading + '' + '' + label + '' + ''; return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg); } function h2ResolveLicenseDisplayUrl(file) { var url = h2GetUploadFileUrl(file); if (url) return url; return h2BuildPrototypeDocPreviewSvg(file && file.name, 'license'); } function h2ResolveUploadPreview(file, variant) { var name = (file && file.name) || '附件'; var url = h2GetUploadFileUrl(file); var isPdf = h2IsPdfFileName(name); if (url && isPdf) { return { url: url, name: name, type: 'pdf' }; } if (url) { return { url: url, name: name, type: 'image' }; } return { url: h2BuildPrototypeDocPreviewSvg(name, variant || 'doc'), name: name, type: 'image' }; } /** 合同附件:按钮上传 + 预览/下载 */ function ContractFilesUpload(props) { var antd = window.antd; var Upload = antd.Upload; var Button = antd.Button; var fileList = props.fileList || []; var disabled = props.disabled; var showHint = props.showHint !== false; var wrapClass = 'h2-contract-upload' + (props.wrapClassName ? ' ' + props.wrapClassName : ''); var uploadClass = props.uploadClassName || 'h2-create-upload-btn'; return React.createElement('div', { className: wrapClass }, React.createElement('div', { className: 'h2-contract-upload-actions' }, React.createElement(Upload, { className: uploadClass, multiple: true, disabled: disabled, fileList: fileList, beforeUpload: function () { return false; }, onChange: props.onChange, showUploadList: false }, React.createElement(Button, { type: props.buttonType || 'default', icon: H2_ICONS.upload, disabled: disabled, className: props.buttonClassName, style: props.buttonStyle }, props.uploadLabel || '上传附件') ), showHint ? React.createElement('span', { style: { fontSize: 12, color: '#94a3b8' } }, '点击上传,可多选') : null ), fileList.length ? React.createElement('div', { className: 'h2-contract-file-list' }, fileList.map(function (file) { return React.createElement('div', { key: file.uid, className: 'h2-contract-file-item' }, React.createElement('span', { className: 'h2-contract-file-name', title: file.name }, file.name || '未命名附件'), React.createElement('div', { className: 'h2-contract-file-btns' }, React.createElement(Button, { type: 'link', size: 'small', disabled: disabled, onClick: function () { var url = h2GetUploadFileUrl(file); if (url) window.open(url, '_blank'); } }, '预览'), React.createElement(Button, { type: 'link', size: 'small', disabled: disabled, onClick: function () { h2DownloadUploadFile(file); } }, '下载'), !disabled ? React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { if (!props.onChange) return; props.onChange({ fileList: fileList.filter(function (f) { return f.uid !== file.uid; }) }); } }, '删除') : null ) ); }) ) : null ); } function h2StationContactPhone(station) { return (station.mobilePhone || '').trim() || (station.landlinePhone || '').trim(); } function h2FilterContactPhoneInput(raw) { var v = String(raw || '').replace(/[^\d-]/g, ''); var digits = v.replace(/-/g, ''); if (!digits) return ''; if (digits.charAt(0) === '1') return digits.slice(0, 11); var parts = v.split('-'); if (parts.length > 2) v = parts[0] + '-' + parts.slice(1).join(''); return v.slice(0, 16); } function h2IsMobilePhone(text) { var digits = String(text || '').replace(/[\s-]/g, ''); return /^1[3-9]\d{9}$/.test(digits); } function h2IsLandlinePhone(text) { var raw = String(text || '').trim(); if (!raw) return false; var compact = raw.replace(/[\s-]/g, ''); if (/^0\d{2,3}\d{7,8}$/.test(compact)) return true; if (/^0\d{2,3}-\d{7,8}$/.test(raw)) return true; if (/^\d{7,8}$/.test(compact)) return true; return false; } function h2IsValidContactPhone(text) { var v = String(text || '').trim(); if (!v) return false; return h2IsMobilePhone(v) || h2IsLandlinePhone(v); } function h2GetContactPhoneInputTip(text) { var v = String(text || '').trim(); if (!v) return ''; if (h2IsValidContactPhone(v)) return ''; var digits = v.replace(/[^\d]/g, ''); if (digits.charAt(0) === '1') { if (digits.length < 11) return '手机号码应为11位'; return '手机号码格式不正确'; } if (digits.charAt(0) === '0') { return '固定电话格式不正确,示例:0573-12345678'; } return '固定电话应为7-8位本地号码,或带区号如 0573-12345678'; } function h2FilterMobilePhoneInput(raw) { return String(raw || '').replace(/\D/g, '').slice(0, 11); } function h2FilterLandlinePhoneInput(raw) { var v = String(raw || '').replace(/[^\d-]/g, ''); var parts = v.split('-'); if (parts.length > 2) v = parts[0] + '-' + parts.slice(1).join(''); return v.slice(0, 16); } function h2GetMobilePhoneInputTip(text) { var v = String(text || '').trim(); if (!v) return ''; if (h2IsMobilePhone(v)) return ''; var digits = v.replace(/\D/g, ''); if (digits.length < 11) return '手机号码应为11位'; return '手机号码格式不正确'; } function h2GetLandlinePhoneInputTip(text) { var v = String(text || '').trim(); if (!v) return ''; if (h2IsLandlinePhone(v)) return ''; return '固定电话格式不正确,示例:0573-12345678'; } function h2ValidateStationPhones(station) { var mobile = (station.mobilePhone || '').trim(); var landline = (station.landlinePhone || '').trim(); if (!mobile && !landline) return '请至少填写手机号或固定电话'; if (mobile && !h2IsMobilePhone(mobile)) return h2GetMobilePhoneInputTip(mobile) || '手机号码格式不正确'; if (landline && !h2IsLandlinePhone(landline)) return h2GetLandlinePhoneInputTip(landline) || '固定电话格式不正确'; return ''; } function h2ApplyStationContactPhone(station, text) { var v = h2FilterContactPhoneInput(text); var digits = v.replace(/-/g, ''); if (/^1/.test(digits)) { return Object.assign({}, station, { mobilePhone: v, landlinePhone: '' }); } return Object.assign({}, station, { mobilePhone: '', landlinePhone: v }); } var H2_BUSINESS_HOURS_MODE_OPTIONS = [ { label: '全天营业', value: 'allDay', tone: 'allday' }, { label: '非全天营业', value: 'custom', tone: 'custom' } ]; var H2_BUSINESS_STATUS_BTN_OPTIONS = [ { label: '营业中', value: '营业中', tone: 'open' }, { label: '暂停营业', value: '暂停营业', tone: 'pause' }, { label: '停止营业', value: '停止营业', tone: 'stop' } ]; function h2RenderOptionButtonGroup(options, value, onChange, opts) { opts = opts || {}; var groupClass = 'h2-option-btn-group' + (opts.className ? ' ' + opts.className : ''); return React.createElement('div', { className: groupClass, role: 'group', 'aria-label': opts.ariaLabel || '' }, (options || []).map(function (opt) { var active = value === opt.value; var btnClass = 'h2-option-btn' + (active ? ' h2-option-btn--active' : '') + (opt.tone ? ' h2-option-btn--' + opt.tone : ''); return React.createElement('button', { type: 'button', key: String(opt.value), className: btnClass, 'aria-pressed': active, disabled: !!opts.disabled, onClick: function () { if (onChange) onChange(opt.value); } }, opt.label); }) ); } function h2IsAllDayBusinessHours(str) { var s = String(str || '').trim(); if (!s || s === '—') return false; if (s === '全天营业' || s === '24小时' || s === '24 小时') return true; return /^0?0:00\s*[-–]\s*24:00$/.test(s); } function h2ParseBusinessHours(str) { var s = String(str || '').trim(); if (!s || s === '—') { return { mode: 'allDay', start: '08:00', end: '22:00' }; } if (h2IsAllDayBusinessHours(s)) { return { mode: 'allDay', start: '08:00', end: '22:00' }; } var parts = s.split('-'); if (parts.length >= 2) { return { mode: 'custom', start: (parts[0] || '').trim(), end: (parts.slice(1).join('-') || '').trim() }; } return { mode: 'custom', start: '', end: '' }; } function h2FormatBusinessHours(val) { if (!val || val.mode === 'allDay') return '00:00-24:00'; var start = (val.start || '').trim(); var end = (val.end || '').trim(); if (!start || !end) return ''; return start + '-' + end; } function h2DisplayBusinessHours(str) { var s = String(str || '').trim(); if (!s || s === '—') return '—'; if (h2IsAllDayBusinessHours(s) || s === '全天营业') return '00:00-24:00'; var parsed = h2ParseBusinessHours(s); if (parsed.mode === 'custom' && parsed.start && parsed.end) return parsed.start + '-' + parsed.end; return s; } function h2NormalizeTimeText(text) { var s = String(text || '').trim(); if (!s) return ''; var m = /^(\d{1,2}):(\d{2})$/.exec(s); if (!m) return s; var h = parseInt(m[1], 10); var min = parseInt(m[2], 10); if (isNaN(h) || isNaN(min) || h < 0 || h > 23 || min < 0 || min > 59) return s; return (h < 10 ? '0' : '') + h + ':' + (min < 10 ? '0' : '') + min; } function h2TimeTextToDayjs(text) { var dayjs = window.dayjs; if (!dayjs) return null; var norm = h2NormalizeTimeText(text); if (!norm) return null; var parts = norm.split(':'); return dayjs().hour(parseInt(parts[0], 10)).minute(parseInt(parts[1], 10)).second(0).millisecond(0); } function h2DayjsToTimeText(d) { var dayjs = window.dayjs; if (!d || !dayjs) return ''; return dayjs(d).format('HH:mm'); } /** 营业时间:全天 / 非全天按钮组 + 起止时间(时:分,可输入) */ function BusinessHoursInput(props) { var antd = window.antd; var TimePicker = antd.TimePicker; var Input = antd.Input; var parsed = h2ParseBusinessHours(props.value); var onChange = props.onChange; var disabled = props.disabled; var inputClassName = props.inputClassName || ''; var fieldStyle = props.fieldStyle || { width: '100%' }; var layout = props.layout || 'full'; var dayjs = window.dayjs; var rootClass = 'h2-business-hours' + (layout === 'col4' ? ' h2-business-hours--col4' : ''); var emit = function (next) { onChange && onChange(h2FormatBusinessHours(next)); }; var renderTimeField = function (label, timeKey, placeholder) { var timeVal = parsed[timeKey] || ''; var onTimeChange = function (nextText) { var patch = { mode: 'custom', start: parsed.start || '', end: parsed.end || '' }; patch[timeKey] = h2NormalizeTimeText(nextText); emit(patch); }; return React.createElement('div', { className: 'h2-business-hours-range-item', key: timeKey }, React.createElement('span', { className: 'h2-business-hours-range-label' }, label), dayjs ? React.createElement(TimePicker, { className: inputClassName, format: 'HH:mm', minuteStep: 1, hourStep: 1, showNow: false, allowClear: !disabled, inputReadOnly: false, disabled: disabled, style: fieldStyle, placeholder: placeholder, value: h2TimeTextToDayjs(timeVal), onChange: function (d) { onTimeChange(d ? h2DayjsToTimeText(d) : ''); } }) : React.createElement(Input, { className: inputClassName, disabled: disabled, style: fieldStyle, placeholder: placeholder || 'HH:mm', value: timeVal, maxLength: 5, onChange: function (e) { onTimeChange(e.target.value); }, onBlur: function (e) { onTimeChange(e.target.value); } }) ); }; return React.createElement('div', { className: rootClass }, h2RenderOptionButtonGroup(H2_BUSINESS_HOURS_MODE_OPTIONS, parsed.mode, function (mode) { if (mode === 'allDay') emit({ mode: 'allDay', start: parsed.start, end: parsed.end }); else emit({ mode: 'custom', start: parsed.start || '08:00', end: parsed.end || '22:00' }); }, { ariaLabel: '营业时间类型', disabled: disabled }), parsed.mode === 'custom' ? React.createElement('div', { className: 'h2-business-hours-custom-panel' }, React.createElement('div', { className: 'h2-business-hours-custom-panel__title' }, '营业时段'), React.createElement('div', { className: 'h2-business-hours-range' }, renderTimeField('开始时间', 'start', '请选择开始时间'), renderTimeField('结束时间', 'end', '请选择结束时间') ) ) : null ); } function h2CreateEmptyForm() { return { name: '', address: h2EmptyAddressValue(), isSigned: true, contractStart: '', contractEnd: '', contractFiles: [], businessStatus: '营业中', businessHours: '', contact: '', phone: '' }; } function h2RecordToForm(record) { if (!record) return h2CreateEmptyForm(); return { name: record.name || '', address: { region: (record.region || []).slice(), detail: record.addressDetail || '' }, isSigned: record.isSigned !== false, contractStart: record.contractStart || '', contractEnd: record.contractEnd || '', contractFiles: (record.contractFiles || []).map(function (f, i) { return Object.assign({}, f, { uid: f.uid || ('f-' + i), status: 'done' }); }), businessStatus: record.businessStatus || '营业中', businessHours: record.businessHours || '', contact: record.contact || '', phone: record.phone || '' }; } function h2FormToRecord(form, id) { var files = (form.contractFiles || []).map(function (f) { return { uid: f.uid, name: f.name || '合同附件.pdf', url: f.url || '' }; }); return { id: id, name: (form.name || '').trim(), region: form.address.region || [], addressDetail: (form.address.detail || '').trim(), fullAddress: h2BuildFullAddress(form.address.region, form.address.detail), isSigned: !!form.isSigned, contractStart: form.contractStart || '', contractEnd: form.contractEnd || '', contractFiles: files, businessStatus: form.businessStatus || '营业中', businessHours: (form.businessHours || '').trim(), contact: (form.contact || '').trim(), phone: (form.phone || '').trim(), updateTime: new Date().toISOString().slice(0, 16).replace('T', ' ') }; } var H2_MOCK_STATIONS = [ { id: 1, name: '嘉兴加氢站(一期)', region: ['浙江省', '嘉兴市'], addressDetail: '南湖区科技大道88号', fullAddress: '浙江省-嘉兴市南湖区科技大道88号', isSigned: true, contractStart: '2024-01-01', contractEnd: '2026-12-31', contractFiles: [{ uid: 'c1', name: '嘉兴加氢站签约合同.pdf' }], businessStatus: '营业中', businessHours: '08:00-22:00', costUnitPrice: 42.5, customerUnitPrice: 45, costPriceLogs: [ { id: 'cpl-1-1', operator: '王静', operateTime: '2026-01-10 09:00', beforeCostPrice: null, afterCostPrice: 41.0, effectiveTime: '2026-01-15 00:00' }, { id: 'cpl-1-2', operator: '系统管理员', operateTime: '2026-05-01 14:20', beforeCostPrice: 41.0, afterCostPrice: 42.5, effectiveTime: '2026-05-05 00:00' } ], prepaidBalance: 85620.0, balanceAlertThreshold: 50000, contact: '张三', phone: '13800138001', businessStatusLogs: [], supplierMode: 'link', linkedSupplierId: 'sup-h2-1', bindAccountMode: 'bind', boundAccountId: 'acc-h2-1', boundAccount: { id: 'acc-h2-1', username: 'jx_h2_admin', displayName: '嘉兴站管理员', role: '加氢站管理员', mobile: '13800138001', status: '启用' }, updateTime: '2026-05-20 14:30' }, { id: 2, name: '杭州临平加氢站', region: ['浙江省', '杭州市'], addressDetail: '临平区东湖街道能源路16号', fullAddress: '浙江省-杭州市临平区东湖街道能源路16号', isSigned: true, contractStart: '2025-03-01', contractEnd: '2027-02-28', contractFiles: [{ uid: 'c2', name: '临平加氢站合作协议.pdf' }, { uid: 'c3', name: '临平加氢站补充协议.pdf' }], businessStatus: '营业中', businessHours: '00:00-24:00', costUnitPrice: 43.0, customerUnitPrice: 46, costPriceLogs: [ { id: 'cpl-2-1', operator: '李四', operateTime: '2026-05-20 11:00', beforeCostPrice: 42.0, afterCostPrice: 43.0, effectiveTime: '2026-05-25 00:00' } ], prepaidBalance: 125000.5, contact: '李四', phone: '13900139002', supplierMode: 'new', supplier: { signingCompany: '浙江羚牛氢能科技有限公司', name: '杭州临平氢能运营有限公司', type: '加氢站', city: ['浙江省', '杭州市'], address: '临平区东湖街道能源路16号', region: '华东', contactName: '李四', contactMobile: '13900139002', contactTitle: '站长', taxId: '91330110MA3LINPING1', invoiceAddress: '浙江省杭州市临平区东湖街道能源路16号', invoicePhone: '0571-88882222', bankName: '中国农业银行杭州临平支行', bankAccount: '6228480402631234567', mailingAddress: '浙江省杭州市临平区东湖街道能源路16号', businessAddress: '浙江省杭州市临平区东湖街道能源路16号', businessLicenseFiles: [{ uid: 'bl-hz', name: '营业执照_临平氢能.pdf' }], fillingLicenseFiles: [ { uid: 'fl-hz-1', name: '加氢站经营许可证.pdf' }, { uid: 'fl-hz-2', name: '压力容器检验报告.pdf' } ] }, updateTime: '2026-06-01 09:15' }, { id: 3, name: '上海宝山加氢站', region: ['上海市', '上海市'], addressDetail: '宝山区富联路128号', fullAddress: '上海市-上海市宝山区富联路128号', isSigned: true, contractStart: '2023-06-01', contractEnd: '2025-05-31', contractFiles: [{ uid: 'c4', name: '宝山加氢站合同(即将到期).pdf' }], businessStatus: '暂停营业', businessHours: '09:00-18:00', costUnitPrice: 44.0, customerUnitPrice: 47, costPriceLogs: [ { id: 'cpl-3-1', operator: '王五', operateTime: '2026-03-01 10:30', beforeCostPrice: 43.5, afterCostPrice: 44.0, effectiveTime: '2026-03-05 00:00' } ], prepaidBalance: -3250.0, balanceAlertThreshold: 10000, businessStatusLogs: [ { id: 'bsl-3-2', operateTime: '2026-04-01 10:00', operator: '王五', beforeStatus: '营业中', afterStatus: '暂停营业' }, { id: 'bsl-3-1', operateTime: '2026-03-15 09:30', operator: '系统管理员', beforeStatus: '暂停营业', afterStatus: '营业中' } ], contact: '王五', phone: '13700137003', supplierMode: 'link', linkedSupplierId: 'sup-h2-2', bindAccountMode: 'bind', boundAccountId: 'acc-h2-3', boundAccount: { id: 'acc-h2-3', username: 'sh_bs_h2', displayName: '宝山站管理员', role: '加氢站管理员', mobile: '13700137003', status: '启用' }, updateTime: '2026-04-12 16:40' }, { id: 4, name: '平湖合作停车场(待建加氢)', region: ['浙江省', '嘉兴市'], addressDetail: '平湖市乍嘉公路与平善大道交叉口', fullAddress: '浙江省-嘉兴市平湖市乍嘉公路与平善大道交叉口', isSigned: false, contractStart: '', contractEnd: '', contractFiles: [], businessStatus: '停止营业', businessHours: '—', prepaidBalance: 0, contact: '赵六', phone: '13600136004', updateTime: '2026-03-08 11:20' }, { id: 5, name: '苏州工业园区备用站', region: ['江苏省', '苏州市'], addressDetail: '工业园区星湖街328号', fullAddress: '江苏省-苏州市工业园区星湖街328号', isSigned: false, contractStart: '', contractEnd: '', contractFiles: [], businessStatus: '停止营业', businessHours: '10:00-17:00', prepaidBalance: 15800.0, balanceAlertThreshold: 20000, contact: '钱七', phone: '13500135005', supplierMode: 'link', linkedSupplierId: 'sup-h2-3', updateTime: '2026-02-18 08:50' }, { id: 6, name: '桐乡合作加氢站', region: ['浙江省', '嘉兴市'], addressDetail: '桐乡市梧桐街道庆丰路66号', fullAddress: '浙江省-嘉兴市桐乡市梧桐街道庆丰路66号', isSigned: true, contractStart: '2025-06-01', contractEnd: '2027-05-31', contractFiles: [], businessStatus: '营业中', businessHours: '08:00-20:00', costUnitPrice: 41.8, customerUnitPrice: 44.5, costPriceLogs: [], prepaidBalance: 42000.0, contact: '周八', phone: '13800138006', updateTime: '2026-05-10 15:20' } ]; /** 解析站点关联的供应商(内嵌 supplier 或 linkedSupplierId) */ function h2ResolveStationSupplier(record) { if (!record) return null; if (record.supplier && ((record.supplier.name || '').trim() || (record.supplier.taxId || '').trim())) { return Object.assign({ type: '加氢站' }, record.supplier); } if (record.linkedSupplierId) { var i; for (i = 0; i < H2_MOCK_EXISTING_SUPPLIERS.length; i++) { if (H2_MOCK_EXISTING_SUPPLIERS[i].id === record.linkedSupplierId) { return H2_MOCK_EXISTING_SUPPLIERS[i]; } } } return null; } var H2_SETTLEMENT_STATUS_OPTIONS = [ { value: 'customer', label: '客户承担' }, { value: 'internal', label: '我司承担' }, { value: 'customer_self', label: '客户自行结算' } ]; var H2_SETTLEMENT_STATUS_MAP = { customer: { label: '客户承担', color: 'blue' }, internal: { label: '我司承担', color: 'green' }, customer_self: { label: '客户自行结算', color: 'orange' } }; function h2SettlementStatusLabel(value) { var info = H2_SETTLEMENT_STATUS_MAP[value]; return info ? info.label : '—'; } /** 站点加氢记录 Mock(字段对齐车辆氢费明细) */ var H2_MOCK_REFUEL_RECORDS = [ { id: 'rf-1', stationName: '嘉兴加氢站(一期)', hydrogenTime: '2026-05-28 10:21:08', plateNo: '浙A12345F', customerName: '嘉兴市鑫峤供应链科技有限公司', hydrogenKg: 12.5, costUnitPrice: 42.5, costAmount: 531.25, customerUnitPrice: 45, customerAmount: 562.5, settlementStatus: 'customer' }, { id: 'rf-2', stationName: '嘉兴加氢站(一期)', hydrogenTime: '2026-05-26 14:08:33', plateNo: '浙A67890F', customerName: '浙江绿运物流有限公司', hydrogenKg: 10.0, costUnitPrice: 42.5, costAmount: 425.0, customerUnitPrice: 45, customerAmount: 450.0, settlementStatus: 'internal' }, { id: 'rf-3', stationName: '嘉兴加氢站(一期)', hydrogenTime: '2026-05-22 09:15:00', plateNo: '浙A88888F', customerName: '嘉兴市鑫峤供应链科技有限公司', hydrogenKg: 18.3, costUnitPrice: 42.5, costAmount: 777.75, customerUnitPrice: 45, customerAmount: 823.5, settlementStatus: 'customer_self' }, { id: 'rf-4', stationName: '嘉兴加氢站(一期)', hydrogenTime: '2026-05-18 16:42:11', plateNo: '浙A03561F', customerName: '嘉兴港务氢能运输队', hydrogenKg: 15.6, costUnitPrice: 42.5, costAmount: 663.0, customerUnitPrice: 45, customerAmount: 702.0, settlementStatus: 'customer' }, { id: 'rf-5', stationName: '杭州临平加氢站', hydrogenTime: '2026-05-30 09:30:22', plateNo: '浙B23456F', customerName: '杭州临平城配中心', hydrogenKg: 15.3, costUnitPrice: 43.0, costAmount: 657.9, customerUnitPrice: 46, customerAmount: 703.8, settlementStatus: 'internal' }, { id: 'rf-6', stationName: '杭州临平加氢站', hydrogenTime: '2026-05-27 18:10:05', plateNo: '浙B99999F', customerName: '浙江氢运科技', hydrogenKg: 18.2, costUnitPrice: 43.0, costAmount: 782.6, customerUnitPrice: 46, customerAmount: 837.2, settlementStatus: 'customer_self' }, { id: 'rf-7', stationName: '杭州临平加氢站', hydrogenTime: '2026-05-24 11:05:40', plateNo: '浙B58888F', customerName: '杭州临平城配中心', hydrogenKg: 11.8, costUnitPrice: 43.0, costAmount: 507.4, customerUnitPrice: 46, customerAmount: 542.8, settlementStatus: 'customer' }, { id: 'rf-8', stationName: '上海宝山加氢站', hydrogenTime: '2026-04-20 16:45:18', plateNo: '沪A88888F', customerName: '上海羚牛氢运', hydrogenKg: 8.0, costUnitPrice: 44.0, costAmount: 352.0, customerUnitPrice: 47, customerAmount: 376.0, settlementStatus: 'internal' }, { id: 'rf-9', stationName: '上海宝山加氢站', hydrogenTime: '2026-04-08 09:12:55', plateNo: '沪BDB9161F', customerName: '宝山园区试运车队', hydrogenKg: 9.5, costUnitPrice: 44.0, costAmount: 418.0, customerUnitPrice: 47, customerAmount: 446.5, settlementStatus: 'customer_self' }, { id: 'rf-10', stationName: '苏州工业园区备用站', hydrogenTime: '2026-03-15 10:00:00', plateNo: '苏E33333F', customerName: '苏州试运客户', hydrogenKg: 6.2, costUnitPrice: 41.0, costAmount: 254.2, customerUnitPrice: 44, customerAmount: 272.8, settlementStatus: 'customer' } ]; function h2FormatKgNum(v) { var n = typeof v === 'number' ? v : parseFloat(v); return isNaN(n) ? '0.00' : n.toFixed(2); } function h2FormatYuanNum(v) { var n = typeof v === 'number' ? v : parseFloat(v); return isNaN(n) ? '0.00' : n.toFixed(2); } function h2FormatYuanSymbol(v, options) { var opts = options || {}; if (v == null || v === '') return '—'; if (!opts.keepZero && Number(v) === 0) return '—'; var n = typeof v === 'number' ? v : parseFloat(v); if (isNaN(n)) return '—'; return '¥' + n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } var H2_BALANCE_BIZ_TYPE_MAP = { recharge: { key: 'recharge', label: '充值', color: 'success' }, refuel: { key: 'refuel', label: '车辆加氢', color: 'warning' }, ending: { key: 'ending', label: '期末', color: 'default' } }; function h2Pad2(n) { return String(n).padStart(2, '0'); } function h2BuildBalanceChangeTime(seqIndex) { var day = Math.max(1, 28 - seqIndex * 3); var hour = 8 + ((seqIndex * 3) % 12); var minute = (seqIndex * 17) % 60; return '2026-05-' + h2Pad2(day) + ' ' + h2Pad2(hour) + ':' + h2Pad2(minute) + ':00'; } function h2ResolveBalanceBizType(row) { if (row && row.bizType && H2_BALANCE_BIZ_TYPE_MAP[row.bizType]) { return H2_BALANCE_BIZ_TYPE_MAP[row.bizType]; } if (row && row.incomeAmount != null && row.incomeAmount !== '') return H2_BALANCE_BIZ_TYPE_MAP.recharge; if (row && row.expenseAmount != null && row.expenseAmount !== '') return H2_BALANCE_BIZ_TYPE_MAP.refuel; return H2_BALANCE_BIZ_TYPE_MAP.ending; } function h2FormatBalanceExportAmount(v) { if (v == null || v === '') return ''; return h2FormatYuanNum(v); } function h2ExportBalanceDrillCsv(stationName, rows) { var headers = ['序号', '时间', '类型', '收入(元)', '支出(元)', '余额(元)', '订单编号']; var csvRows = (rows || []).map(function (r, index) { var typeInfo = h2ResolveBalanceBizType(r); return [ index + 1, r.changeTime || '', typeInfo.label, h2FormatBalanceExportAmount(r.incomeAmount), h2FormatBalanceExportAmount(r.expenseAmount), h2FormatBalanceExportAmount(r.balance), r.orderNo || '' ]; }); var safeName = String(stationName || '站点').replace(/[\\/:*?"<>|]/g, '_'); h2DownloadCsv('余额变更明细_' + safeName + '_' + new Date().getTime() + '.csv', headers, csvRows); } function h2NumOrZero(v) { var n = typeof v === 'number' ? v : parseFloat(v); return isNaN(n) ? 0 : n; } function h2FormatLedgerMoney(v) { if (v === null || v === undefined || v === '' || Number(v) === 0) return '—'; return h2FormatYuanNum(v); } /** 原型:加氢站预付余额变更明细(联调后对接加氢站打款/余额流水接口) */ function h2BuildMockPrepaidBalanceRows(record) { var seed = 0; var code = String(record && record.id != null ? record.id : ''); var nameKey = String((record && record.name) || ''); var i; for (i = 0; i < code.length; i++) seed += code.charCodeAt(i); for (i = 0; i < nameKey.length; i++) seed += nameKey.charCodeAt(i); seed = seed % 100; var stationName = (record && record.name) || '—'; var finalBalance = h2NumOrZero(record && record.prepaidBalance); var lineCount = Math.max(5, 6 + (seed % 4)); var rows = []; var openingBalance = Math.round((42000 + seed * 520) * 100) / 100; if (openingBalance < 0) openingBalance = Math.abs(openingBalance) + 30000; rows.push({ key: code + '-bal-0', stationName: stationName, changeTime: '2026-01-01 00:00:00', bizType: 'recharge', incomeAmount: openingBalance, expenseAmount: null, balance: openingBalance, orderNo: 'QC' + code + '260101' }); var balance = openingBalance; for (i = 1; i < lineCount - 1; i++) { var isIncome = (seed + i) % 3 === 0; var incomeAmount = null; var expenseAmount = null; if (isIncome) { incomeAmount = Math.round((5000 + (seed + i) * 42) * 100) / 100; balance = Math.round((balance + incomeAmount) * 100) / 100; } else { expenseAmount = Math.round((2200 + (seed + i) * 26) * 100) / 100; balance = Math.round((balance - expenseAmount) * 100) / 100; } rows.push({ key: code + '-bal-' + i, stationName: stationName, changeTime: h2BuildBalanceChangeTime(i), bizType: isIncome ? 'recharge' : 'refuel', incomeAmount: incomeAmount, expenseAmount: expenseAmount, balance: balance, orderNo: (isIncome ? 'DK' : 'JQ') + code + String(200 + i).padStart(5, '0') }); } var diff = Math.round((finalBalance - balance) * 100) / 100; if (Math.abs(diff) < 0.01) { rows.push({ key: code + '-bal-last', stationName: stationName, changeTime: '2026-06-01 23:59:59', bizType: 'ending', incomeAmount: null, expenseAmount: null, balance: finalBalance, orderNo: 'BZ' + code + String(900).padStart(5, '0') }); } else if (diff > 0) { rows.push({ key: code + '-bal-last', stationName: stationName, changeTime: '2026-06-01 18:30:00', bizType: 'recharge', incomeAmount: diff, expenseAmount: null, balance: finalBalance, orderNo: 'DK' + code + String(999).padStart(5, '0') }); } else { rows.push({ key: code + '-bal-last', stationName: stationName, changeTime: '2026-06-01 18:30:00', bizType: 'refuel', incomeAmount: null, expenseAmount: Math.round(Math.abs(diff) * 100) / 100, balance: finalBalance, orderNo: 'JQ' + code + String(999).padStart(5, '0') }); } return rows; } function h2FormatBalanceTrendLabel(changeTime) { if (!changeTime) return '—'; var text = String(changeTime).trim(); if (text.length >= 10) return text.slice(5, 10); return text; } function h2ParseChangeTimeMs(changeTime) { if (!changeTime) return 0; var text = String(changeTime).trim(); var ms = Date.parse(text.indexOf('T') === -1 ? text.replace(' ', 'T') : text); return isNaN(ms) ? 0 : ms; } function h2ExtractBalanceDateKey(changeTime) { if (!changeTime) return ''; var text = String(changeTime).trim(); return text.length >= 10 ? text.slice(0, 10) : text; } function h2FormatDateOnlyFromDate(date) { return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0'); } function h2GetYesterdayDateOnly() { var d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() - 1); return h2FormatDateOnlyFromDate(d); } function h2AddMonths(dateStr, months) { var ms = h2ParseDateOnlyMs(dateStr); if (isNaN(ms)) return dateStr; var d = new Date(ms); d.setMonth(d.getMonth() + (months || 0)); return h2FormatDateOnlyFromDate(d); } /** 趋势图固定展示近三个月:结束日为昨日,起始日为昨日往前推 3 个月 */ function h2ResolveBalanceTrendRange() { var endDate = h2GetYesterdayDateOnly(); var startDate = h2AddMonths(endDate, -3); return { startDate: startDate, endDate: endDate }; } /** 流水按日聚合为趋势点:每日取末笔余额,无流水日沿用前一日余额 */ function h2BuildBalanceTrendSeries(rows) { var range = h2ResolveBalanceTrendRange(); var startDate = range.startDate; var endDate = range.endDate; var startMs = h2ParseDateOnlyMs(startDate); var endMs = h2ParseDateOnlyMs(endDate); if (!rows || !rows.length || isNaN(startMs) || isNaN(endMs)) { return { points: [], min: 0, max: 0, hasNegative: false, granularity: 'day', startDate: startDate, endDate: endDate }; } var sorted = rows.slice().sort(function (a, b) { return h2ParseChangeTimeMs(a.changeTime) - h2ParseChangeTimeMs(b.changeTime); }); var dayBuckets = {}; var i; for (i = 0; i < sorted.length; i++) { var row = sorted[i]; var dateKey = h2ExtractBalanceDateKey(row.changeTime); if (!dateKey) continue; if (!dayBuckets[dateKey]) { dayBuckets[dateKey] = row; } else if (h2ParseChangeTimeMs(row.changeTime) >= h2ParseChangeTimeMs(dayBuckets[dateKey].changeTime)) { dayBuckets[dateKey] = row; } } var seedBalance = null; for (i = sorted.length - 1; i >= 0; i--) { var seedKey = h2ExtractBalanceDateKey(sorted[i].changeTime); if (seedKey && h2ParseDateOnlyMs(seedKey) < startMs) { seedBalance = h2NumOrZero(sorted[i].balance); break; } } var points = []; var lastBalance = seedBalance; var cursor = startDate; var guard = 0; while (guard < 4000) { guard += 1; var cursorMs = h2ParseDateOnlyMs(cursor); if (isNaN(cursorMs) || cursorMs > endMs) break; if (dayBuckets[cursor]) { lastBalance = h2NumOrZero(dayBuckets[cursor].balance); } if (lastBalance != null) { points.push({ key: 'day-' + cursor, label: h2FormatBalanceTrendLabel(cursor + ' 00:00:00'), fullTime: cursor, balance: lastBalance }); } if (cursor === endDate) break; cursor = h2AddDays(cursor, 1); } if (!points.length) { return { points: [], min: 0, max: 0, hasNegative: false, granularity: 'day', startDate: startDate, endDate: endDate }; } var min = points[0].balance; var max = points[0].balance; var hasNegative = false; for (i = 0; i < points.length; i++) { if (points[i].balance < min) min = points[i].balance; if (points[i].balance > max) max = points[i].balance; if (points[i].balance < 0) hasNegative = true; } var pad = Math.max((max - min) * 0.12, max === min ? Math.abs(max) * 0.1 || 500 : 0); if (hasNegative && min > 0) min = 0; return { points: points, min: min - pad, max: max + pad, hasNegative: hasNegative, granularity: 'day', startDate: startDate, endDate: endDate }; } function H2BalanceTrendChart(props) { var series = props.series || {}; var points = series.points || []; var hoverState = React.useState(null); var hoverIndex = hoverState[0]; var setHoverIndex = hoverState[1]; if (!points.length) { return React.createElement('div', { className: 'h2-balance-trend-empty' }, '暂无趋势数据'); } var W = 820; var H = 220; var PL = 56; var PR = 20; var PT = 18; var PB = 36; var chartW = W - PL - PR; var chartH = H - PT - PB; var min = series.min; var max = series.max; var range = max - min || 1; var coords = points.map(function (p, idx) { var x = PL + (points.length === 1 ? chartW / 2 : (idx / (points.length - 1)) * chartW); var y = PT + chartH - ((p.balance - min) / range) * chartH; return { x: x, y: y, label: p.label, balance: p.balance, fullTime: p.fullTime, key: p.key }; }); var linePath = coords.map(function (c, i) { return (i === 0 ? 'M' : 'L') + c.x.toFixed(1) + ' ' + c.y.toFixed(1); }).join(' '); var areaPath = linePath + ' L' + coords[coords.length - 1].x.toFixed(1) + ' ' + (PT + chartH) + ' L' + coords[0].x.toFixed(1) + ' ' + (PT + chartH) + ' Z'; var zeroY = null; if (min < 0 && max > 0) { zeroY = PT + chartH - ((0 - min) / range) * chartH; } var yTicks = [ { value: max, y: PT }, { value: min + range / 2, y: PT + chartH / 2 }, { value: min, y: PT + chartH } ]; var xLabels = [ { x: coords[0].x, label: coords[0].label }, { x: coords[coords.length - 1].x, label: coords[coords.length - 1].label } ]; var pickNearestIndex = function (mouseX) { var nearest = 0; var minDist = Infinity; var j; for (j = 0; j < coords.length; j++) { var dist = Math.abs(coords[j].x - mouseX); if (dist < minDist) { minDist = dist; nearest = j; } } return nearest; }; var handleChartMouseMove = function (e) { var rect = e.currentTarget.getBoundingClientRect(); if (!rect.width) return; var mouseX = ((e.clientX - rect.left) / rect.width) * W; if (mouseX < PL - 8 || mouseX > W - PR + 8) { setHoverIndex(null); return; } setHoverIndex(pickNearestIndex(mouseX)); }; var active = hoverIndex != null && coords[hoverIndex] ? coords[hoverIndex] : null; var tooltipNode = active ? React.createElement('div', { className: 'h2-balance-trend-tooltip', style: { left: (active.x / W * 100) + '%', top: (active.y / H * 100) + '%' }, role: 'tooltip' }, React.createElement('div', { className: 'h2-balance-trend-tooltip__date' }, active.fullTime || '—'), React.createElement('div', { className: 'h2-balance-trend-tooltip__balance' + (active.balance < 0 ? ' h2-balance-trend-tooltip__balance--negative' : '') }, h2FormatYuanSymbol(active.balance, { keepZero: true })) ) : null; return React.createElement('div', { className: 'h2-balance-trend-chart-wrap', onMouseLeave: function () { setHoverIndex(null); } }, tooltipNode, React.createElement('svg', { className: 'h2-balance-trend-svg', viewBox: '0 0 ' + W + ' ' + H, role: 'img', 'aria-label': '预付余额趋势图', onMouseMove: handleChartMouseMove, onMouseLeave: function () { setHoverIndex(null); } }, React.createElement('defs', null, React.createElement('linearGradient', { id: 'h2BalanceTrendArea', x1: '0', y1: '0', x2: '0', y2: '1' }, React.createElement('stop', { offset: '0%', stopColor: '#10b981', stopOpacity: '0.28' }), React.createElement('stop', { offset: '100%', stopColor: '#10b981', stopOpacity: '0.02' }) ) ), yTicks.map(function (tick, idx) { return React.createElement('g', { key: 'yt-' + idx }, React.createElement('line', { x1: PL, y1: tick.y, x2: W - PR, y2: tick.y, stroke: '#f1f5f9', strokeWidth: 1 }), React.createElement('text', { x: PL - 8, y: tick.y + 4, textAnchor: 'end', fontSize: 10, fill: '#94a3b8', fontFamily: 'ui-sans-serif, system-ui, sans-serif' }, h2FormatYuanNum(tick.value)) ); }), zeroY != null ? React.createElement('line', { x1: PL, y1: zeroY, x2: W - PR, y2: zeroY, stroke: '#fca5a5', strokeWidth: 1, strokeDasharray: '4 4' }) : null, active ? React.createElement('line', { x1: active.x, y1: PT, x2: active.x, y2: PT + chartH, stroke: '#a7f3d0', strokeWidth: 1, strokeDasharray: '3 3' }) : null, React.createElement('path', { d: areaPath, fill: 'url(#h2BalanceTrendArea)' }), React.createElement('path', { d: linePath, fill: 'none', stroke: '#10b981', strokeWidth: 2.5, strokeLinejoin: 'round', strokeLinecap: 'round' }), coords.map(function (c, idx) { var negative = c.balance < 0; var isActive = hoverIndex === idx; return React.createElement('g', { key: c.key }, React.createElement('circle', { className: 'h2-balance-trend-hit', cx: c.x, cy: c.y, r: 10, onMouseEnter: function () { setHoverIndex(idx); } }), React.createElement('circle', { className: isActive ? 'h2-balance-trend-point--active' : '', cx: c.x, cy: c.y, r: isActive ? 5.5 : 4, fill: '#fff', stroke: negative ? '#dc2626' : '#10b981', strokeWidth: isActive ? 2.5 : 2, pointerEvents: 'none' }) ); }), xLabels.map(function (item, idx) { return React.createElement('text', { key: 'xl-' + idx, x: item.x, y: H - 10, textAnchor: idx === 0 ? 'start' : 'end', fontSize: 10, fill: '#64748b', fontFamily: 'ui-sans-serif, system-ui, sans-serif' }, item.label); }) ) ); } function h2BuildRefuelOrderNo(record, index) { if (record && record.orderNo) return record.orderNo; var idPart = String((record && record.id) || '').replace(/\D/g, '') || String(index + 1); return 'JQ' + idPart.padStart(4, '0') + String(100 + index); } /** 对账状态(对齐车辆氢费明细) */ var H2_RECONCILE_RECONCILED = 'reconciled'; var H2_RECONCILE_PENDING = 'pending'; var H2_PAYMENT_STATUS_PAID = 'paid'; var H2_PAYMENT_STATUS_UNPAID = 'unpaid'; function h2ParseDateOnlyMs(dateStr) { if (!dateStr) return NaN; var parts = String(dateStr).trim().split('-'); if (parts.length < 3) return NaN; var y = parseInt(parts[0], 10); var m = parseInt(parts[1], 10); var d = parseInt(parts[2], 10); if (isNaN(y) || isNaN(m) || isNaN(d)) return NaN; return new Date(y, m - 1, d, 0, 0, 0, 0).getTime(); } function h2AddDays(dateStr, days) { var ms = h2ParseDateOnlyMs(dateStr); if (isNaN(ms)) return dateStr; var d = new Date(ms + (days || 0) * 86400000); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); } function h2FormatOperateDateOnly() { return h2OperateTimestamp().slice(0, 10); } /** 台账加氢记录 Mock(字段对齐车辆氢费明细,costTotal 对应成本总价) */ function h2BuildLedgerReconciledRecords() { return H2_MOCK_REFUEL_RECORDS.map(function (r, index) { var isReconciled = index < 9; return { id: r.id, stationName: r.stationName, hydrogenTime: r.hydrogenTime, plateNo: r.plateNo, customerName: r.customerName, hydrogenKg: r.hydrogenKg, costUnitPrice: r.costUnitPrice, costTotal: r.costAmount, settlementStatus: r.settlementStatus, orderNo: h2BuildRefuelOrderNo(r, index), reconcileStatus: isReconciled ? H2_RECONCILE_RECONCILED : H2_RECONCILE_PENDING, statementRecordId: null, reconcileDate: null, receiptDate: null, paymentStatus: isReconciled ? H2_PAYMENT_STATUS_UNPAID : null }; }); } function h2InitLedgerRecordsStore() { return h2BuildLedgerReconciledRecords(); } function h2InitStatementRecordsStore() { return []; } function h2GetLastStatementEndDate(statementRecords, stationName) { var name = String(stationName || '').trim(); var list = (statementRecords || []).filter(function (s) { return (s.stationName || '') === name; }); if (!list.length) return null; var max = list[0].endDate; var i; for (i = 1; i < list.length; i++) { if (String(list[i].endDate || '') > String(max || '')) max = list[i].endDate; } return max || null; } function h2GetAvailableStatementLedgerRows(store, stationName, startDate, endDate) { var name = String(stationName || '').trim(); var startMs = h2ParseDateOnlyMs(startDate); var endMs = h2ParseDateOnlyMs(endDate); if (!name || isNaN(startMs) || isNaN(endMs)) return []; var endInclusive = endMs + 86400000 - 1; return (store || []).filter(function (r) { if (r.reconcileStatus !== H2_RECONCILE_RECONCILED) return false; if (r.statementRecordId) return false; if ((r.stationName || '') !== name) return false; var t = h2ParseDateTimeMs(r.hydrogenTime); if (isNaN(t)) return false; return t >= startMs && t <= endInclusive; }).slice().sort(function (a, b) { return String(b.hydrogenTime || '').localeCompare(String(a.hydrogenTime || '')); }); } function h2GetStatementRecordsByStation(statementRecords, stationName) { var name = String(stationName || '').trim(); return (statementRecords || []).filter(function (s) { return (s.stationName || '') === name; }).slice().sort(function (a, b) { return String(b.reconcileDate || '').localeCompare(String(a.reconcileDate || '')); }); } function h2GetLedgerRowsByIds(store, ids) { var idSet = {}; var i; for (i = 0; i < (ids || []).length; i++) idSet[ids[i]] = true; return (store || []).filter(function (r) { return idSet[r.id]; }).slice().sort(function (a, b) { return String(b.hydrogenTime || '').localeCompare(String(a.hydrogenTime || '')); }); } function h2PushStatementLedgerPatches(patches) { if (typeof window === 'undefined') return; window.H2_STATION_STATEMENT_LEDGER_UPDATES = (window.H2_STATION_STATEMENT_LEDGER_UPDATES || []).concat(patches || []); } function h2CalcStatementSummary(records) { var rows = records || []; var totalKg = 0; var totalCost = 0; var i; for (i = 0; i < rows.length; i++) { totalKg += rows[i].hydrogenKg || 0; totalCost += rows[i].costTotal || 0; } return { count: rows.length, totalKg: totalKg, totalCost: totalCost }; } function h2ExportStatementCsv(stationName, startDate, endDate, records) { var headers = ['序号', '加氢时间', '车牌号', '加氢量(kg)', '成本单价(元/kg)', '成本总价(元)', '订单编号']; var csvRows = (records || []).map(function (r, index) { return [ index + 1, r.hydrogenTime || '', r.plateNo || '', h2FormatKgNum(r.hydrogenKg), h2FormatBalanceExportAmount(r.costUnitPrice), h2FormatBalanceExportAmount(r.costTotal), r.orderNo || '' ]; }); var safeName = String(stationName || '站点').replace(/[\\/:*?"<>|]/g, '_'); var rangePart = String(startDate || '').replace(/-/g, '') + '-' + String(endDate || '').replace(/-/g, ''); h2DownloadCsv('氢费对账单_' + safeName + '_' + rangePart + '_' + new Date().getTime() + '.csv', headers, csvRows); } function h2GetRefuelRecordsByStation(stationName) { var name = String(stationName || '').trim(); return H2_MOCK_REFUEL_RECORDS.filter(function (r) { return r.stationName === name; }).slice().sort(function (a, b) { return String(b.hydrogenTime || '').localeCompare(String(a.hydrogenTime || '')); }).map(function (r, index) { return Object.assign({}, r, { orderNo: h2BuildRefuelOrderNo(r, index) }); }); } function h2ExportRefuelDrillCsv(stationName, records) { var headers = ['序号', '加氢时间', '车牌号', '客户名称', '加氢量(kg)', '成本单价(元/kg)', '成本总价(元)', '加氢单价(元/kg)', '加氢总价(元)', '承担方式', '订单编号']; var csvRows = (records || []).map(function (r, index) { return [ index + 1, r.hydrogenTime || '', r.plateNo || '', r.customerName || '', h2FormatKgNum(r.hydrogenKg), h2FormatBalanceExportAmount(r.costUnitPrice), h2FormatBalanceExportAmount(r.costAmount), h2FormatBalanceExportAmount(r.customerUnitPrice), h2FormatBalanceExportAmount(r.customerAmount), h2SettlementStatusLabel(r.settlementStatus), r.orderNo || '' ]; }); var safeName = String(stationName || '站点').replace(/[\\/:*?"<>|]/g, '_'); h2DownloadCsv('加氢明细_' + safeName + '_' + new Date().getTime() + '.csv', headers, csvRows); } function h2CalcRefuelStats(stationName) { var records = h2GetRefuelRecordsByStation(stationName); var totalKg = 0; var i; for (i = 0; i < records.length; i++) totalKg += records[i].hydrogenKg || 0; return { count: records.length, totalKg: totalKg }; } var H2_LINGNIU_SIGNING_COMPANY_NAMES = [ '浙江羚牛氢能科技有限公司', '羚牛氢能科技(上海)有限公司', '羚牛氢能科技(广东)有限公司', '羚牛新能源运营有限公司' ]; function h2GetLingniuSigningCompanyOptions() { var names = H2_LINGNIU_SIGNING_COMPANY_NAMES.slice(); if (typeof window !== 'undefined' && window.ONEOS_LESSOR_COMPANIES && window.ONEOS_LESSOR_COMPANIES.length) { window.ONEOS_LESSOR_COMPANIES.forEach(function (c) { var n = (c.legalName || '').trim(); if (n && names.indexOf(n) < 0) names.push(n); }); } return names.map(function (name) { return { value: name, label: name }; }); } var H2_MOCK_EXISTING_SUPPLIERS = [ { id: 'sup-h2-1', name: '嘉兴氢能供应有限公司', type: '加氢站', city: ['浙江省', '嘉兴市'], address: '南湖区科技大道66号', region: '华东', dept: '能源部', manager: '张经理', remark: '嘉兴区域主力加氢站运营商', contactName: '李供', contactMobile: '13800138088', contactTitle: '站长', taxId: '91330400MA2ABCDEF1', invoiceAddress: '浙江省嘉兴市南湖区科技大道66号', invoicePhone: '0573-88886666', bankName: '中国工商银行嘉兴南湖支行', bankAccount: '6222021203001234567', mailingAddress: '浙江省嘉兴市南湖区科技大道66号', businessAddress: '浙江省嘉兴市南湖区科技大道66号', businessLicenseFiles: [{ uid: 'bl-1', name: '营业执照_嘉兴氢能.pdf', status: 'done' }], fillingLicenseFiles: [ { uid: 'fl-1a', name: '危化品经营许可证.pdf', status: 'done' }, { uid: 'fl-1b', name: '特种设备使用登记证.pdf', status: 'done' }, { uid: 'fl-1c', name: '消防验收合格证.pdf', status: 'done' } ] }, { id: 'sup-h2-2', name: '上海羚牛氢能科技', type: '加氢站', city: ['上海市', '上海市'], address: '宝山区富联路200号', region: '华东', dept: '能源部', manager: '王专员', remark: '', contactName: '陈氢', contactMobile: '13900139001', contactTitle: '运营主管', taxId: '91310100MA1XYZ7890', invoiceAddress: '上海市宝山区富联路200号', invoicePhone: '021-66886688', bankName: '招商银行上海宝山支行', bankAccount: '6214830210123456789', mailingAddress: '上海市宝山区富联路200号', businessAddress: '上海市宝山区富联路200号', businessLicenseFiles: [{ uid: 'bl-2', name: '营业执照_羚牛氢能.pdf', status: 'done' }], fillingLicenseFiles: [{ uid: 'fl-2', name: '加氢站备案证明.pdf', status: 'done' }] }, { id: 'sup-h2-3', name: '苏州工业园区氢源科技', type: '加氢站', city: ['江苏省', '苏州市'], address: '工业园区星湖街328号', region: '华东', dept: '华东大区', manager: '赵经理', remark: '备用站点供应商', contactName: '钱七', contactMobile: '13500135005', contactTitle: '联系人', taxId: '91320500MA2SUPPLIER', invoiceAddress: '江苏省苏州市工业园区星湖街328号', invoicePhone: '0512-66668888', bankName: '中国银行苏州工业园区支行', bankAccount: '6216600100009876543', mailingAddress: '江苏省苏州市工业园区星湖街328号', businessAddress: '江苏省苏州市工业园区星湖街328号', businessLicenseFiles: [], fillingLicenseFiles: [] }, { id: 'sup-other-1', name: '杭州绿能备件有限公司', type: '备件供应商', city: ['浙江省', '杭州市'], address: '余杭区文一西路998号', region: '华东', dept: '采购部', manager: '孙采购', remark: '', contactName: '周备件', contactMobile: '13600136001', contactTitle: '销售', taxId: '91330100MA3OTHER01', invoiceAddress: '浙江省杭州市余杭区文一西路998号', invoicePhone: '0571-88880001', bankName: '中国建设银行杭州余杭支行', bankAccount: '6227000012345678901', mailingAddress: '浙江省杭州市余杭区文一西路998号', businessAddress: '浙江省杭州市余杭区文一西路998号', businessLicenseFiles: [], fillingLicenseFiles: [] } ]; /** 可加氢站绑定的系统账号 Mock */ var H2_MOCK_SYSTEM_ACCOUNTS = [ { id: 'acc-h2-1', username: 'jx_h2_admin', displayName: '嘉兴站管理员', role: '加氢站管理员', mobile: '13800138001', status: '启用' }, { id: 'acc-h2-2', username: 'hz_lp_station', displayName: '临平站操作员', role: '加氢站管理员', mobile: '13900139002', status: '启用' }, { id: 'acc-h2-3', username: 'sh_bs_h2', displayName: '宝山站管理员', role: '加氢站管理员', mobile: '13700137003', status: '启用' }, { id: 'acc-h2-4', username: 'sz_h2_ops', displayName: '苏州备用站账号', role: '加氢站管理员', mobile: '13500135005', status: '启用' }, { id: 'acc-h2-5', username: 'h2_station_new', displayName: '待分配站点账号', role: '加氢站管理员', mobile: '13600136099', status: '启用' } ]; function h2FindSystemAccountById(accountId) { if (!accountId) return null; var i; for (i = 0; i < H2_MOCK_SYSTEM_ACCOUNTS.length; i++) { if (H2_MOCK_SYSTEM_ACCOUNTS[i].id === accountId) return H2_MOCK_SYSTEM_ACCOUNTS[i]; } return null; } function h2NormalizeBindAccountIds(source) { if (!source) return []; if (source.bindAccountIds && source.bindAccountIds.length) return source.bindAccountIds.slice(); if (source.boundAccountIds && source.boundAccountIds.length) return source.boundAccountIds.slice(); var single = source.bindAccountId || source.boundAccountId; return single ? [single] : []; } function h2BuildBoundAccountsFromIds(ids) { return (ids || []).map(function (id) { var acc = h2FindSystemAccountById(id); if (!acc) return null; return { id: acc.id, username: acc.username, displayName: acc.displayName, role: acc.role, mobile: acc.mobile, status: acc.status }; }).filter(Boolean); } function h2CollectUsedBindAccountMap(stations, excludeStationId) { var map = {}; (stations || []).forEach(function (s) { if (excludeStationId != null && s.id === excludeStationId) return; h2NormalizeBindAccountIds(s).forEach(function (aid) { map[aid] = s.name || '—'; }); }); return map; } function h2ResolveStationSystemAccounts(record) { if (!record) return []; if (record.boundAccounts && record.boundAccounts.length) return record.boundAccounts.slice(); if (record.boundAccount && (record.boundAccount.username || record.boundAccount.displayName)) { return [record.boundAccount]; } return h2BuildBoundAccountsFromIds(h2NormalizeBindAccountIds(record)); } function h2ResolveStationSystemAccount(record) { var list = h2ResolveStationSystemAccounts(record); return list.length ? list[0] : null; } function h2RecordToEditStationForm(record) { if (!record) return h2CreateEmptyStationForm(); var ids = h2NormalizeBindAccountIds(record); return { name: record.name || '', address: { region: (record.region || []).slice(), detail: record.addressDetail || '' }, isSigned: record.isSigned !== false, contractStart: record.contractStart || '', contractEnd: record.contractEnd || '', contractFiles: (record.contractFiles || []).map(function (f, i) { return Object.assign({}, f, { uid: f.uid || ('f-' + i), status: 'done' }); }), businessStatus: record.businessStatus || '营业中', businessHours: record.businessHours === '—' ? '' : (record.businessHours || ''), contact: record.contact || '', mobilePhone: record.mobilePhone || record.phone || '', landlinePhone: record.landlinePhone || '', costUnitPrice: record.costUnitPrice != null ? String(record.costUnitPrice) : '', customerUnitPrice: record.customerUnitPrice != null ? String(record.customerUnitPrice) : '', prepaidBalance: record.prepaidBalance || 0, bindAccountMode: record.bindAccountMode || (ids.length ? 'bind' : 'none'), bindAccountIds: ids }; } function h2CreateEmptyStationForm() { return { name: '', address: h2EmptyAddressValue(), isSigned: true, contractStart: '', contractEnd: '', contractFiles: [], businessStatus: '营业中', businessHours: '00:00-24:00', contact: '', mobilePhone: '', landlinePhone: '', costUnitPrice: '', customerUnitPrice: '', prepaidBalance: 0, bindAccountMode: 'none', bindAccountIds: [] }; } function h2CreateEmptySupplierForm() { return { signingCompany: undefined, name: '', city: undefined, address: '', region: '', dept: '', manager: '', remark: '', contactName: '', contactMobile: '', contactTitle: '', taxId: '', invoiceAddress: '', invoicePhone: '', bankName: '', bankAccount: '', mailingAddress: '', businessAddress: '', businessLicenseFiles: [], fillingLicenseFiles: [] }; } function h2GetRegionByProvince(province) { var a = String(province || '').trim(); if (!a) return ''; if (a === '北京市' || a === '天津市' || a === '河北省' || a === '山西省' || a === '内蒙古') return '华北'; if (a === '上海市' || a === '江苏省' || a === '浙江省' || a === '安徽省' || a === '福建省' || a === '江西省' || a === '山东省') return '华东'; if (a === '广东省' || a === '广西省' || a === '海南省') return '华南'; if (a === '河南省' || a === '湖北省' || a === '湖南省') return '华中'; if (a === '辽宁省' || a === '吉林省' || a === '黑龙江省') return '东北'; if (a === '四川省' || a === '云南省' || a === '贵州省' || a === '重庆市') return '西南'; if (a === '陕西省' || a === '甘肃省' || a === '青海省' || a === '宁夏' || a === '新疆') return '西北'; return ''; } function h2SupplierToForm(s) { if (!s) return h2CreateEmptySupplierForm(); return { signingCompany: s.signingCompany || undefined, name: s.name || '', city: s.city ? s.city.slice() : undefined, address: s.address || '', region: s.region || '', dept: s.dept || '', manager: s.manager || '', remark: s.remark || '', contactName: s.contactName || '', contactMobile: s.contactMobile || '', contactTitle: s.contactTitle || '', taxId: s.taxId || '', invoiceAddress: s.invoiceAddress || '', invoicePhone: s.invoicePhone || '', bankName: s.bankName || '', bankAccount: s.bankAccount || '', mailingAddress: s.mailingAddress || '', businessAddress: s.businessAddress || s.mailingAddress || '', businessLicenseFiles: (s.businessLicenseFiles || []).map(function (f, i) { return Object.assign({}, f, { uid: f.uid || ('bl-' + i), status: 'done' }); }), fillingLicenseFiles: (s.fillingLicenseFiles || []).map(function (f, i) { return Object.assign({}, f, { uid: f.uid || ('fl-' + i), status: 'done' }); }) }; } var H2_STATION_USER_MANUAL_DOC = `# 加氢站管理 · 站点信息 — 用户使用说明书 | 项目 | 内容 | |------|------| | 文档版本 | v1.0 | | 产品模块 | 加氢站管理 → 站点信息 | | 文档类型 | **用户使用说明书** | | 适用读者 | 加氢站运营、财务结算、平台管理员、培训与汇报人员 | | 关联模块 | 台账数据 → 车辆氢费明细 | | 配图目录 | 与本文件同级的 docs/ 文件夹 | --- ## 使用本说明书 本说明书用于**教会一线用户操作**站点管理模块,也可作为**项目汇报、培训宣讲**的配套材料。建议按以下顺序阅读: 1. **第二章** — 认识页面布局(5 分钟) 2. **第四章** — 新建 / 编辑 / 导入站点(15 分钟) 3. **第七章** — 生成对账单与结算(重点,20 分钟) 4. **第九章** — 汇报话术与常见问题 页面右上角可点击 **「查看使用说明书」** 随时打开本文档。 --- ## 一、模块能做什么 ### 1.1 一句话说明 站点信息模块是加氢站的**主数据与运营台账中心**:维护站点档案、营业与成本价,查看加氢量与预付余额,并按周期与加氢站完成**氢费对账、收票登记与付款闭环**。 ### 1.2 功能地图 \`\`\`mermaid mindmap root((站点信息)) 查询统计 筛选条件 KPI 分类卡 列表排序分页 主数据 新建站点 编辑站点 查看详情 批量导入 删除站点 运营配置 营业状态 成本价格 数据分析 加氢量下钻 预付余额下钻 财务结算 生成对账单 查看对账记录 \`\`\` ### 1.3 谁来做什么事 | 角色 | 常用功能 | 注意事项 | |------|----------|----------| | **平台管理员(admin)** | 新建、编辑、批量导入、删除、绑定系统账号 | 系统账号支持多选绑定 | | **加氢站运营账号** | 编辑本站资料、营业状态、价格配置 | 编辑时**不能改**供应商与系统账号 | | **财务 / 结算人员** | 生成对账单、登记收票、查看对账记录 | 需先在台账完成「已对账」 | --- ## 二、进入模块与页面总览 ### 2.1 菜单路径 左侧导航:**加氢站管理 → 站点信息** ### 2.2 页面结构(自上而下) \`\`\` ┌─────────────────────────────────────────────────────────────┐ │ 面包屑:加氢站管理 > 站点信息 [查看需求说明][使用说明书] │ ├─────────────────────────────────────────────────────────────┤ │ 【筛选条件】名称 | 是否签约 | 地区 | 营业状态 [重置][查询] │ ├─────────────────────────────────────────────────────────────┤ │ KPI:全部 | 已签约 | 未签约 | 高频 | 低频 | 无加氢 │ ├─────────────────────────────────────────────────────────────┤ │ 工具栏:[批量导入] [新建站点] │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 站点列表(名称/签约/营业/价格/加氢量/余额/操作) │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ \`\`\` ![图 2-1 站点列表页总览示意](docs/h2-station-manual-list-page.png) ### 2.3 列表字段速查 | 列名 | 怎么用 | |------|--------| | 加氢站名称 | 含「已签约」标签;下方显示完整地址 | | 合作时间 | 未签约显示为空 | | 营业状态 / 营业时间 | 只读;在「更多 → 营业状态」修改 | | 当前成本价格 | 元/kg;由价格配置生效时间驱动 | | 加氢次数 / 加氢量 | **点击加氢量**可下钻明细 | | 预付余额 | **点击余额**可下钻流水;负数标红「已欠费」 | | 操作 | **查看** + **更多**(⋮) | --- ## 三、日常查询与 KPI 筛选 ### 3.1 操作流程 \`\`\`mermaid flowchart LR A[输入筛选条件] --> B[点击查询] B --> C[列表刷新] D[点击 KPI 卡] --> C E[点击重置] --> F[清空条件] F --> B \`\`\` ### 3.2 操作步骤 1. 在「筛选条件」卡片填写一个或多个条件。 2. 点击 **查询**;列表按条件过滤,并回到第 1 页。 3. 点击 **重置** 可清空所有筛选。 4. 也可直接点击 KPI 卡片快速筛选: - **全部站点** — 显示所有 - **已签约 / 未签约** — 按签约状态 - **高频站点** — 加氢次数 ≥ 3 - **低频站点** — 加氢次数 1~2 - **无加氢站点** — 加氢次数 = 0 ### 3.3 列表排序与分页 - 点击 **加氢次数**、**加氢量** 列表头可升序 / 降序排序。 - 底部分页默认每页 10 条,可切换 5 / 10 / 20 / 50。 --- ## 四、站点主数据维护 ### 4.1 整体流程 \`\`\`mermaid flowchart TB subgraph 入口 N[新建站点] --> CP[整页新建表单] I[批量导入] --> IM[上传模板文件] E[更多-编辑] --> EP[整页编辑表单] V[查看] --> VM[详情弹窗] D[更多-删除] --> DM[确认删除] end CP --> S[提交创建] EP --> U[保存修改] IM --> CI[确认导入] \`\`\` ![图 4-1 新建/编辑整页表单示意](docs/h2-station-manual-create-page.png) ### 4.2 新建站点 **入口**:列表工具栏 → **新建站点** 整页表单分三块卡片: #### (1)加氢站基本信息 | 字段 | 必填 | 说明 | |------|------|------| | 加氢站名称 | 是 | 不可与已有站点重名 | | 省 / 市、详细地址 | 是 | 可用「地址解析」自动解析 | | 联系人、联系电话 | 是 | 手机或固话 | | 是否签约 | 否 | 选「签约站点」后须填合作时间与合同附件 | #### (2)系统账号绑定 1. 选择 **绑定系统账号** 或 **暂不绑定**。 2. 若绑定:在「选择系统账号」中**多选**账号(已绑定其他站点的不可选)。 3. 下方可预览已选账号的登录名、姓名、角色、手机号。 > 绑定后,所选账号可登录系统并管理该站点。 #### (3)供应商相关信息 - **关联已有供应商** — 搜索档案绑定 - **新增供应商** — 填写主体、开票、银行账户、营业执照与其他证件 **底部操作**: - **返回 / 取消** — 有未保存内容时会二次确认 - **提交创建** — 底部进度条达 100% 后可提交(新建页**无「重置」按钮**) ### 4.3 编辑站点 **入口**:列表操作 → **更多(⋮)→ 编辑** 编辑页与新建页**同布局整页**,但有以下差异: | 项目 | 编辑页规则 | |------|------------| | 供应商信息 | **不展示**,不可修改 | | 系统账号 | **仅 admin 可改**;加氢站账号登录时为只读展示 | | 营业状态 | 不在此页维护,请用「更多 → 营业状态」 | | 底部按钮 | **保存修改**(无重置) | \`\`\`mermaid flowchart TD A[打开编辑页] --> B{当前登录角色?} B -->|admin| C[可改基本信息 + 系统账号多选] B -->|加氢站账号| D[仅可改基本信息] D --> E[系统账号只读展示] C --> F[保存修改] D --> F \`\`\` ### 4.4 查看站点 **入口**:列表操作 → **查看** 弹窗分四块只读信息: 1. 加氢站信息(含签约、营业时间等) 2. 系统账号(支持展示多个绑定账号) 3. 供应商信息 4. 付款信息 ### 4.5 删除站点 **入口**:列表操作 → **更多 → 删除** 弹出确认框,确认后从列表移除,**不可撤销**。 ### 4.6 批量导入 **入口**:列表工具栏 → **批量导入** \`\`\`mermaid flowchart LR A[下载 CSV 模板] --> B[按模板填写] B --> C[上传 csv/xlsx/xls] C --> D[系统校验预览] D --> E{可导入条数>0?} E -->|是| F[确认导入] E -->|否| G[修正文件重传] \`\`\` **注意**: - 站点名称不能与现有台账重复 - 模板字段与新建表单一致 - 上传后查看「可导入 / 不可导入」条数与错误原因 --- ## 五、运营配置 ### 5.1 营业状态 **入口**:更多 → **营业状态** \`\`\` ┌──────────────────────────────────────┐ │ 站点信息卡(名称、地址、当前状态) │ ├──────────────────────────────────────┤ │ 营业状态:[营业中][暂停营业][停止营业] │ │ 营业时间:[全天营业][自定义时段] │ ├──────────────────────────────────────┤ │ 营业状态变更记录(历史列表) │ │ [取消] [保存] │ └──────────────────────────────────────┘ \`\`\` **操作要点**: 1. 营业状态、营业时间均用**按钮组**点选(非下拉)。 2. 非「全天营业」时,必须填写开始与结束时间(HH:mm)。 3. 保存后列表「营业状态 / 营业时间」同步更新;若状态变更会写入记录表。 ### 5.2 价格配置 **入口**:更多 → **价格配置** 1. 填写 **成本价格(元/kg)** 与 **生效时间**。 2. 保存后: - 若已到生效时间 → 列表「当前成本价格」**立即更新** - 若未到生效时间 → 到点后自动更新 3. 下方展示价格调整记录(操作人、调整前后价格、生效时间)。 --- ## 六、数据下钻分析 ### 6.1 加氢量下钻 **入口**:列表点击 **加氢量** 数字 弹窗内容: - 统计卡:加氢次数、加氢总量(kg)、成本总价、加氢总价 - 明细表:字段对齐「车辆氢费明细」 - **导出 CSV** 按钮 ### 6.2 预付余额下钻 **入口**:列表点击 **预付余额** 数字 弹窗内容: - 当前余额(负数显示「已欠费」) - 充值 / 车辆加氢流水明细 - **导出 CSV** 按钮 --- ## 七、对账与结算(核心流程) 本流程与 **车辆氢费明细** 联动,完成站点侧付款闭环。 ### 7.1 前置条件(必知) 生成对账单前,台账中须已有满足以下**全部**条件的记录: 1. 对账状态 = **已对账**(业务员已在台账点击「完成对账」) 2. **尚未**被本站点历史对账单结算过 3. 加氢时间在所选账单日期区间内 4. 加氢站名称与当前站点一致 > 对账单只展示**成本侧**字段(加氢量、成本单价、成本总价),不展示客户加氢价。 ### 7.2 生成对账单 — 两阶段操作 **入口**:更多 → **生成对账单** ![图 7-1 生成对账单两阶段流程示意](docs/h2-station-manual-statement-flow.png) \`\`\`mermaid flowchart TD A[打开生成对账单] --> B[选择账单开始/结束日期] B --> C[查看上次对账单结束时间] C --> D[点击生成对账单] D -->|无明细| E[提示调整日期] D -->|有明细| F[进入结算阶段] F --> G[查看统计卡与成本明细表] G --> H[填写收票日期/金额/发票附件] H --> I[点击提交对账单] I --> J[更新余额 + 写对账历史 + 回写台账] \`\`\` #### 阶段一:选期生成 | 步骤 | 操作 | |------|------| | 1 | 选择 **账单开始日期**、**账单结束日期** | | 2 | 参考「上次对账单结束时间」(无历史显示「暂无」) | | 3 | 点击 **生成对账单** | | 4 | 成功后进入阶段二,**日期锁定不可改** | #### 阶段二:结算提交 **统计卡**(明细表上方): | 指标 | 含义 | |------|------| | 加氢次数 | 本账单包含的记录笔数 | | 加氢总量 | kg 合计 | | 成本总金额 | 成本总价合计 | **结算表单**(必填): | 字段 | 规则 | |------|------| | 结算后加氢站预付款余额 | **只读**,= 当前余额 − 成本总金额 | | 收票日期 | 必填,与收票金额同一行 | | 收票金额 | 必填,带 ¥ 前缀;默认等于成本总金额 | | 发票附件 | 必填,支持上传 | 点击 **提交对账单** 后系统将: 1. 写入对账历史(供后续查询) 2. 扣减站点预付余额 3. 标记台账记录已结算(防重复入账) 4. 回写车辆氢费明细:对账日期、收票日期、加氢站付款状态 → **已付款** \`\`\`mermaid sequenceDiagram participant 业务 as 台账业务员 participant 财务 as 财务操作人 participant 站点 as 站点信息 participant 台账 as 车辆氢费明细 业务->>台账: 完成对账 → 已对账 财务->>站点: 更多 → 生成对账单 站点->>台账: 拉取已对账且未结算记录 财务->>站点: 填写收票信息并提交 站点->>站点: 更新预付余额 站点->>台账: 回写已付款 \`\`\` ### 7.3 查看对账记录 **入口**:更多 → **查看对账记录** 历史列表字段:对账日期、对账人、账单区间、加氢次数、加氢金额、对账后余额、操作。 点击 **查看明细** 可看到: - 账单区间与统计三卡 - 收票时间、收票金额 - 发票附件下载 - 成本明细表 --- ## 八、操作列「更多」菜单速查 | 菜单项 | 作用 | |--------|------| | 编辑 | 整页编辑站点主数据 | | 营业状态 | 维护营业状态与营业时间 | | 价格配置 | 设置成本价与生效时间 | | 生成对账单 | 两阶段对账结算 | | 查看对账记录 | 历史对账单与明细 | | 删除 | 删除站点(不可恢复) | --- ## 九、汇报与培训话术要点 向领导或客户汇报时,可围绕 **「一个中心、三条主线」** 组织: **一个中心**:站点全生命周期台账(档案 → 运营 → 结算) **三条主线**: 1. **主数据线** — 新建 / 导入 / 编辑,保证站点档案完整准确 2. **运营线** — 营业状态、成本价格、加氢量与余额下钻,支撑日常经营分析 3. **结算线** — 台账已对账 → 站点生成对账单 → 收票登记 → 余额扣减 → 台账已付款,形成闭环 **可量化成果表述示例**: - 支持按 KPI(高频/低频/无加氢)快速识别运营薄弱站点 - 对账单两阶段设计,避免选错账期;提交后自动更新余额并防重复结算 - 多账号绑定 + 角色权限,满足平台管理与站点自治并存 --- ## 十、常见问题 | 问题 | 解答 | |------|------| | 生成对账单提示无记录? | 检查台账是否「已对账」、日期区间是否正确、是否已被历史对账单结算 | | 编辑时看不到供应商? | 编辑页 intentionally 不展示供应商,需在供应商模块或新建时维护 | | 加氢站账号改不了系统账号? | 仅 admin 可修改绑定;加氢站账号只能查看 | | 预付余额为负还能提交吗? | 可以;系统标「已欠费」,提醒后续充值 | | 批量导入失败? | 检查名称重复、营业状态枚举、日期格式;建议用 CSV UTF-8 | --- ## 十一、快速检查清单 培训验收时可按此清单实操一遍: - [ ] 用筛选 + KPI 找到目标站点 - [ ] 新建一个站点(含供应商 + 多账号绑定) - [ ] 用加氢站角色编辑,确认账号只读 - [ ] 用 admin 编辑,调整系统账号绑定 - [ ] 配置营业状态与成本价格 - [ ] 下钻加氢量与预付余额并导出 CSV - [ ] 完成一次「生成对账单 → 提交」全流程 - [ ] 在「查看对账记录」中核对明细与发票信息 --- **文档结束** `; var H2_STATION_REQUIREMENT_DOC = `# 加氢站管理 · 站点信息 — 产品需求说明(PRD) | 项目 | 内容 | |------|------| | 文档版本 | v1.0 | | 产品模块 | 加氢站管理 → 站点信息 | | 文档类型 | 产品需求说明 | | 适用读者 | 研发、测试、产品、项目 | | 关联模块 | 台账数据 → 车辆氢费明细 | --- ## 一、为什么做这件事 ### 1.1 业务背景 加氢站是氢费成本结算的关键节点。运营侧需要维护站点主数据(签约、营业、成本价、预付余额),并在财务周期内按站点与加氢站完成对账、收票与付款闭环。 ### 1.2 本期目标 建设 Web 端「站点信息」页面,支撑: - 站点台账的查询、新建、编辑、查看、删除与批量导入 - 营业状态、成本价格、预付余额等运营配置 - **按站点生成氢费对账单 → 填写收票信息 → 提交结算**,并与「车辆氢费明细」联动回写 ### 1.3 用户角色 | 角色 | 典型诉求 | |------|----------| | **加氢站运营** | 维护站点资料、营业与价格;查看加氢量与余额 | | **财务/结算** | 按周期生成对账单、登记收票、完成站点付款闭环 | | **管理员** | 批量导入站点、删除无效站点 | --- ## 二、页面整体结构 用户进入「加氢站管理 → 站点信息」后,页面自上而下为: 1. **筛选区** — 名称、签约、地区、营业状态 2. **KPI 分类** — 全部 / 高频 / 低频 / 无加氢;已签约快捷筛选 3. **站点列表** — 主数据 + 运营指标 + 操作列 4. **弹窗/抽屉** — 查看、编辑、营业状态、价格配置、对账单、对账记录等 \`\`\`mermaid flowchart TB subgraph 列表页 A[筛选 / KPI] --> B[站点列表] B --> C[查看站点] B --> D[编辑 / 删除] B --> E[更多菜单] end E --> F[营业状态] E --> G[价格配置] E --> H[生成对账单] E --> I[查看对账记录] B --> J[加氢量下钻] B --> K[预付余额下钻] \`\`\` --- ## 三、列表与 KPI 规则 ### 3.1 列表字段 | 字段 | 说明 | |------|------| | 加氢站名称 | 含签约标签;名称下一行展示省市区+详细地址 | | 合作时间 | 未签约可空 | | 营业状态 / 营业时间 | 列表只读;在「更多-营业状态」维护 | | 当前成本价格 | 元/kg;受价格配置生效时间驱动 | | 加氢次数 / 加氢量 | 加氢量可点击下钻;列表头支持排序 | | 预付余额 | 可点击下钻流水;负值标红并显示「已欠费」 | | 联系方式 | 联系人 + 电话 | | 操作 | 查看 + 更多 | ### 3.2 KPI 划分逻辑 | 分类 | 规则 | |------|------| | 高频站点 | 加氢次数 ≥ 3 | | 低频站点 | 加氢次数 1~2 | | 无加氢站点 | 加氢次数 = 0 | 筛选或 KPI 切换后列表回到第 1 页;默认分页 10 条,可选 5/10/20/50。 --- ## 四、站点主数据(新建 / 编辑 / 查看) ### 4.1 新建站点 同页内嵌视图,包含: - **基本信息**:名称、地址、营业时间、签约、联系方式 - **系统账号绑定**:可选绑定,绑定后该账号可登录管理对应站点 - **供应商信息**:新建或关联已有供应商、付款信息 ### 4.2 编辑 / 查看 | 模式 | 交互 | |------|------| | 编辑 | 整页(与新建同结构);不含供应商、不含营业状态;系统账号仅 admin 可改,支持多选 | | 查看 | 弹窗分块:加氢站信息、系统账号、供应商信息、付款信息 | ### 4.3 批量导入 - 下载 CSV 模板 → 上传 .csv / .xlsx / .xls - 站点名称不可与现有台账重复;解析预览后确认导入 --- ## 五、运营配置 ### 5.1 营业状态 - 卡片式弹窗:营业状态、营业时间均为**按钮组** - 非「全天营业」须配置开始/结束时段 - 保存时校验时段完整性;下方展示状态变更记录 ### 5.2 价格配置 - 配置**成本价格(元/kg)**与**生效时间** - 到达生效时间后自动刷新列表「当前成本价格」 - 下方展示价格调整记录(操作人、调整前后价格、生效时间) ### 5.3 加氢量 / 预付余额下钻 **加氢量下钻**:统计卡(次数、加氢量、成本总价、加氢总价)+ 明细表 + 导出 CSV,字段对齐车辆氢费明细。 **预付余额下钻**:充值/车辆加氢流水 + 导出 CSV;余额为负显示已欠费。 --- ## 六、核心流程:生成对账单(重点) 本模块与「车辆氢费明细」配合完成**站点侧氢费结算闭环**。研发需重点理解以下逻辑。 ### 6.1 数据来源 对账单明细取自 **车辆氢费明细** 中同时满足以下条件的记录: 1. 对账状态 = **已对账**(业务侧已在台账完成「完成对账」) 2. **尚未**被本站点历史对账单结算过(未绑定 statementRecordId) 3. 加氢时间落在用户选择的 **[账单开始日期, 账单结束日期]** 内 4. 加氢站名称与当前操作站点一致 > 对账单明细**仅展示成本字段**(加氢量、成本单价、成本总价等),不展示加氢单价、加氢总价。 ### 6.2 两阶段交互 \`\`\`mermaid flowchart TD A[打开「生成对账单」] --> B[选择账单开始/结束日期] B --> C[展示上次对账单结束时间] C --> D{点击「生成对账单」} D -->|无符合条件记录| E[提示调整日期] D -->|有记录| F[生成对账记录草稿] F --> G[展示统计:加氢次数/总量/成本总金额] G --> H[展示成本明细表] H --> I[填写结算信息] I --> J{点击「提交对账单」} J -->|校验失败| K[提示必填项] J -->|校验通过| L[完成闭环] \`\`\` **阶段一 · 选期生成** - 日期区下方展示:**上次对账单结束时间 YYYY-MM-DD**(无历史则「暂无」) - 默认开始日期建议 = 上次结束日 + 1 天 - 点击「生成对账单」后进入阶段二,日期锁定不可改 **阶段二 · 结算提交** 统计卡(明细表上方): | 指标 | 说明 | |------|------| | 加氢次数 | 本账单包含的已对账记录笔数 | | 加氢总量 | kg 合计 | | 成本总金额 | 成本总价合计 | 明细表下方**必填**结算项: | 字段 | 规则 | |------|------| | 结算后加氢站预付款余额 | **只读**,= 当前预付余额 − 成本总金额;不足为负值并标「已欠费」 | | 收票日期 | 必填,与收票金额同一行展示 | | 收票金额 | 必填,输入框带 ¥ 前缀;默认填入成本总金额 | | 发票附件 | 必填,支持上传 | ### 6.3 提交后的系统行为(关键) 提交成功后需**原子性**完成: 1. **写入对账历史**(供「查看对账记录」查询) 2. **扣减站点预付余额** = 结算后余额 3. **标记台账记录已结算**(绑定 statementRecordId,避免重复入账) 4. **回写车辆氢费明细**(原型通过 window 补丁 / API 同步): | 车辆氢费明细字段 | 回写值 | |------------------|--------| | 对账日期 | 操作完成时间,格式 YYYY-MM-DD HH:MM | | 收票日期 | 操作完成日期,格式 YYYY-MM-DD | | 加氢站付款状态 | 已付款 | \`\`\`mermaid sequenceDiagram participant 台账 as 车辆氢费明细 participant 站点 as 站点信息 participant 财务 as 结算操作人 台账->>台账: 业务员「完成对账」→ 已对账 财务->>站点: 更多 → 生成对账单 站点->>台账: 拉取已对账且未结算记录 财务->>站点: 生成对账记录 + 填收票信息 财务->>站点: 提交对账单 站点->>站点: 更新预付余额 / 写对账历史 站点->>台账: 回写对账日期、收票日期、已付款 \`\`\` --- ## 七、查看对账记录 入口:列表操作列 → 更多 → **查看对账记录** ### 7.1 历史列表字段 | 列 | 说明 | |----|------| | 对账日期 | 提交对账单的操作时间 | | 对账人 | 当前操作人 | | 账单开始/结束日期 | 本次账单覆盖区间 | | 加氢次数 | 本单笔数 | | 加氢金额 | 成本总金额 | | 对账后加氢站预付款余额 | 结算后余额;负值标「已欠费」 | | 操作 | 查看明细 | ### 7.2 查看明细 弹窗展示: - 账单区间、加氢统计三卡 - **收票时间、收票金额**(同一信息区) - **发票附件下载** - 成本明细表(同生成对账单字段范围) --- ## 八、校验与异常 | 场景 | 处理 | |------|------| | 日期未填 / 开始晚于结束 | 阻止生成,提示用户 | | 区间内无已对账未结算记录 | 阻止生成,提示调整日期 | | 收票日期/金额/附件未填 | 阻止提交 | | 收票金额 ≤ 0 | 阻止提交 | | 预付余额结算后为负 | 允许提交,列表与明细标「已欠费」 | --- ## 九、研发实现要点(给开发) 1. **对账单幂等**:同一笔已对账记录只能进入一次成功提交的对账单(statementRecordId 约束)。 2. **上次结束时间**:取该站点已成功提交对账单的最大 endDate 字段。 3. **余额计算**:balanceAfter = prepaidBalance - sum(costTotal),提交后写回站点主数据。 4. **跨模块同步**:生产环境应对接统一台账服务;原型使用 H2_STATION_STATEMENT_LEDGER_UPDATES / H2_VEHICLE_LEDGER_API 模拟回写。 5. **明细字段范围**:对账相关弹窗统一不展示客户侧加氢单价/总价,避免与成本结算混淆。 6. **权限**(后续迭代):生成/提交对账单建议限制财务或站点管理员角色。 --- ## 十、本期不做 - 对账单审批流、ERP 自动过账 - 发票 OCR 识别与验真 - 多币种 / 多税率 - 按组织架构的复杂数据权限(本期按功能原型描述) --- **文档结束** `; function h2StationParsePrdInlineText(text) { var parts = String(text || '').split(/(\*\*[^*]+\*\*)/g); var nodes = []; var i; for (i = 0; i < parts.length; i++) { var p = parts[i]; if (!p) continue; if (p.indexOf('**') === 0 && p.lastIndexOf('**') === p.length - 2) { nodes.push(React.createElement('strong', { key: i }, p.slice(2, -2))); } else { nodes.push(p); } } return nodes.length === 1 ? nodes[0] : nodes; } function h2StationIsPrdTableRow(line) { return /^\|.+\|$/.test(String(line || '').trim()); } function h2StationIsPrdTableSep(line) { return /^\|[\s\-:|]+\|$/.test(String(line || '').trim()); } function h2StationRenderPrdTableRow(line, rowKey, isHeader) { var cells = String(line).trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(function (c) { return c.trim(); }); return React.createElement('tr', { key: rowKey }, cells.map(function (cell, ci) { var Tag = isHeader ? 'th' : 'td'; return React.createElement(Tag, { key: ci, style: { border: '1px solid #e5e7eb', padding: '8px 10px', textAlign: 'left', verticalAlign: 'top', fontWeight: isHeader ? 600 : 400, background: isHeader ? '#f8fafc' : '#fff', fontSize: 13, lineHeight: 1.5 } }, h2StationParsePrdInlineText(cell)); }) ); } function h2StationRenderPrdMarkdown(markdown) { var lines = String(markdown || '').split(/\r?\n/); var nodes = []; var i = 0; var inCode = false; var codeBuf = []; while (i < lines.length) { var line = lines[i]; var trimmed = String(line || '').trim(); if (trimmed.indexOf('```') === 0) { if (inCode) { nodes.push(React.createElement('pre', { key: 'code-' + i, style: { margin: '12px 0', padding: '12px 14px', background: '#f6f8fa', border: '1px solid #e5e7eb', borderRadius: 8, fontSize: 12, lineHeight: 1.6, overflow: 'auto', color: '#334155', whiteSpace: 'pre-wrap' } }, codeBuf.join('\n'))); codeBuf = []; inCode = false; } else { inCode = true; } i += 1; continue; } if (inCode) { codeBuf.push(line); i += 1; continue; } if (trimmed === '---') { nodes.push(React.createElement('hr', { key: 'hr-' + i, style: { border: 'none', borderTop: '1px solid #e8ecf0', margin: '20px 0' } })); i += 1; continue; } if (h2StationIsPrdTableRow(trimmed)) { var tableLines = []; while (i < lines.length && h2StationIsPrdTableRow(String(lines[i]).trim())) { tableLines.push(String(lines[i]).trim()); i += 1; } var bodyRows = []; var ti; for (ti = 0; ti < tableLines.length; ti++) { if (h2StationIsPrdTableSep(tableLines[ti])) continue; bodyRows.push(h2StationRenderPrdTableRow(tableLines[ti], 'tr-' + i + '-' + ti, ti === 0)); } if (bodyRows.length) { nodes.push(React.createElement('div', { key: 'tbl-' + i, style: { overflowX: 'auto', margin: '12px 0 16px' } }, React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } }, React.createElement('tbody', null, bodyRows) ) )); } continue; } if (!trimmed) { i += 1; continue; } if (trimmed.indexOf('# ') === 0) { nodes.push(React.createElement('h1', { key: 'h1-' + i, style: { fontSize: 20, fontWeight: 700, color: '#0f172a', margin: '0 0 16px', lineHeight: 1.35 } }, h2StationParsePrdInlineText(trimmed.slice(2).trim()))); i += 1; continue; } if (trimmed.indexOf('## ') === 0) { nodes.push(React.createElement('h2', { key: 'h2-' + i, style: { fontSize: 16, fontWeight: 700, color: '#1e293b', margin: '24px 0 12px', paddingBottom: 6, borderBottom: '2px solid #e0f2fe', lineHeight: 1.4 } }, h2StationParsePrdInlineText(trimmed.slice(3).trim()))); i += 1; continue; } if (trimmed.indexOf('### ') === 0) { nodes.push(React.createElement('h3', { key: 'h3-' + i, style: { fontSize: 14, fontWeight: 600, color: '#334155', margin: '16px 0 8px', lineHeight: 1.45 } }, h2StationParsePrdInlineText(trimmed.slice(4).trim()))); i += 1; continue; } if (/^!\[([^\]]*)\]\(([^)]+)\)$/.test(trimmed)) { var imgMatch = trimmed.match(/^!\[([^\]]*)\]\(([^)]+)\)$/); var imgAlt = imgMatch[1] || '配图'; var imgSrc = imgMatch[2] || ''; nodes.push(React.createElement('figure', { key: 'img-' + i, style: { margin: '16px 0 20px', textAlign: 'center' } }, React.createElement('img', { src: imgSrc, alt: imgAlt, style: { maxWidth: '100%', borderRadius: 10, border: '1px solid #e5e7eb', boxShadow: '0 4px 12px rgba(15,23,42,0.06)' }, onError: function (e) { if (e && e.target) { e.target.style.display = 'none'; } } }), React.createElement('figcaption', { style: { fontSize: 12, color: '#94a3b8', marginTop: 8, lineHeight: 1.5 } }, imgAlt) )); i += 1; continue; } if (trimmed === '**文档结束**') { nodes.push(React.createElement('div', { key: 'end-' + i, style: { marginTop: 24, paddingTop: 16, borderTop: '1px dashed #e2e8f0', color: '#94a3b8', fontSize: 13, textAlign: 'center' } }, '— 文档结束 —')); i += 1; continue; } if (/^\d+\.\s/.test(trimmed)) { nodes.push(React.createElement('div', { key: 'ol-' + i, style: { fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '6px 0 6px 4px', paddingLeft: 4 } }, h2StationParsePrdInlineText(trimmed))); i += 1; continue; } if (trimmed.indexOf('- ') === 0) { nodes.push(React.createElement('div', { key: 'ul-' + i, style: { display: 'flex', gap: 8, fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '4px 0 4px 2px' } }, React.createElement('span', { style: { color: '#1677ff', flexShrink: 0 } }, '•'), React.createElement('span', { style: { flex: 1 } }, h2StationParsePrdInlineText(trimmed.slice(2).trim())) )); i += 1; continue; } if (trimmed.indexOf('> ') === 0) { nodes.push(React.createElement('div', { key: 'quote-' + i, style: { margin: '8px 0', padding: '8px 12px', borderLeft: '3px solid #c4b5fd', background: '#faf5ff', borderRadius: '0 8px 8px 0', fontSize: 13, color: '#5b21b6', lineHeight: 1.65 } }, h2StationParsePrdInlineText(trimmed.slice(2).trim()))); i += 1; continue; } nodes.push(React.createElement('p', { key: 'p-' + i, style: { fontSize: 13, color: '#475569', lineHeight: 1.75, margin: '6px 0' } }, h2StationParsePrdInlineText(trimmed))); i += 1; } return nodes; } function renderH2StationRequirementDocPanel() { return React.createElement('div', { className: 'h2-req-doc-panel', style: { padding: '4px 4px 16px' } }, h2StationRenderPrdMarkdown(H2_STATION_REQUIREMENT_DOC) ); } function renderH2StationUserManualDocPanel() { return React.createElement('div', { className: 'h2-req-doc-panel h2-user-manual-panel', style: { padding: '4px 4px 16px' } }, h2StationRenderPrdMarkdown(H2_STATION_USER_MANUAL_DOC) ); } var H2_PRIMARY_BTN_STYLE = { borderRadius: 8, fontWeight: 600, background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', border: 'none' }; var H2_REQ_BTN_STYLE = { borderRadius: 8, border: '1px solid #cbd5e1', fontWeight: 600, display: 'inline-flex', alignItems: 'center', boxShadow: '0 1px 2px rgba(0,0,0,0.05)', color: '#475569' }; function h2ResolveRecordSupplierMode(record) { if (!record) return 'link'; if (record.supplierMode === 'none') return 'none'; if (record.supplierMode === 'link' || record.linkedSupplierId) return 'link'; if (record.supplierMode === 'new') return 'new'; var supplier = h2ResolveStationSupplier(record); if (supplier && (supplier.name || '').trim()) return 'new'; return 'none'; } function h2SupplierModeLabel(mode) { if (mode === 'link') return '关联已有供应商'; if (mode === 'new') return '新增供应商'; if (mode === 'none') return '不关联供应商'; return '—'; } function h2CreateFormDirty(station, supplier, supplierMode, linkedSupplierId) { if ((station.name || '').trim()) return true; if ((station.contact || '').trim()) return true; if ((station.mobilePhone || '').trim() || (station.landlinePhone || '').trim()) return true; if (station.address && ((station.address.region && station.address.region.length) || (station.address.detail || '').trim())) return true; if (supplierMode !== 'none') { if ((supplier.signingCompany || '').trim()) return true; if ((supplier.name || '').trim()) return true; if (supplierMode === 'link' && linkedSupplierId) return true; if ((supplier.businessLicenseFiles || []).length || (supplier.fillingLicenseFiles || []).length) return true; } if ((station.contractFiles || []).length) return true; if (supplierMode !== 'none' && station.bindAccountMode === 'bind' && station.bindAccountIds && station.bindAccountIds.length) return true; return false; } function h2EditFormDirty(station, originForm) { if (!originForm) return h2CreateFormDirty(station, h2CreateEmptySupplierForm(), 'new', undefined); var keys = ['name', 'contact', 'mobilePhone', 'landlinePhone', 'businessHours', 'isSigned', 'contractStart', 'contractEnd', 'bindAccountMode']; var i; for (i = 0; i < keys.length; i++) { if (String(station[keys[i]] || '') !== String(originForm[keys[i]] || '')) return true; } if (JSON.stringify(station.address || {}) !== JSON.stringify(originForm.address || {})) return true; if (JSON.stringify(station.bindAccountIds || []) !== JSON.stringify(originForm.bindAccountIds || [])) return true; if ((station.contractFiles || []).length !== (originForm.contractFiles || []).length) return true; return false; } function h2EditFormProgress(station) { var checks = [ !!(station.name || '').trim(), station.address && station.address.region && station.address.region.length >= 2 && !!(station.address.detail || '').trim(), !!(station.contact || '').trim(), !!((station.mobilePhone || '').trim() || (station.landlinePhone || '').trim()) ]; var done = checks.filter(Boolean).length; return Math.round((done / checks.length) * 100); } function h2CreateFormProgress(station, supplier, supplierMode, linkedSupplierId) { var checks = [ !!(station.name || '').trim(), station.address && station.address.region && station.address.region.length >= 2 && !!(station.address.detail || '').trim(), !!(station.contact || '').trim(), !!((station.mobilePhone || '').trim() || (station.landlinePhone || '').trim()), supplierMode === 'none' ? true : (supplierMode === 'link' ? !!linkedSupplierId : !!(supplier.name || '').trim()) ]; var done = checks.filter(Boolean).length; return Math.round((done / checks.length) * 100); } function h2CardTitleWithStep(step, text) { return React.createElement('span', { className: 'h2-card-title-bar h2-card-title-bar--step' }, React.createElement('span', { className: 'h2-card-step-badge', 'aria-hidden': true }, step), React.createElement('span', { className: 'h2-card-title-text' }, text) ); } function h2MaskMobile(mobile) { if (!mobile) return '—'; var s = String(mobile); if (s.length >= 11) return s.slice(0, 3) + '****' + s.slice(-4); return s; } function h2AccountRoleTagColor(role) { var text = String(role || ''); if (text.indexOf('管理') >= 0) return 'success'; if (text.indexOf('运营') >= 0) return 'processing'; return 'default'; } function h2BuildStationCreateView(ctx) { var antd = window.antd; var Card = antd.Card; var Button = antd.Button; var Input = antd.Input; var Select = antd.Select; var Cascader = antd.Cascader; var Radio = antd.Radio; var DatePicker = antd.DatePicker; var Upload = antd.Upload; var Form = antd.Form; var Row = antd.Row; var Col = antd.Col; var Tag = antd.Tag; var Divider = antd.Divider; var dayjs = window.dayjs; var inputCls = 'h2-create-input'; var pageMode = ctx.pageMode || 'create'; var isEdit = pageMode === 'edit'; var showSupplier = ctx.showSupplier !== false; var accountEditable = ctx.accountEditable !== false; var stationAddr = ctx.station.address || { region: [], detail: '' }; var bindAccountIds = ctx.station.bindAccountIds || []; var showAccountBind = ctx.supplierMode !== 'none'; var formItem = function (label, required, node, extra) { return React.createElement(Form.Item, { label: required ? React.createElement('span', null, React.createElement('span', { style: { color: '#ef4444', marginRight: 4 } }, '*'), label) : label, extra: extra }, node); }; var H2_CREATE_GUTTER = [24, 20]; var formRow = function () { return React.createElement.apply(null, [Row, { gutter: H2_CREATE_GUTTER, align: 'stretch' }].concat(Array.prototype.slice.call(arguments))); }; var col24 = function (node) { return React.createElement(Col, { xs: 24, span: 24 }, node); }; var col16 = function (node) { return React.createElement(Col, { xs: 24, lg: 16 }, node); }; var col12 = function (node) { return React.createElement(Col, { xs: 24, md: 12 }, node); }; var col8 = function (node) { return React.createElement(Col, { xs: 24, lg: 8 }, node); }; var gridBlock = function () { var rows = Array.prototype.slice.call(arguments, 0); return React.createElement('div', { className: 'h2-create-form-grid' }, rows); }; var gridSubsection = function (text) { return formRow(col24(React.createElement('div', { className: 'h2-create-subsection-title' }, text))); }; var renderAccountBindLayout = function () { var removeAccount = function (accountId) { var nextIds = bindAccountIds.filter(function (id) { return id !== accountId; }); ctx.updateStation({ bindAccountMode: nextIds.length ? 'bind' : 'none', bindAccountIds: nextIds }); }; return React.createElement('div', { className: 'h2-account-bind-layout' }, React.createElement('div', { className: 'h2-account-bind-side' }, formItem('绑定系统账号', false, React.createElement(Select, { className: inputCls, mode: 'multiple', showSearch: true, placeholder: '请选择要绑定的系统账号', value: bindAccountIds, options: systemAccountOptions, optionFilterProp: 'label', maxTagCount: 'responsive', onChange: function (v) { var ids = v || []; ctx.updateStation({ bindAccountMode: ids.length ? 'bind' : 'none', bindAccountIds: ids }); }, dropdownStyle: { borderRadius: 8 } })), React.createElement('p', { className: 'h2-account-bind-side__hint' }, '可选择多个账号,绑定后可使用对应账号登录系统'), React.createElement('div', { className: 'h2-account-bind-selected-row' }, React.createElement('span', { className: 'h2-account-bind-selected-label' }, '已选账号'), React.createElement(Tag, { color: 'success', className: 'h2-account-bind-selected-count' }, selectedSystemAccounts.length + ' 个') ) ), React.createElement('div', { className: 'h2-account-bind-preview-area' }, React.createElement('div', { className: 'h2-account-bind-preview-title' }, '账号预览'), selectedSystemAccounts.length ? React.createElement('div', { className: 'h2-account-bind-preview-cards' }, selectedSystemAccounts.map(function (acc) { return React.createElement('div', { key: acc.id, className: 'h2-account-bind-user-card' }, React.createElement('button', { type: 'button', className: 'h2-account-bind-user-card__remove', 'aria-label': '移除账号 ' + (acc.displayName || acc.username || ''), onClick: function () { removeAccount(acc.id); } }, '×'), React.createElement('span', { className: 'h2-account-bind-user-card__avatar', 'aria-hidden': true }, H2_ICONS.user), React.createElement('div', { className: 'h2-account-bind-user-card__body' }, React.createElement('div', { className: 'h2-account-bind-user-card__name-row' }, React.createElement('span', { className: 'h2-account-bind-user-card__name' }, acc.displayName || acc.username || '—'), acc.role ? React.createElement(Tag, { color: h2AccountRoleTagColor(acc.role), className: 'h2-account-bind-user-card__role' }, acc.role) : null ), React.createElement('div', { className: 'h2-account-bind-user-card__meta' }, '账号:' + (acc.username || '—')), React.createElement('div', { className: 'h2-account-bind-user-card__meta' }, '手机号:' + h2MaskMobile(acc.mobile)) ) ); }) ) : React.createElement('div', { className: 'h2-account-bind-preview-empty' }, '请在左侧选择系统账号,预览将显示在此处') ) ); }; var uploadDragger = function (fileList, onChange, btnText, hint, disabled, options) { options = options || {}; var uploadProps = { className: 'h2-create-upload' + (disabled ? ' h2-create-upload--readonly' : ''), multiple: options.multiple !== false, disabled: disabled, fileList: fileList, beforeUpload: function () { return false; }, onChange: onChange, showUploadList: true }; if (options.maxCount != null) uploadProps.maxCount = options.maxCount; return React.createElement(Upload.Dragger, uploadProps, React.createElement('p', { className: 'ant-upload-drag-icon', style: { marginBottom: 8 } }, H2_ICONS.upload), React.createElement('p', { style: { margin: 0, fontWeight: 600, color: '#334155' } }, btnText), React.createElement('p', { className: 'h2-create-upload-hint' }, hint) ); }; var supplierOptions = H2_MOCK_EXISTING_SUPPLIERS.map(function (s) { return { value: s.id, label: s.name + (s.type ? '(' + s.type + ')' : '') + (s.city && s.city.length ? ' · ' + s.city.join('-') : '') }; }); var lingniuSigningCompanyOptions = h2GetLingniuSigningCompanyOptions(); var supplierReadonlyInput = function (value, placeholder) { return React.createElement(Input, { className: inputCls + ' h2-create-readonly-input', value: value != null && value !== '' ? String(value) : '', placeholder: placeholder || '—', disabled: true }); }; var renderSigningCompanySelect = function (supplier) { return formItem('签约公司', true, React.createElement(Select, { className: inputCls, showSearch: true, placeholder: '请选择羚牛签约主体', value: supplier.signingCompany, options: lingniuSigningCompanyOptions, optionFilterProp: 'label', onChange: function (v) { ctx.updateSupplier({ signingCompany: v }); }, dropdownStyle: { borderRadius: 8 } })); }; var renderSupplierDetailFields = function (opts) { opts = opts || {}; var supplier = opts.supplier || ctx.supplier; var readonly = !!opts.readonly; var licensesEditable = !!opts.licensesEditable; var licensesReadonly = readonly && !licensesEditable; var rows = [ gridSubsection('主体信息') ]; if (!readonly) { rows.push(formRow(col16(renderSigningCompanySelect(supplier)))); } rows.push( formRow( col8(formItem('供应商名称', true, readonly ? supplierReadonlyInput(supplier.name) : React.createElement(Input, { className: inputCls, value: supplier.name, placeholder: '请输入供应商名称', onChange: function (e) { ctx.updateSupplier({ name: e.target.value }); } }))), col8(formItem('省市', false, supplierReadonlyInput( supplier.city && supplier.city.length ? h2FormatRegion(supplier.city) : '', '自动从地址解析' ))), col8(formItem('详细地址', false, supplierReadonlyInput(supplier.address, '自动从地址解析'))) ) ); if (!readonly) { rows.push(formRow( col24(formItem('地址解析', false, React.createElement(AddressPasteInput, { inputClassName: inputCls, onParsed: function (parsed) { ctx.updateSupplier({ city: parsed.region, address: parsed.detail }); } }))) )); } rows.push( gridSubsection('开票信息'), formRow( col8(formItem('纳税人识别号', false, readonly ? supplierReadonlyInput(supplier.taxId) : React.createElement(Input, { className: inputCls, value: supplier.taxId, placeholder: '请输入纳税人识别号', onChange: function (e) { ctx.updateSupplier({ taxId: e.target.value }); } }))), col8(formItem('注册电话', false, readonly ? supplierReadonlyInput(supplier.invoicePhone) : React.createElement(Input, { className: inputCls, value: supplier.invoicePhone, placeholder: '手机号或固定电话', type: 'tel', onChange: function (e) { ctx.updateSupplier({ invoicePhone: e.target.value }); } }))), col8(formItem('注册地址', false, readonly ? supplierReadonlyInput(supplier.invoiceAddress) : React.createElement(Input, { className: inputCls, value: supplier.invoiceAddress, placeholder: '请输入注册地址', onChange: function (e) { ctx.updateSupplier({ invoiceAddress: e.target.value }); } }))) ), gridSubsection('银行账户'), formRow( col8(formItem('开户行', false, readonly ? supplierReadonlyInput(supplier.bankName) : React.createElement(Input, { className: inputCls, value: supplier.bankName, placeholder: '请输入开户行', onChange: function (e) { ctx.updateSupplier({ bankName: e.target.value }); } }))), col16(formItem('银行账号', false, readonly ? supplierReadonlyInput(supplier.bankAccount) : React.createElement(Input, { className: inputCls, value: supplier.bankAccount, placeholder: '请输入银行账号', onChange: function (e) { ctx.updateSupplier({ bankAccount: e.target.value }); } }))) ), gridSubsection('资质证照'), formRow( col12(formItem('营业执照', false, uploadDragger( supplier.businessLicenseFiles, function (info) { ctx.updateSupplier({ businessLicenseFiles: info.fileList }); if (licensesReadonly) return; if (info.file && info.file.status !== 'removed') { h2TriggerBusinessLicenseOcr(info.file, ctx); } }, licensesReadonly ? '已关联证照' : '点击或拖拽上传', licensesReadonly ? '仅展示,不可修改' : '支持 OCR 识别,上传后自动反写名称、税号、通讯地址', licensesReadonly, { multiple: false } ))), col12(formItem('其他证件', false, uploadDragger( supplier.fillingLicenseFiles, licensesReadonly ? function () {} : function (info) { ctx.updateSupplier({ fillingLicenseFiles: info.fileList }); }, licensesReadonly ? '已关联证照' : '点击或拖拽上传', licensesReadonly ? '仅展示,不可修改' : '支持多文件上传,不限数量,PDF、图片', licensesReadonly, { multiple: true } ))) ) ); return gridBlock.apply(null, rows); }; var renderSupplierLinkForm = function () { return React.createElement(React.Fragment, null, gridBlock( formRow( col16(formItem('选择供应商', true, React.createElement(Select, { className: inputCls, showSearch: true, placeholder: '请搜索并选择供应商', value: ctx.linkedSupplierId, options: supplierOptions, optionFilterProp: 'label', onChange: ctx.handleLinkSupplierChange, dropdownStyle: { borderRadius: 8 } }))), col8(renderSigningCompanySelect(ctx.supplier)) ) ), ctx.linkedSupplierId ? React.createElement('div', { className: 'h2-supplier-linked-preview' }, renderSupplierDetailFields({ readonly: true, licensesEditable: true, supplier: ctx.supplier })) : null ); }; var renderSupplierNewForm = function () { return renderSupplierDetailFields({ readonly: false }); }; var renderSupplierNonePanel = function () { return gridBlock( formRow( col24(React.createElement('p', { className: 'h2-supplier-none-hint' }, '当前不关联供应商,无需填写或关联任何供应商信息。')) ) ); }; var renderSupplierTabs = function () { var tabs = [ { key: 'link', label: '关联已有' }, { key: 'new', label: '新建供应商' }, { key: 'none', label: '不关联供应商' } ]; return React.createElement('div', { className: 'h2-supplier-tabs' }, React.createElement('div', { className: 'h2-supplier-tabs__nav', role: 'tablist', 'aria-label': '供应商关联方式' }, tabs.map(function (tab) { var active = ctx.supplierMode === tab.key; return React.createElement('button', { type: 'button', key: tab.key, role: 'tab', className: 'h2-supplier-tabs__item' + (active ? ' h2-supplier-tabs__item--active' : ''), 'aria-selected': active, onClick: function () { if (ctx.supplierMode !== tab.key) { ctx.handleSupplierModeChange({ target: { value: tab.key } }); } } }, tab.label); }) ), React.createElement('div', { className: 'h2-supplier-tabs__panel', role: 'tabpanel' }, ctx.supplierMode === 'link' ? renderSupplierLinkForm() : ctx.supplierMode === 'new' ? renderSupplierNewForm() : renderSupplierNonePanel() ) ); }; var usedBindAccountMap = ctx.usedBindAccountMap || {}; var systemAccountOptions = H2_MOCK_SYSTEM_ACCOUNTS.filter(function (a) { return !usedBindAccountMap[a.id]; }).map(function (a) { return { value: a.id, label: '「' + (a.username || '—') + '」「' + (a.role || '—') + '」' }; }); var selectedSystemAccounts = h2BuildBoundAccountsFromIds(bindAccountIds); var contractDateRangeValue = null; if (dayjs && ctx.station.contractStart && ctx.station.contractEnd) { var ds = dayjs(ctx.station.contractStart); var de = dayjs(ctx.station.contractEnd); if (ds.isValid() && de.isValid()) contractDateRangeValue = [ds, de]; } var mobilePhoneVal = (ctx.station.mobilePhone || '').trim(); var landlinePhoneVal = (ctx.station.landlinePhone || '').trim(); var mobilePhoneError = mobilePhoneVal && !h2IsMobilePhone(mobilePhoneVal) ? h2GetMobilePhoneInputTip(mobilePhoneVal) : ''; var landlinePhoneError = landlinePhoneVal && !h2IsLandlinePhone(landlinePhoneVal) ? h2GetLandlinePhoneInputTip(landlinePhoneVal) : ''; var renderContractDates = function () { if (dayjs && DatePicker && DatePicker.RangePicker) { return React.createElement(DatePicker.RangePicker, { className: inputCls, style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: ['开始日期', '结束日期'], value: contractDateRangeValue, onChange: function (dates) { if (!dates || dates.length < 2) { ctx.updateStation({ contractStart: '', contractEnd: '' }); return; } ctx.updateStation({ contractStart: dayjs(dates[0]).format('YYYY-MM-DD'), contractEnd: dayjs(dates[1]).format('YYYY-MM-DD') }); } }); } return React.createElement('div', { className: 'h2-contract-dates' }, React.createElement(Input, { className: inputCls, type: 'date', value: ctx.station.contractStart || '', style: { flex: 1, minWidth: 0 }, onChange: function (e) { ctx.updateStation({ contractStart: e.target.value }); } }), React.createElement('span', { className: 'h2-contract-dates-sep' }, '至'), React.createElement(Input, { className: inputCls, type: 'date', value: ctx.station.contractEnd || '', style: { flex: 1, minWidth: 0 }, onChange: function (e) { ctx.updateStation({ contractEnd: e.target.value }); } }) ); }; return React.createElement(React.Fragment, null, React.createElement('div', { className: 'h2-create-shell' }, React.createElement('div', { className: 'h2-create-back-only' }, React.createElement(Button, { className: 'h2-create-back-btn', type: 'default', icon: H2_ICONS.back, onClick: ctx.onCancel, disabled: ctx.submitting }, '返回') ), React.createElement(Form, { layout: 'vertical', requiredMark: false, className: 'h2-create-form h2-create-form--list h2-create-form--grid' }, React.createElement(Card, { className: 'h2-create-card', id: 'h2-create-station-card', title: h2CardTitleWithStep(1, '加氢站基本信息'), bordered: false }, gridBlock( formRow( col8(formItem('加氢站名称', true, React.createElement(Input, { className: inputCls, value: ctx.station.name, placeholder: '请输入加氢站名称', maxLength: 80, onChange: function (e) { ctx.updateStation({ name: e.target.value }); }, onBlur: ctx.syncSupplierFromStation }))), col8(formItem('省市', true, React.createElement(Input, { className: inputCls + ' h2-create-readonly-input', value: stationAddr.region && stationAddr.region.length ? h2FormatRegion(stationAddr.region) : '', placeholder: '自动从地址解析', disabled: true }))), col8(formItem('通讯地址', true, React.createElement(Input, { className: inputCls + ' h2-create-readonly-input', value: stationAddr.detail || '', placeholder: '自动从地址解析', disabled: true }))) ), formRow( col24(formItem('地址解析', false, React.createElement(AddressPasteInput, { inputClassName: inputCls, onParsed: function (parsed) { ctx.updateStation({ address: { region: parsed.region, detail: parsed.detail } }); if (ctx.supplierMode === 'new' && parsed.region && parsed.region.length >= 2) { ctx.updateSupplier({ city: parsed.region.slice(), address: parsed.detail || '' }); } } }))) ), formRow( col8(formItem('联系人', true, React.createElement(Input, { className: inputCls, value: ctx.station.contact, placeholder: '请输入联系人', onChange: function (e) { ctx.updateStation({ contact: e.target.value }); }, onBlur: ctx.syncSupplierFromStation }))), col8(formItem('手机号', false, React.createElement(Input, { className: inputCls, value: ctx.station.mobilePhone || '', placeholder: '请输入手机号码', type: 'tel', autoComplete: 'tel', maxLength: 11, status: mobilePhoneError ? 'error' : undefined, onChange: function (e) { ctx.updateStation({ mobilePhone: h2FilterMobilePhoneInput(e.target.value) }); }, onBlur: ctx.syncSupplierFromStation }), mobilePhoneError)), col8(formItem('固定电话', false, React.createElement(Input, { className: inputCls, value: ctx.station.landlinePhone || '', placeholder: '请输入固定电话', type: 'tel', autoComplete: 'tel', status: landlinePhoneError ? 'error' : undefined, onChange: function (e) { ctx.updateStation({ landlinePhone: h2FilterLandlinePhoneInput(e.target.value) }); }, onBlur: ctx.syncSupplierFromStation }), landlinePhoneError)) ), formRow( col8(formItem('站点类型', false, React.createElement('div', { className: 'h2-station-type-field' }, h2RenderOptionButtonGroup([ { label: '签约站点', value: 'signed', tone: 'allday' }, { label: '普通站点', value: 'unsigned', tone: 'custom' } ], ctx.station.isSigned ? 'signed' : 'unsigned', function (v) { var signed = v === 'signed'; ctx.updateStation({ isSigned: signed, contractStart: signed ? ctx.station.contractStart : '', contractEnd: signed ? ctx.station.contractEnd : '' }); }, { ariaLabel: '站点类型' }) ))), ctx.station.isSigned ? col16(formItem('合作时间', true, renderContractDates())) : null ), ctx.station.isSigned ? formRow( col24(formItem('合同附件', false, React.createElement('div', { className: 'h2-contract-panel' }, React.createElement(ContractFilesUpload, { fileList: ctx.station.contractFiles, onChange: function (info) { ctx.updateStation({ contractFiles: info.fileList }); } }) ))) ) : null ) ), showSupplier ? React.createElement(Card, { className: 'h2-create-card', id: 'h2-create-supplier-card', title: h2CardTitleWithStep(2, '供应商相关信息'), bordered: false }, renderSupplierTabs() ) : null, showSupplier && showAccountBind ? React.createElement(Card, { className: 'h2-create-card', id: 'h2-create-account-card', title: h2CardTitleWithStep(3, '系统账号绑定'), bordered: false }, gridBlock( !accountEditable && isEdit ? formRow( col24(React.createElement('div', { className: 'h2-account-bind-readonly-wrap' }, React.createElement('p', { className: 'h2-account-bind-hint', style: { marginBottom: 10 } }, '当前为加氢站账号登录,系统账号绑定仅管理员(admin)可修改。' ), selectedSystemAccounts.length ? selectedSystemAccounts.map(function (acc) { return React.createElement('div', { key: acc.id, className: 'h2-account-bind-readonly-item' }, React.createElement('div', { className: 'h2-account-bind-readonly-item__name' }, acc.displayName || acc.username || '—'), React.createElement('div', { className: 'h2-account-bind-readonly-item__meta' }, (acc.username || '—') + ' · ' + (acc.role || '—') + ' · ' + (acc.mobile || '—') ) ); }) : React.createElement('p', { className: 'h2-account-bind-hint' }, '暂未绑定系统账号') )) ) : null, accountEditable ? formRow(col24(renderAccountBindLayout())) : null ) ) : null ) ), React.createElement('footer', { className: 'h2-create-footer' }, React.createElement('div', { className: 'h2-create-footer-inner' }, React.createElement('div', { className: 'h2-create-footer-actions' }, React.createElement(Button, { onClick: ctx.onCancel, disabled: ctx.submitting, style: { borderRadius: 8 }, 'aria-label': '取消并返回列表' }, '取消'), ctx.showReset ? React.createElement(Button, { onClick: ctx.onReset, disabled: ctx.submitting, style: { borderRadius: 8 }, 'aria-label': '重置表单' }, '重置') : null, React.createElement(Button, { type: 'primary', style: H2_PRIMARY_BTN_STYLE, onClick: ctx.onSubmit, loading: ctx.submitting, 'aria-label': isEdit ? '保存编辑加氢站点' : '提交新建加氢站点' }, ctx.submitLabel || (isEdit ? '保存修改' : '提交创建')) ) ) ) ); } const Component = function () { var useState = React.useState; var useMemo = React.useMemo; var useCallback = React.useCallback; var antd = window.antd; var Breadcrumb = antd.Breadcrumb; var Card = antd.Card; var Table = antd.Table; var Button = antd.Button; var Select = antd.Select; var Input = antd.Input; var Space = antd.Space; var Image = antd.Image; var Modal = antd.Modal; var Form = antd.Form; var Switch = antd.Switch; var Upload = antd.Upload; var Tag = antd.Tag; var Tooltip = antd.Tooltip; var Divider = antd.Divider; var Alert = antd.Alert; var Dropdown = antd.Dropdown; var Descriptions = antd.Descriptions; var Cascader = antd.Cascader; var Radio = antd.Radio; var DatePicker = antd.DatePicker; var message = antd.message; var dayjs = window.dayjs; var listState = useState(function () { return h2ApplyDueCostPricesToList(H2_MOCK_STATIONS.slice()).records; }); var listData = listState[0]; var setListData = listState[1]; var categoryTabState = useState('all'); var categoryTab = categoryTabState[0]; var setCategoryTab = categoryTabState[1]; var listFiltersState = useState(h2EmptyListFilters()); var listFilters = listFiltersState[0]; var setListFilters = listFiltersState[1]; var appliedFiltersState = useState(h2EmptyListFilters()); var appliedFilters = appliedFiltersState[0]; var setAppliedFilters = appliedFiltersState[1]; var deleteModalState = useState({ open: false, record: null }); var deleteModal = deleteModalState[0]; var setDeleteModal = deleteModalState[1]; var viewModalState = useState({ open: false, record: null }); var viewModal = viewModalState[0]; var setViewModal = viewModalState[1]; var filePreviewModalState = useState({ open: false, url: '', name: '', type: 'image' }); var filePreviewModal = filePreviewModalState[0]; var setFilePreviewModal = filePreviewModalState[1]; var requirementModalState = useState(false); var requirementModalOpen = requirementModalState[0]; var setRequirementModalOpen = requirementModalState[1]; var userManualModalState = useState(false); var userManualModalOpen = userManualModalState[0]; var setUserManualModalOpen = userManualModalState[1]; var importModalState = useState(false); var importModalOpen = importModalState[0]; var setImportModalOpen = importModalState[1]; var importFileListState = useState([]); var importFileList = importFileListState[0]; var setImportFileList = importFileListState[1]; var importPreviewState = useState(null); var importPreview = importPreviewState[0]; var setImportPreview = importPreviewState[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 refuelSortState = useState({ key: null, order: null }); var refuelSort = refuelSortState[0]; var setRefuelSort = refuelSortState[1]; var businessModalState = useState({ open: false, record: null, businessStatus: '营业中', businessHours: '', originBusinessStatus: '营业中', statusLogs: [] }); var businessModal = businessModalState[0]; var setBusinessModal = businessModalState[1]; var priceModalState = useState({ open: false, record: null, costUnitPrice: '', effectiveTime: '', priceLogs: [] }); var priceModal = priceModalState[0]; var setPriceModal = priceModalState[1]; var balanceAlertModalState = useState({ open: false, record: null, threshold: '' }); var balanceAlertModal = balanceAlertModalState[0]; var setBalanceAlertModal = balanceAlertModalState[1]; var rechargeModalState = useState({ open: false, lines: [] }); var rechargeModal = rechargeModalState[0]; var setRechargeModal = rechargeModalState[1]; var alertStationModalState = useState({ open: false, type: '', title: '', stations: [] }); var alertStationModal = alertStationModalState[0]; var setAlertStationModal = alertStationModalState[1]; var refuelModalState = useState({ open: false, station: null, records: [] }); var refuelModal = refuelModalState[0]; var setRefuelModal = refuelModalState[1]; var ledgerStoreState = useState(function () { return h2InitLedgerRecordsStore(); }); var ledgerStore = ledgerStoreState[0]; var setLedgerStore = ledgerStoreState[1]; var statementRecordsState = useState(function () { return h2InitStatementRecordsStore(); }); var statementRecords = statementRecordsState[0]; var setStatementRecords = statementRecordsState[1]; var statementModalState = useState({ open: false, record: null, startDate: '', endDate: '', phase: 'select', draftRows: [], balanceAfterSettlement: 0, receiptDate: '', receiptAmount: '', invoiceFiles: [] }); var statementModal = statementModalState[0]; var setStatementModal = statementModalState[1]; var statementHistoryModalState = useState({ open: false, record: null }); var statementHistoryModal = statementHistoryModalState[0]; var setStatementHistoryModal = statementHistoryModalState[1]; var statementDetailModalState = useState({ open: false, statementRecord: null, detailRows: [] }); var statementDetailModal = statementDetailModalState[0]; var setStatementDetailModal = statementDetailModalState[1]; var prepaidBalanceDrillState = useState({ open: false, stationName: '', endingBalance: 0, rows: [] }); var prepaidBalanceDrill = prepaidBalanceDrillState[0]; var setPrepaidBalanceDrill = prepaidBalanceDrillState[1]; var subViewState = useState('list'); var subView = subViewState[0]; var setSubView = subViewState[1]; var createStationState = useState(h2CreateEmptyStationForm()); var createStation = createStationState[0]; var setCreateStation = createStationState[1]; var createSupplierModeState = useState('link'); var createSupplierMode = createSupplierModeState[0]; var setCreateSupplierMode = createSupplierModeState[1]; var createLinkedSupplierIdState = useState(undefined); var createLinkedSupplierId = createLinkedSupplierIdState[0]; var setCreateLinkedSupplierId = createLinkedSupplierIdState[1]; var createSupplierState = useState(h2CreateEmptySupplierForm()); var createSupplier = createSupplierState[0]; var setCreateSupplier = createSupplierState[1]; var createSubmittingState = useState(false); var createSubmitting = createSubmittingState[0]; var setCreateSubmitting = createSubmittingState[1]; var editStationState = useState(h2CreateEmptyStationForm()); var editStation = editStationState[0]; var setEditStation = editStationState[1]; var editRecordState = useState(null); var editRecord = editRecordState[0]; var setEditRecord = editRecordState[1]; var editOriginFormState = useState(null); var editOriginForm = editOriginFormState[0]; var setEditOriginForm = editOriginFormState[1]; var editSubmittingState = useState(false); var editSubmitting = editSubmittingState[0]; var setEditSubmitting = editSubmittingState[1]; var editSupplierModeState = useState('link'); var editSupplierMode = editSupplierModeState[0]; var setEditSupplierMode = editSupplierModeState[1]; var editLinkedSupplierIdState = useState(undefined); var editLinkedSupplierId = editLinkedSupplierIdState[0]; var setEditLinkedSupplierId = editLinkedSupplierIdState[1]; var editSupplierState = useState(h2CreateEmptySupplierForm()); var editSupplier = editSupplierState[0]; var setEditSupplier = editSupplierState[1]; var isAdminUser = h2IsAdminUser(); var createSupplierReadonly = createSupplierMode === 'link' && !!createLinkedSupplierId; var existingNameMap = useMemo(function () { var map = {}; listData.forEach(function (r) { if (r.name) map[String(r.name).trim()] = true; }); return map; }, [listData]); var categoryCounts = useMemo(function () { var counts = { all: listData.length, balanceAlert: 0, arrears: 0, none: 0 }; listData.forEach(function (r) { if (h2IsArrearsStation(r)) counts.arrears += 1; else if (h2IsBalanceAlertStation(r)) counts.balanceAlert += 1; var stats = h2CalcRefuelStats(r.name); if (h2DeriveFrequencyByRefuelCount(stats.count) === 'none') counts.none += 1; }); return counts; }, [listData]); var signedStats = useMemo(function () { var signed = 0; listData.forEach(function (r) { if (r.isSigned) signed += 1; }); return { signed: signed, unsigned: listData.length - signed }; }, [listData]); var filteredList = useMemo(function () { var list = listData.slice(); if (categoryTab !== 'all') { list = list.filter(function (r) { if (categoryTab === 'balanceAlert') return h2IsBalanceAlertStation(r); if (categoryTab === 'arrears') return h2IsArrearsStation(r); if (categoryTab === 'none') { var stats = h2CalcRefuelStats(r.name); return h2DeriveFrequencyByRefuelCount(stats.count) === 'none'; } return true; }); } var kw = (appliedFilters.name || '').trim().toLowerCase(); if (kw) list = list.filter(function (r) { return (r.name || '').toLowerCase().indexOf(kw) !== -1; }); if (appliedFilters.signed === 'yes') list = list.filter(function (r) { return r.isSigned; }); if (appliedFilters.signed === 'no') list = list.filter(function (r) { return !r.isSigned; }); if (appliedFilters.region && appliedFilters.region.length) { list = list.filter(function (r) { return h2MatchRegionFilter(r.region, appliedFilters.region); }); } if (appliedFilters.businessStatus) { list = list.filter(function (r) { return r.businessStatus === appliedFilters.businessStatus; }); } return list.map(function (r) { var stats = h2CalcRefuelStats(r.name); return Object.assign({}, r, { refuelCount: stats.count, refuelTotalKg: stats.totalKg, refuelFreqKey: h2DeriveFrequencyByRefuelCount(stats.count) }); }); }, [listData, categoryTab, appliedFilters]); var sortedList = useMemo(function () { var list = filteredList.slice(); if (!refuelSort.key || !refuelSort.order) return list; var dir = refuelSort.order === 'ascend' ? 1 : -1; list.sort(function (a, b) { var av = refuelSort.key === 'refuelCount' ? (a.refuelCount || 0) : (a.refuelTotalKg || 0); var bv = refuelSort.key === 'refuelCount' ? (b.refuelCount || 0) : (b.refuelTotalKg || 0); if (av === bv) return 0; return av > bv ? dir : -dir; }); return list; }, [filteredList, refuelSort]); var totalCount = sortedList.length; var displayList = useMemo(function () { var start = (page - 1) * pageSize; return sortedList.slice(start, start + pageSize); }, [sortedList, page, pageSize]); var tablePagination = useMemo(function () { return { current: page, pageSize: pageSize, total: totalCount, showSizeChanger: true, pageSizeOptions: ['5', '10', '20', '50'], showTotal: function (t) { return '共 ' + t + ' 条'; }, onChange: function (p, size) { setPage(p); if (size && size !== pageSize) setPageSize(size); } }; }, [page, pageSize, totalCount]); var handleKpiCardClick = useCallback(function (key) { setCategoryTab(key); if (key === 'all' || key === 'none') { setListFilters(function (p) { return Object.assign({}, p, { signed: undefined }); }); setAppliedFilters(function (p) { return Object.assign({}, p, { signed: undefined }); }); } setPage(1); }, []); var handleSignedFilterCardClick = useCallback(function (key) { var nextSigned = appliedFilters.signed === key ? undefined : key; if (nextSigned) setCategoryTab('all'); setListFilters(function (p) { return Object.assign({}, p, { signed: nextSigned }); }); setAppliedFilters(function (p) { return Object.assign({}, p, { signed: nextSigned }); }); setPage(1); }, [appliedFilters.signed]); var handleListFilterQuery = useCallback(function () { setAppliedFilters({ name: listFilters.name, signed: listFilters.signed, region: listFilters.region, businessStatus: listFilters.businessStatus }); if (listFilters.signed) setCategoryTab('all'); setPage(1); }, [listFilters]); var handleListFilterReset = useCallback(function () { var empty = h2EmptyListFilters(); setListFilters(empty); setAppliedFilters(empty); setCategoryTab('all'); setPage(1); }, []); var handleListTableChange = useCallback(function (_pagination, _filters, sorter) { var nextSorter = Array.isArray(sorter) ? sorter[0] : sorter; if (!nextSorter || !nextSorter.columnKey) { setRefuelSort({ key: null, order: null }); return; } if (nextSorter.columnKey !== 'refuelCount' && nextSorter.columnKey !== 'refuelTotalKg') return; setRefuelSort({ key: nextSorter.columnKey, order: nextSorter.order || 'descend' }); setPage(1); }, []); var renderFilterField = useCallback(function (label, control) { return React.createElement('div', { className: 'lc-filter-field' }, React.createElement('span', { className: 'lc-filter-field-label' }, label), React.createElement('div', { className: 'lc-filter-field-control' }, control) ); }, []); var renderAlertStatCard = useCallback(function (card, options) { var active = options.active; var count = options.count; var onClick = options.onClick; var icon = options.icon; return React.createElement('div', { key: card.key, role: 'button', tabIndex: 0, className: 'lc-alert-card lc-alert-card--' + card.type + ' lc-alert-card-clickable' + (active ? ' lc-alert-card-active' : ''), onClick: onClick, onKeyDown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } }, 'aria-pressed': active, 'aria-label': card.title + ',共 ' + count + ' 个' }, React.createElement('div', { className: 'lc-alert-card-tip-anchor' }, React.createElement(Tooltip, { title: card.desc, placement: 'topRight', overlayStyle: { maxWidth: 320 } }, React.createElement('span', { className: 'lc-alert-card-tip', role: 'img', 'aria-label': card.title + '说明', onClick: function (e) { e.stopPropagation(); }, onMouseDown: function (e) { e.stopPropagation(); } }, h2SvgIcon([{ tag: 'circle', cx: 12, cy: 12, r: 10 }, { tag: 'line', x1: 12, y1: 16, x2: 12, y2: 12 }, { tag: 'line', x1: 12, y1: 8, x2: 12.01, y2: 8 }], 12) ) ) ), React.createElement('div', { className: 'lc-alert-card-icon', 'aria-hidden': true }, icon), React.createElement('div', { className: 'lc-alert-card-main' }, React.createElement('div', { className: 'lc-alert-card-title' }, card.title), React.createElement('div', { className: 'lc-alert-card-val' }, count) ) ); }, []); var resetCreateForm = useCallback(function () { setCreateStation(h2CreateEmptyStationForm()); setCreateSupplierMode('link'); setCreateLinkedSupplierId(undefined); setCreateSupplier(h2CreateEmptySupplierForm()); }, []); var openCreate = useCallback(function () { resetCreateForm(); setSubView('create'); }, [resetCreateForm]); var handleCreatePageBack = useCallback(function () { setCreateSubmitting(false); setSubView('list'); }, []); var handleCreateCancel = useCallback(function () { if (createSubmitting) return; if (h2CreateFormDirty(createStation, createSupplier, createSupplierMode, createLinkedSupplierId)) { Modal.confirm({ title: '放弃未保存的更改?', content: '当前填写内容尚未提交,返回列表后将丢失。', okText: '放弃并返回', cancelText: '继续填写', okButtonProps: { danger: true }, centered: true, onOk: handleCreatePageBack }); return; } handleCreatePageBack(); }, [createSubmitting, createStation, createSupplier, createSupplierMode, createLinkedSupplierId, handleCreatePageBack]); var updateCreateStation = useCallback(function (patch) { setCreateStation(function (prev) { return Object.assign({}, prev, patch); }); }, []); var updateCreateSupplier = useCallback(function (patch) { setCreateSupplier(function (prev) { return Object.assign({}, prev, patch); }); }, []); var handleCreateSupplierModeChange = useCallback(function (e) { var nextMode = e.target.value; setCreateSupplierMode(nextMode); setCreateLinkedSupplierId(undefined); setCreateSupplier(h2CreateEmptySupplierForm()); if (nextMode === 'none') { setCreateStation(function (prev) { return Object.assign({}, prev, { bindAccountMode: 'none', bindAccountIds: [] }); }); } }, []); var handleCreateLinkSupplierChange = useCallback(function (id) { setCreateLinkedSupplierId(id); var found = H2_MOCK_EXISTING_SUPPLIERS.filter(function (s) { return s.id === id; })[0]; setCreateSupplier(h2SupplierToForm(found)); }, []); var handleCreateSupplierCityChange = useCallback(function (v) { updateCreateSupplier({ city: v, region: v && v[0] ? h2GetRegionByProvince(v[0]) : '' }); }, [updateCreateSupplier]); var syncCreateSupplierFromStation = useCallback(function () { if (createSupplierMode !== 'new') return; setCreateSupplier(function (supplier) { var patch = {}; if (createStation.name && !supplier.name) patch.name = createStation.name; if (createStation.address && createStation.address.region && createStation.address.region.length >= 2) { patch.city = createStation.address.region.slice(); patch.region = h2GetRegionByProvince(createStation.address.region[0]); } if (createStation.address && createStation.address.detail && !supplier.address) patch.address = createStation.address.detail; if (createStation.contact && !supplier.contactName) patch.contactName = createStation.contact; if (createStation.mobilePhone && !supplier.contactMobile) patch.contactMobile = createStation.mobilePhone; return Object.keys(patch).length ? Object.assign({}, supplier, patch) : supplier; }); }, [createSupplierMode, createStation]); var validateCreateForm = useCallback(function () { if (!(createStation.name || '').trim()) { message.warning('请填写加氢站名称'); return false; } if (!createStation.address.region || createStation.address.region.length < 2) { message.warning('请通过地址解析填写省 / 市'); return false; } if (!(createStation.address.detail || '').trim()) { message.warning('请通过地址解析填写通讯地址'); return false; } if (!(createStation.contact || '').trim()) { message.warning('请填写联系人'); return false; } var createPhoneErr = h2ValidateStationPhones(createStation); if (createPhoneErr) { message.warning(createPhoneErr); return false; } if (createStation.isSigned) { if (!createStation.contractStart || !createStation.contractEnd) { message.warning('签约站点请填写合作时间'); return false; } } var hoursParsed = h2ParseBusinessHours(createStation.businessHours); if (hoursParsed.mode === 'custom') { if (!(hoursParsed.start || '').trim() || !(hoursParsed.end || '').trim()) { message.warning('非全天营业请选择开始与结束时间'); return false; } if (!/^(\d{1,2}):(\d{2})$/.test(h2NormalizeTimeText(hoursParsed.start)) || !/^(\d{1,2}):(\d{2})$/.test(h2NormalizeTimeText(hoursParsed.end))) { message.warning('营业时间格式须为 HH:mm'); return false; } } if (createSupplierMode === 'link' && !createLinkedSupplierId) { message.warning('请选择要关联的已有供应商'); return false; } if (createSupplierMode === 'new' && !(createSupplier.name || '').trim()) { message.warning('请填写供应商名称'); return false; } if (createSupplierMode !== 'none' && !(createSupplier.signingCompany || '').trim()) { message.warning('请选择签约公司'); return false; } if (createSupplierMode !== 'none' && createStation.bindAccountIds && createStation.bindAccountIds.length) { var usedMapCreate = h2CollectUsedBindAccountMap(listData); var ci; for (ci = 0; ci < createStation.bindAccountIds.length; ci++) { var boundName = usedMapCreate[createStation.bindAccountIds[ci]]; if (boundName) { message.warning('系统账号已绑定站点「' + boundName + '」,请重新选择'); return false; } } } return true; }, [createStation, createSupplierMode, createLinkedSupplierId, createSupplier, listData]); var handleCreatePageSubmit = useCallback(function (payload) { var station = payload && payload.station; if (!station) return; var nextId = listData.reduce(function (m, r) { return Math.max(m, r.id || 0); }, 0) + 1; var created = Object.assign({}, station, { id: nextId }); setListData(function (prev) { return [created].concat(prev); }); setPage(1); setSubView('list'); var supplierHint = payload.supplierMode === 'link' ? '已关联已有供应商' : payload.supplierMode === 'new' ? '已同步创建加氢站类型供应商' : '未关联供应商'; var accountHint = ''; if (station.bindAccountMode === 'bind' && station.boundAccounts && station.boundAccounts.length) { accountHint = ';已绑定 ' + station.boundAccounts.length + ' 个系统账号'; } message.success('站点已创建;' + supplierHint + accountHint + '(原型)'); }, [listData]); var handleCreateSubmit = useCallback(function () { if (createSubmitting) return; if (!validateCreateForm()) return; setCreateSubmitting(true); syncCreateSupplierFromStation(); var now = h2OperateTimestamp(); var phoneDisplay = createStation.mobilePhone || createStation.landlinePhone || ''; if (createStation.mobilePhone && createStation.landlinePhone) phoneDisplay = createStation.mobilePhone; var createBindIds = createSupplierMode === 'none' ? [] : (createStation.bindAccountIds || []).slice(); var boundAccounts = h2BuildBoundAccountsFromIds(createBindIds); var boundAccount = boundAccounts.length ? boundAccounts[0] : null; var stationRecord = { name: (createStation.name || '').trim(), region: createStation.address.region || [], addressDetail: (createStation.address.detail || '').trim(), fullAddress: h2BuildFullAddress(createStation.address.region, createStation.address.detail), isSigned: !!createStation.isSigned, contractStart: createStation.isSigned ? createStation.contractStart : '', contractEnd: createStation.isSigned ? createStation.contractEnd : '', contractFiles: (createStation.contractFiles || []).map(function (f) { return { uid: f.uid, name: f.name || '合同附件.pdf', url: f.url || '' }; }), businessStatus: createStation.businessStatus || '营业中', businessHours: (createStation.businessHours || '').trim(), contact: (createStation.contact || '').trim(), phone: phoneDisplay, mobilePhone: (createStation.mobilePhone || '').trim(), landlinePhone: (createStation.landlinePhone || '').trim(), costUnitPrice: createStation.costUnitPrice ? parseFloat(createStation.costUnitPrice) : undefined, customerUnitPrice: createStation.customerUnitPrice ? parseFloat(createStation.customerUnitPrice) : undefined, prepaidBalance: 0, businessStatusLogs: [], costPriceLogs: [], updateTime: now, supplierMode: createSupplierMode, linkedSupplierId: createSupplierMode === 'link' ? createLinkedSupplierId : undefined, supplier: createSupplierMode === 'none' ? undefined : Object.assign({ type: '加氢站' }, createSupplier), bindAccountMode: createBindIds.length ? 'bind' : 'none', bindAccountIds: createBindIds, boundAccountIds: createBindIds, boundAccountId: boundAccount ? boundAccount.id : undefined, boundAccount: boundAccount, boundAccounts: boundAccounts }; window.setTimeout(function () { handleCreatePageSubmit({ station: stationRecord, supplier: createSupplier, supplierMode: createSupplierMode }); setCreateSubmitting(false); }, 380); }, [createSubmitting, validateCreateForm, syncCreateSupplierFromStation, createStation, createSupplier, createSupplierMode, createLinkedSupplierId, handleCreatePageSubmit]); var handleEditPageBack = useCallback(function () { setEditSubmitting(false); setEditRecord(null); setEditOriginForm(null); setSubView('list'); }, []); var handleEditCancel = useCallback(function () { if (editSubmitting) return; if (h2EditFormDirty(editStation, editOriginForm)) { Modal.confirm({ title: '放弃未保存的更改?', content: '当前修改尚未保存,返回列表后将丢失。', okText: '放弃并返回', cancelText: '继续编辑', okButtonProps: { danger: true }, centered: true, onOk: handleEditPageBack }); return; } handleEditPageBack(); }, [editSubmitting, editStation, editOriginForm, handleEditPageBack]); var updateEditStation = useCallback(function (patch) { setEditStation(function (prev) { return Object.assign({}, prev, patch); }); }, []); var updateEditSupplier = useCallback(function (patch) { setEditSupplier(function (prev) { return Object.assign({}, prev, patch); }); }, []); var confirmEditSupplierChange = useCallback(function (applyChange) { Modal.confirm({ title: '确认修改供应商', content: '修改供应商会影响对账时的供应商信息,请确认是否进行修改', okText: '确认修改', cancelText: '取消', centered: true, onOk: function () { applyChange(); } }); }, []); var handleEditSupplierModeChange = useCallback(function (e) { var nextMode = e.target.value; if (nextMode === editSupplierMode) return; confirmEditSupplierChange(function () { setEditSupplierMode(nextMode); setEditLinkedSupplierId(undefined); setEditSupplier(h2CreateEmptySupplierForm()); if (nextMode === 'none') { setEditStation(function (prev) { return Object.assign({}, prev, { bindAccountMode: 'none', bindAccountIds: [] }); }); } }); }, [editSupplierMode, confirmEditSupplierChange]); var handleEditLinkSupplierChange = useCallback(function (id) { if (id === editLinkedSupplierId) return; confirmEditSupplierChange(function () { setEditLinkedSupplierId(id); var found = H2_MOCK_EXISTING_SUPPLIERS.filter(function (s) { return s.id === id; })[0]; setEditSupplier(h2SupplierToForm(found)); }); }, [editLinkedSupplierId, confirmEditSupplierChange]); var handleEditSupplierCityChange = useCallback(function (v) { updateEditSupplier({ city: v, region: v && v[0] ? h2GetRegionByProvince(v[0]) : '' }); }, [updateEditSupplier]); var syncEditSupplierFromStation = useCallback(function () { if (editSupplierMode !== 'new') return; setEditSupplier(function (supplier) { var patch = {}; if (editStation.name && !supplier.name) patch.name = editStation.name; if (editStation.address && editStation.address.region && editStation.address.region.length >= 2) { patch.city = editStation.address.region.slice(); patch.region = h2GetRegionByProvince(editStation.address.region[0]); } if (editStation.address && editStation.address.detail && !supplier.address) patch.address = editStation.address.detail; if (editStation.contact && !supplier.contactName) patch.contactName = editStation.contact; if (editStation.mobilePhone && !supplier.contactMobile) patch.contactMobile = editStation.mobilePhone; return Object.keys(patch).length ? Object.assign({}, supplier, patch) : supplier; }); }, [editSupplierMode, editStation]); var validateEditForm = useCallback(function () { if (!(editStation.name || '').trim()) { message.warning('请填写加氢站名称'); return false; } if (!editStation.address.region || editStation.address.region.length < 2) { message.warning('请通过地址解析填写省 / 市'); return false; } if (!(editStation.address.detail || '').trim()) { message.warning('请通过地址解析填写通讯地址'); return false; } if (!(editStation.contact || '').trim()) { message.warning('请填写联系人'); return false; } var editPhoneErr = h2ValidateStationPhones(editStation); if (editPhoneErr) { message.warning(editPhoneErr); return false; } if (editStation.isSigned) { if (!editStation.contractStart || !editStation.contractEnd) { message.warning('签约站点请填写合作时间'); return false; } } var hoursParsedEdit = h2ParseBusinessHours(editStation.businessHours); if (hoursParsedEdit.mode === 'custom') { if (!(hoursParsedEdit.start || '').trim() || !(hoursParsedEdit.end || '').trim()) { message.warning('非全天营业请选择开始与结束时间'); return false; } } if (editSupplierMode === 'link' && !editLinkedSupplierId) { message.warning('请选择要关联的已有供应商'); return false; } if (editSupplierMode === 'new' && !(editSupplier.name || '').trim()) { message.warning('请填写供应商名称'); return false; } if (editSupplierMode !== 'none' && !(editSupplier.signingCompany || '').trim()) { message.warning('请选择签约公司'); return false; } if (isAdminUser && editSupplierMode !== 'none' && editStation.bindAccountIds && editStation.bindAccountIds.length) { var usedMapEdit = h2CollectUsedBindAccountMap(listData, editRecord && editRecord.id); var ei; for (ei = 0; ei < editStation.bindAccountIds.length; ei++) { var boundEditName = usedMapEdit[editStation.bindAccountIds[ei]]; if (boundEditName) { message.warning('系统账号已绑定站点「' + boundEditName + '」,请重新选择'); return false; } } } return true; }, [editStation, editRecord, editSupplierMode, editLinkedSupplierId, editSupplier, listData, isAdminUser]); var handleEditSubmit = useCallback(function () { if (editSubmitting || !editRecord) return; if (!validateEditForm()) return; setEditSubmitting(true); syncEditSupplierFromStation(); var now = h2OperateTimestamp(); var phoneDisplay = editStation.mobilePhone || editStation.landlinePhone || ''; if (editStation.mobilePhone && editStation.landlinePhone) phoneDisplay = editStation.mobilePhone; var nextBoundAccounts = []; var nextBindMode = editRecord.bindAccountMode || 'none'; var nextBindIds = h2NormalizeBindAccountIds(editRecord); if (editSupplierMode === 'none') { nextBindMode = 'none'; nextBindIds = []; nextBoundAccounts = []; } else if (isAdminUser) { nextBindIds = (editStation.bindAccountIds || []).slice(); nextBindMode = nextBindIds.length ? 'bind' : 'none'; nextBoundAccounts = h2BuildBoundAccountsFromIds(nextBindIds); } else { nextBoundAccounts = h2ResolveStationSystemAccounts(editRecord); nextBindIds = h2NormalizeBindAccountIds(editRecord); nextBindMode = editRecord.bindAccountMode || (nextBindIds.length ? 'bind' : 'none'); } var nextBoundAccount = nextBoundAccounts.length ? nextBoundAccounts[0] : null; var recordId = editRecord.id; setListData(function (prev) { return prev.map(function (r) { if (r.id !== recordId) return r; return Object.assign({}, r, { name: (editStation.name || '').trim(), region: editStation.address.region || [], addressDetail: (editStation.address.detail || '').trim(), fullAddress: h2BuildFullAddress(editStation.address.region, editStation.address.detail), isSigned: !!editStation.isSigned, contractStart: editStation.isSigned ? editStation.contractStart : '', contractEnd: editStation.isSigned ? editStation.contractEnd : '', contractFiles: (editStation.contractFiles || []).map(function (f) { return { uid: f.uid, name: f.name || '合同附件.pdf', url: f.url || '' }; }), businessHours: (editStation.businessHours || '').trim() || '—', contact: (editStation.contact || '').trim(), phone: phoneDisplay, mobilePhone: (editStation.mobilePhone || '').trim(), landlinePhone: (editStation.landlinePhone || '').trim(), bindAccountMode: nextBindMode, bindAccountIds: nextBindIds, boundAccountIds: nextBindIds, boundAccountId: nextBoundAccount ? nextBoundAccount.id : undefined, boundAccount: nextBoundAccount, boundAccounts: nextBoundAccounts, supplierMode: editSupplierMode, linkedSupplierId: editSupplierMode === 'link' ? editLinkedSupplierId : undefined, supplier: editSupplierMode === 'none' ? undefined : Object.assign({ type: '加氢站' }, editSupplier), updateTime: now }); }); }); window.setTimeout(function () { message.success('站点已保存(原型)'); setEditSubmitting(false); handleEditPageBack(); }, 320); }, [editSubmitting, editRecord, editStation, editSupplierMode, editLinkedSupplierId, editSupplier, validateEditForm, syncEditSupplierFromStation, isAdminUser, handleEditPageBack]); var openRefuelModal = useCallback(function (record) { setRefuelModal({ open: true, station: record, records: h2GetRefuelRecordsByStation(record.name) }); }, []); var closeRefuelModal = useCallback(function () { setRefuelModal({ open: false, station: null, records: [] }); }, []); var handleExportRefuelDrill = useCallback(function () { var records = refuelModal.records || []; if (!records.length) { message.warning('暂无加氢明细可导出'); return; } var stationName = (refuelModal.station && refuelModal.station.name) || ''; h2ExportRefuelDrillCsv(stationName, records); message.success('已导出 ' + records.length + ' 条加氢记录'); }, [refuelModal]); var openPrepaidBalanceDrill = useCallback(function (record) { setPrepaidBalanceDrill({ open: true, stationName: record.name || '', endingBalance: h2NumOrZero(record.prepaidBalance), rows: h2BuildMockPrepaidBalanceRows(record) }); }, []); var closePrepaidBalanceDrill = useCallback(function () { setPrepaidBalanceDrill({ open: false, stationName: '', endingBalance: 0, rows: [] }); }, []); var handleExportBalanceDrill = useCallback(function () { var rows = prepaidBalanceDrill.rows || []; if (!rows.length) { message.warning('暂无流水明细可导出'); return; } h2ExportBalanceDrillCsv(prepaidBalanceDrill.stationName, rows); message.success('已导出 ' + rows.length + ' 条流水记录'); }, [prepaidBalanceDrill]); var openStatementModal = useCallback(function (record) { var lastEnd = h2GetLastStatementEndDate(statementRecords, record.name); var defaultStart = lastEnd ? h2AddDays(lastEnd, 1) : '2026-05-01'; setStatementModal({ open: true, record: record, startDate: defaultStart, endDate: '2026-05-31', phase: 'select', draftRows: [], balanceAfterSettlement: h2NumOrZero(record.prepaidBalance), receiptDate: '', receiptAmount: '', invoiceFiles: [] }); }, [statementRecords]); var closeStatementModal = useCallback(function () { setStatementModal({ open: false, record: null, startDate: '', endDate: '', phase: 'select', draftRows: [], balanceAfterSettlement: 0, receiptDate: '', receiptAmount: '', invoiceFiles: [] }); }, []); var statementDraftSummary = useMemo(function () { return h2CalcStatementSummary(statementModal.draftRows || []); }, [statementModal.draftRows]); var handleExportStatement = useCallback(function () { var rows = statementModal.draftRows || []; if (!statementModal.record || !rows.length) { message.warning('暂无可导出的对账单明细'); return; } h2ExportStatementCsv(statementModal.record.name, statementModal.startDate, statementModal.endDate, rows); message.success('已导出 ' + rows.length + ' 条对账明细'); }, [statementModal]); var handleGenerateStatement = useCallback(function () { if (!statementModal.record) return; if (!statementModal.startDate || !statementModal.endDate) { message.warning('请选择账单开始日期和结束日期'); return; } var startMs = h2ParseDateOnlyMs(statementModal.startDate); var endMs = h2ParseDateOnlyMs(statementModal.endDate); if (isNaN(startMs) || isNaN(endMs)) { message.warning('日期格式不正确'); return; } if (startMs > endMs) { message.warning('开始日期不能晚于结束日期'); return; } var rows = h2GetAvailableStatementLedgerRows(ledgerStore, statementModal.record.name, statementModal.startDate, statementModal.endDate); if (!rows.length) { message.warning('所选日期范围内暂无「已对账」且未结算的加氢记录,请调整日期后重试'); return; } var summary = h2CalcStatementSummary(rows); var balanceAfter = Math.round((h2NumOrZero(statementModal.record.prepaidBalance) - summary.totalCost) * 100) / 100; setStatementModal(function (m) { return Object.assign({}, m, { phase: 'draft', draftRows: rows, balanceAfterSettlement: balanceAfter, receiptAmount: summary.totalCost ? h2FormatYuanNum(summary.totalCost) : '', receiptDate: '', invoiceFiles: [] }); }); message.success('已生成对账记录,共 ' + summary.count + ' 笔,请填写结算信息并提交'); }, [statementModal, ledgerStore]); var handleSubmitStatement = useCallback(function () { if (!statementModal.record || statementModal.phase !== 'draft') return; var rows = statementModal.draftRows || []; if (!rows.length) { message.warning('暂无对账明细可提交'); return; } if (!(statementModal.receiptDate || '').trim()) { message.warning('请填写收票日期'); return; } var receiptAmountNum = parseFloat(statementModal.receiptAmount); if (isNaN(receiptAmountNum) || receiptAmountNum <= 0) { message.warning('请填写有效的收票金额'); return; } if (!statementModal.invoiceFiles || !statementModal.invoiceFiles.length) { message.warning('请上传发票附件'); return; } var summary = h2CalcStatementSummary(rows); var operateTime = h2OperateTimestamp(); var operateDate = h2FormatOperateDateOnly(); var statementId = 'stmt-' + Date.now(); var recordIds = rows.map(function (r) { return r.id; }); var stationRecord = statementModal.record; var balanceAfter = h2NumOrZero(statementModal.balanceAfterSettlement); var newStatement = { id: statementId, stationId: stationRecord.id, stationName: stationRecord.name, reconcileDate: operateTime, reconciler: H2_CURRENT_OPERATOR, startDate: statementModal.startDate, endDate: statementModal.endDate, refuelCount: summary.count, totalKg: summary.totalKg, totalCost: summary.totalCost, balanceAfterSettlement: balanceAfter, receiptDate: statementModal.receiptDate, receiptAmount: receiptAmountNum, invoiceFiles: (statementModal.invoiceFiles || []).slice(), recordIds: recordIds }; var patches = []; setLedgerStore(function (prev) { return prev.map(function (r) { if (recordIds.indexOf(r.id) < 0) return r; patches.push({ ledgerId: r.id, orderNo: r.orderNo, stationName: r.stationName, plateNo: r.plateNo, hydrogenTime: r.hydrogenTime, reconcileDate: operateTime, receiptDate: operateDate, paymentStatus: H2_PAYMENT_STATUS_PAID }); return Object.assign({}, r, { statementRecordId: statementId, reconcileDate: operateTime, receiptDate: operateDate, paymentStatus: H2_PAYMENT_STATUS_PAID }); }); }); setStatementRecords(function (prev) { return [newStatement].concat(prev); }); setListData(function (prev) { return prev.map(function (item) { if (item.id !== stationRecord.id) return item; return Object.assign({}, item, { prepaidBalance: balanceAfter, updateTime: operateTime }); }); }); h2PushStatementLedgerPatches(patches); if (typeof window !== 'undefined' && window.H2_VEHICLE_LEDGER_API && typeof window.H2_VEHICLE_LEDGER_API.applyPatches === 'function') { window.H2_VEHICLE_LEDGER_API.applyPatches(patches); } message.success('对账单已提交,共结算 ' + summary.count + ' 笔加氢记录'); closeStatementModal(); }, [statementModal, closeStatementModal]); var openStatementHistoryModal = useCallback(function (record) { setStatementHistoryModal({ open: true, record: record }); }, []); var closeStatementHistoryModal = useCallback(function () { setStatementHistoryModal({ open: false, record: null }); }, []); var openStatementDetailModal = useCallback(function (statementRecord) { var detailRows = h2GetLedgerRowsByIds(ledgerStore, statementRecord.recordIds); setStatementDetailModal({ open: true, statementRecord: statementRecord, detailRows: detailRows }); }, [ledgerStore]); var closeStatementDetailModal = useCallback(function () { setStatementDetailModal({ open: false, statementRecord: null, detailRows: [] }); }, []); var openEdit = useCallback(function (record) { var formSnapshot = h2RecordToEditStationForm(record); var supplierMode = h2ResolveRecordSupplierMode(record); var linkedSupplier = record.linkedSupplierId; var supplierForm = supplierMode === 'none' ? h2CreateEmptySupplierForm() : h2SupplierToForm(h2ResolveStationSupplier(record)); setEditStation(formSnapshot); setEditOriginForm(JSON.parse(JSON.stringify(formSnapshot))); setEditSupplierMode(supplierMode); setEditLinkedSupplierId(linkedSupplier); setEditSupplier(supplierForm); setEditRecord(record); setSubView('edit'); }, []); var openView = useCallback(function (record) { setViewModal({ open: true, record: record }); }, []); var closeViewModal = useCallback(function () { setViewModal({ open: false, record: null }); setFilePreviewModal({ open: false, url: '', name: '', type: 'image' }); }, []); var openFilePreview = useCallback(function (file, variant) { if (!file) return; var preview = h2ResolveUploadPreview(file, variant || 'doc'); if (preview.type === 'image' && Image && typeof Image.preview === 'function') { Image.preview({ src: preview.url }); return; } setFilePreviewModal({ open: true, url: preview.url, name: preview.name, type: preview.type }); }, [Image]); var closeFilePreviewModal = useCallback(function () { setFilePreviewModal({ open: false, url: '', name: '', type: 'image' }); }, []); var openBusinessSetting = useCallback(function (record) { setBusinessModal({ open: true, record: record, businessStatus: record.businessStatus || '营业中', businessHours: record.businessHours === '—' ? '' : (record.businessHours || ''), originBusinessStatus: record.businessStatus || '营业中', statusLogs: (record.businessStatusLogs || []).slice() }); }, []); var closeBusinessModal = useCallback(function () { setBusinessModal({ open: false, record: null, businessStatus: '营业中', businessHours: '', originBusinessStatus: '营业中', statusLogs: [] }); }, []); var handleSaveBusinessSetting = useCallback(function () { if (!businessModal.record) return; var hoursParsed = h2ParseBusinessHours(businessModal.businessHours); if (hoursParsed.mode === 'custom') { if (!(hoursParsed.start || '').trim() || !(hoursParsed.end || '').trim()) { message.warning('非全天营业请选择开始与结束时间'); return; } if (!/^(\d{1,2}):(\d{2})$/.test(h2NormalizeTimeText(hoursParsed.start)) || !/^(\d{1,2}):(\d{2})$/.test(h2NormalizeTimeText(hoursParsed.end))) { message.warning('营业时间格式须为 HH:mm'); return; } } var nextStatus = businessModal.businessStatus || '营业中'; var statusChanged = nextStatus !== businessModal.originBusinessStatus; var newLog = statusChanged ? h2CreateBusinessStatusLog(businessModal.originBusinessStatus, nextStatus, H2_CURRENT_OPERATOR) : null; setListData(function (prev) { return prev.map(function (r) { if (r.id !== businessModal.record.id) return r; var logs = (r.businessStatusLogs || []).slice(); if (newLog) logs.unshift(newLog); return Object.assign({}, r, { businessStatus: nextStatus, businessHours: (businessModal.businessHours || '').trim() || '—', businessStatusLogs: logs, updateTime: h2OperateTimestamp() }); }); }); message.success(statusChanged ? '营业状态已更新(原型)' : '营业时间已保存(原型)'); closeBusinessModal(); }, [businessModal, closeBusinessModal]); var openPriceConfig = useCallback(function (record) { setPriceModal({ open: true, record: record, costUnitPrice: '', effectiveTime: dayjs ? dayjs().format('YYYY-MM-DD HH:mm') : h2OperateTimestamp(), priceLogs: (record.costPriceLogs || []).slice() }); }, []); var closePriceModal = useCallback(function () { setPriceModal({ open: false, record: null, costUnitPrice: '', effectiveTime: '', priceLogs: [] }); }, []); var handleSavePriceConfig = useCallback(function () { if (!priceModal.record) return; var costText = (priceModal.costUnitPrice || '').trim(); var effectiveText = (priceModal.effectiveTime || '').trim(); if (!costText) { message.warning('请填写成本价格'); return; } var cost = parseFloat(costText); if (isNaN(cost) || cost < 0) { message.warning('请输入有效的成本价格(元/kg)'); return; } if (!effectiveText) { message.warning('请选择生效时间'); return; } var beforePrice = h2ResolveCurrentCostPrice(priceModal.record); var newLog = h2CreateCostPriceLog(beforePrice, cost, effectiveText, H2_CURRENT_OPERATOR); var recordId = priceModal.record.id; setListData(function (prev) { return prev.map(function (r) { if (r.id !== recordId) return r; var logs = [newLog].concat(r.costPriceLogs || []); var updated = Object.assign({}, r, { costPriceLogs: logs, updateTime: h2OperateTimestamp() }); return h2ApplyDueCostPriceToRecord(updated); }); }); var appliedNow = h2ParseDateTimeMs(effectiveText) <= Date.now(); message.success(appliedNow ? '价格配置已保存,当前成本价格已更新(原型)' : '价格配置已保存,将于生效时间自动更新(原型)'); closePriceModal(); }, [priceModal, closePriceModal]); var openBalanceAlertSetting = useCallback(function (record) { var threshold = h2ResolveBalanceAlertThreshold(record); setBalanceAlertModal({ open: true, record: record, threshold: threshold != null ? h2FormatYuanNum(threshold) : '' }); }, []); var closeBalanceAlertModal = useCallback(function () { setBalanceAlertModal({ open: false, record: null, threshold: '' }); }, []); var handleSaveBalanceAlert = useCallback(function () { if (!balanceAlertModal.record) return; var thresholdText = h2FormatReceiptAmountInput(balanceAlertModal.threshold); if (!thresholdText) { message.warning('请填写有效的提醒金额'); return; } var thresholdNum = parseFloat(thresholdText); if (isNaN(thresholdNum) || thresholdNum <= 0) { message.warning('提醒金额须大于 0'); return; } var recordId = balanceAlertModal.record.id; setListData(function (prev) { return prev.map(function (r) { if (r.id !== recordId) return r; return Object.assign({}, r, { balanceAlertThreshold: thresholdNum, updateTime: h2OperateTimestamp() }); }); }); message.success('余额提醒设置已保存(原型)'); closeBalanceAlertModal(); }, [balanceAlertModal, closeBalanceAlertModal]); var openRechargeModal = useCallback(function (stations) { var lines = []; if (stations && stations.length) { lines = stations.map(function (r) { return h2BuildRechargeLineFromStation(r); }); } else { lines = [h2CreateEmptyRechargeLine()]; } setRechargeModal({ open: true, lines: lines }); }, []); var closeRechargeModal = useCallback(function () { setRechargeModal({ open: false, lines: [] }); }, []); var updateRechargeLineField = useCallback(function (lineId, field, value) { setRechargeModal(function (m) { return Object.assign({}, m, { lines: m.lines.map(function (line) { if (line.id !== lineId) return line; return Object.assign({}, line, { [field]: value }); }) }); }); }, []); var handleRechargeStationSelect = useCallback(function (lineId, stationId) { if (!stationId) { setRechargeModal(function (m) { return Object.assign({}, m, { lines: m.lines.map(function (line) { if (line.id !== lineId) return line; var empty = h2CreateEmptyRechargeLine(); empty.id = lineId; empty.payAmount = line.payAmount; return empty; }) }); }); return; } var record = listData.filter(function (r) { return r.id === stationId; })[0]; if (!record) return; setRechargeModal(function (m) { return Object.assign({}, m, { lines: m.lines.map(function (line) { if (line.id !== lineId) return line; var built = h2BuildRechargeLineFromStation(record, line.payAmount); built.id = lineId; return built; }) }); }); }, [listData]); var addRechargeLine = useCallback(function () { setRechargeModal(function (m) { return Object.assign({}, m, { lines: m.lines.concat([h2CreateEmptyRechargeLine()]) }); }); }, []); var removeRechargeLine = useCallback(function (lineId) { setRechargeModal(function (m) { if (m.lines.length <= 1) return m; return Object.assign({}, m, { lines: m.lines.filter(function (l) { return l.id !== lineId; }) }); }); }, []); var handleSubmitRecharge = useCallback(function () { var lines = rechargeModal.lines || []; var valid = []; var i; for (i = 0; i < lines.length; i++) { var line = lines[i]; if (!line.stationId) continue; var payText = h2FormatReceiptAmountInput(line.payAmount); var payNum = parseFloat(payText); if (!payText || isNaN(payNum) || payNum <= 0) continue; valid.push(line); } if (!valid.length) { message.warning('请至少添加一条有效的付款信息(已选站点且付款金额大于 0)'); return; } var missingSupplier = valid.filter(function (l) { return !(l.companyName || '').trim() || !(l.bankAccount || '').trim() || !(l.bankName || '').trim(); }); if (missingSupplier.length) { message.warning('部分站点未关联供应商或缺少收款信息,请补全后再发起'); return; } message.success('已发起审批流程'); closeRechargeModal(); }, [rechargeModal, closeRechargeModal]); var openAlertStationModal = useCallback(function (type) { var title = type === 'balanceAlert' ? '预付余额预警站点' : '已欠费站点'; var stations = listData.filter(function (r) { if (type === 'balanceAlert') return h2IsBalanceAlertStation(r); if (type === 'arrears') return h2IsArrearsStation(r); return false; }); setAlertStationModal({ open: true, type: type, title: title, stations: h2EnrichStationListRows(stations) }); }, [listData]); var closeAlertStationModal = useCallback(function () { setAlertStationModal({ open: false, type: '', title: '', stations: [] }); }, []); var handleBatchRechargeFromAlert = useCallback(function () { var stations = alertStationModal.stations || []; if (!stations.length) { message.warning('暂无站点可发起充值'); return; } var type = alertStationModal.type; closeAlertStationModal(); openRechargeModal(stations); if (type === 'balanceAlert') message.info('已载入 ' + stations.length + ' 个预警站点,请填写付款金额后发起'); else if (type === 'arrears') message.info('已载入 ' + stations.length + ' 个欠费站点,请填写付款金额后发起'); }, [alertStationModal, closeAlertStationModal, openRechargeModal]); var getRowMoreMenuItems = useCallback(function (record) { return [ { key: 'edit', label: '编辑', onClick: function () { openEdit(record); } }, { key: 'business', label: '营业状态', onClick: function () { openBusinessSetting(record); } }, { key: 'price', label: '价格配置', onClick: function () { openPriceConfig(record); } }, { key: 'balanceAlert', label: '余额提醒设置', onClick: function () { openBalanceAlertSetting(record); } }, { key: 'statement', label: '生成对账单', onClick: function () { openStatementModal(record); } }, { key: 'statementHistory', label: '查看对账记录', onClick: function () { openStatementHistoryModal(record); } }, { type: 'divider' }, { key: 'delete', label: '删除', danger: true, onClick: function () { setDeleteModal({ open: true, record: record }); } } ]; }, [openEdit, openBusinessSetting, openPriceConfig, openBalanceAlertSetting, openStatementModal, openStatementHistoryModal]); var closeImportModal = useCallback(function () { setImportModalOpen(false); setImportFileList([]); setImportPreview(null); }, []); var downloadImportTemplate = useCallback(function () { h2DownloadCsv('加氢站点批量导入模板.csv', H2_IMPORT_TEMPLATE_HEADERS, H2_IMPORT_TEMPLATE_SAMPLE); message.success('已下载导入模板'); }, []); var parseImportFile = useCallback(function (file) { if (!file) return; var name = String(file.name || '').toLowerCase(); if (name.endsWith('.xls') || name.endsWith('.xlsx')) { message.warning('原型环境请使用 CSV 模板导入;联调后将支持 Excel 直接上传'); setImportFileList([file]); setImportPreview(null); return; } if (!name.endsWith('.csv')) { message.error('仅支持 .csv、.xls、.xlsx 格式'); return; } setImportFileList([file]); var reader = new FileReader(); reader.onload = function (ev) { var text = (ev && ev.target && ev.target.result) || ''; var parsed = h2ParseImportCsv(text); if (parsed.headerError) { message.error(parsed.headerError); setImportPreview(null); return; } if (!parsed.rows.length) { message.warning('未解析到有效数据,请检查文件内容'); setImportPreview(null); return; } var nameMap = Object.assign({}, existingNameMap); var valid = []; var errors = []; var ri; for (ri = 0; ri < parsed.rows.length; ri++) { var row = parsed.rows[ri]; var rowErrors = h2ValidateImportRow(row, nameMap); if (rowErrors.length) { errors.push({ lineNo: row.lineNo, name: row.name || '—', reasons: rowErrors }); } else { valid.push(row); if (row.name) nameMap[String(row.name).trim()] = true; } } setImportPreview({ valid: valid, errors: errors, fileName: file.name }); }; reader.onerror = function () { message.error('文件读取失败'); }; reader.readAsText(file, 'UTF-8'); }, [existingNameMap]); var handleConfirmImport = useCallback(function () { if (!importPreview || !importPreview.valid.length) { message.warning('没有可导入的有效数据'); return; } setListData(function (prev) { var maxId = prev.reduce(function (m, r) { return Math.max(m, r.id || 0); }, 0); var imported = importPreview.valid.map(function (row, idx) { return h2ImportRowToRecord(row, maxId + idx + 1); }); return imported.concat(prev); }); var errN = (importPreview.errors || []).length; if (errN) { message.success('成功导入 ' + importPreview.valid.length + ' 条,' + errN + ' 条校验失败已跳过'); } else { message.success('成功导入 ' + importPreview.valid.length + ' 条站点'); } setPage(1); closeImportModal(); }, [importPreview, closeImportModal]); React.useEffect(function () { var maxPage = Math.max(1, Math.ceil(totalCount / pageSize) || 1); if (page > maxPage) setPage(maxPage); }, [totalCount, pageSize, page]); React.useEffect(function () { var applyDuePrices = function () { setListData(function (prev) { var result = h2ApplyDueCostPricesToList(prev); return result.changed ? result.records : prev; }); }; applyDuePrices(); var timer = window.setInterval(applyDuePrices, 60000); return function () { window.clearInterval(timer); }; }, []); var renderRefuelFreqShortTag = function (freqKey) { if (freqKey === 'high') { return React.createElement(Tag, { color: 'success', className: 'lc-refuel-freq-tag' }, '高频'); } if (freqKey === 'low') { return React.createElement(Tag, { color: 'warning', className: 'lc-refuel-freq-tag' }, '低频'); } return null; }; var renderBusinessStatusTag = function (v) { if (v === '营业中') return React.createElement(Tag, { color: 'success', style: { borderRadius: 6, fontWeight: 600 } }, v); if (v === '暂停营业') return React.createElement(Tag, { color: 'warning', style: { borderRadius: 6, fontWeight: 600 } }, v); if (v === '停止营业') return React.createElement(Tag, { color: 'error', style: { borderRadius: 6, fontWeight: 600 } }, v); return React.createElement(Tag, { style: { borderRadius: 6, fontWeight: 600, color: '#64748b', background: '#f1f5f9', border: '1px solid #e2e8f0' } }, v || '—'); }; var renderBusinessStatusText = function (v, tone) { var color = v === '营业中' ? '#059669' : v === '暂停营业' ? '#ea580c' : v === '停止营业' ? '#dc2626' : '#64748b'; if (tone === 'muted') color = '#94a3b8'; return React.createElement('span', { style: { fontWeight: 600, color: color } }, v || '—'); }; var renderRefuelDrillLink = function (text, record, ariaLabel) { return React.createElement('button', { type: 'button', className: 'h2-refuel-drill-link', onClick: function (e) { e.stopPropagation(); openRefuelModal(record); }, 'aria-label': ariaLabel }, text); }; var renderPrepaidBalance = useCallback(function (v, record) { if (v == null || v === '') return React.createElement('span', { style: { color: '#94a3b8' } }, '—'); var n = typeof v === 'number' ? v : parseFloat(v); if (isNaN(n)) return '—'; if (!record) { var plainColor = n < 0 ? '#dc2626' : '#0f172a'; return React.createElement('span', { style: { fontWeight: 700, color: plainColor, fontVariantNumeric: 'tabular-nums' }, title: n < 0 ? '预付余额不足,已欠费' : undefined }, h2FormatYuanNum(n)); } var isArrears = n < 0; var amountStyle = isArrears ? { color: '#dc2626', fontVariantNumeric: 'tabular-nums' } : { color: '#059669', fontVariantNumeric: 'tabular-nums' }; return React.createElement('div', { className: 'h2-prepaid-balance-cell' }, isArrears ? React.createElement(Tag, { color: 'error', className: 'lc-station-signed-tag' }, '已欠费') : null, React.createElement('button', { type: 'button', className: 'h2-prepaid-balance-amount', style: amountStyle, title: '点击查看余额变更明细', 'aria-label': '查看「' + (record.name || '') + '」余额变更明细', onClick: function (e) { e.stopPropagation(); openPrepaidBalanceDrill(record); } }, h2FormatYuanNum(n)) ); }, [openPrepaidBalanceDrill]); var renderBalanceDrillMoney = function (text, tone, rawValue) { if (text === '—') { return React.createElement('span', { className: 'h2-balance-money h2-balance-money--muted' }, '—'); } var className = 'h2-balance-money h2-balance-money--' + tone; if (tone === 'balance' && rawValue != null && h2NumOrZero(rawValue) < 0) className += ' h2-balance-money--negative'; return React.createElement('span', { className: className }, text); }; var prepaidBalanceDrillColumns = [ { title: '序号', key: 'seq', width: 52, align: 'center', render: function (_, __, index) { return React.createElement('span', { className: 'h2-balance-seq' }, index + 1); } }, { title: '时间', dataIndex: 'changeTime', key: 'changeTime', width: 158, render: function (v) { return React.createElement('span', { style: { color: '#334155', fontVariantNumeric: 'tabular-nums', fontSize: 12, whiteSpace: 'nowrap' } }, v || '—'); } }, { title: '类型', key: 'bizType', width: 92, align: 'center', render: function (_, row) { var typeInfo = h2ResolveBalanceBizType(row); return React.createElement(Tag, { color: typeInfo.color, style: { margin: 0, borderRadius: 6, fontWeight: 600, fontSize: 12 } }, typeInfo.label); } }, { title: '收入(元)', dataIndex: 'incomeAmount', key: 'incomeAmount', width: 120, align: 'right', render: function (v) { return renderBalanceDrillMoney(h2FormatYuanSymbol(v), 'income'); } }, { title: '支出(元)', dataIndex: 'expenseAmount', key: 'expenseAmount', width: 120, align: 'right', render: function (v) { return renderBalanceDrillMoney(h2FormatYuanSymbol(v), 'expense'); } }, { title: '余额(元)', dataIndex: 'balance', key: 'balance', width: 128, align: 'right', render: function (v) { return renderBalanceDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'balance', v); } }, { title: '订单编号', dataIndex: 'orderNo', key: 'orderNo', width: 148, ellipsis: true, render: function (v) { if (!v) return React.createElement('span', { className: 'h2-balance-money--muted' }, '—'); return React.createElement('span', { className: 'h2-balance-order-no', title: v }, v); } } ]; var prepaidBalanceDrillSummary = useMemo(function () { if (!prepaidBalanceDrill.rows || !prepaidBalanceDrill.rows.length) { return { incomeTotal: 0, expenseTotal: 0, currentBalance: 0 }; } var last = prepaidBalanceDrill.rows[prepaidBalanceDrill.rows.length - 1]; return prepaidBalanceDrill.rows.reduce(function (acc, r) { acc.incomeTotal += h2NumOrZero(r.incomeAmount); acc.expenseTotal += h2NumOrZero(r.expenseAmount); return acc; }, { incomeTotal: 0, expenseTotal: 0, currentBalance: h2NumOrZero(last.balance) }); }, [prepaidBalanceDrill.rows]); var prepaidBalanceTrendSeries = useMemo(function () { return h2BuildBalanceTrendSeries(prepaidBalanceDrill.rows || []); }, [prepaidBalanceDrill.rows]); var renderPrepaidBalanceDrillPanel = useCallback(function () { var rows = prepaidBalanceDrill.rows || []; var summary = prepaidBalanceDrillSummary; var trendSeries = prepaidBalanceTrendSeries; var balanceNegative = h2NumOrZero(summary.currentBalance) < 0; var balanceText = h2FormatYuanSymbol(summary.currentBalance, { keepZero: true }); var stats = [ { key: 'balance', label: '当前预付余额', value: balanceText, mod: 'balance', valueMod: balanceNegative ? 'negative' : 'income' }, { key: 'income', label: '收入合计', value: h2FormatYuanSymbol(summary.incomeTotal, { keepZero: true }), mod: 'income', valueMod: 'income' }, { key: 'expense', label: '支出合计', value: h2FormatYuanSymbol(summary.expenseTotal, { keepZero: true }), mod: 'expense', valueMod: 'expense' } ]; return React.createElement('div', { className: 'h2-balance-drill-panel' }, React.createElement('div', { className: 'h2-balance-drill-stats', role: 'group', 'aria-label': '余额统计' }, stats.map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-balance-drill-stat h2-balance-drill-stat--' + item.mod, role: 'article', 'aria-label': item.label + ' ' + item.value }, React.createElement('div', { className: 'h2-balance-drill-stat__label' }, item.label), React.createElement('div', { className: 'h2-balance-drill-stat__value' + (item.valueMod ? ' h2-balance-drill-stat__value--' + item.valueMod : '') }, item.value) ); }) ), React.createElement('div', { className: 'h2-balance-trend-wrap' }, React.createElement('div', { className: 'h2-balance-trend-head' }, React.createElement('span', { className: 'h2-balance-trend-head__title' }, '余额趋势'), React.createElement('span', { className: 'h2-balance-trend-head__meta' }, (trendSeries.startDate || '—') + ' 至 ' + (trendSeries.endDate || '—') + ' · 按日 · 悬浮查看明细' ) ), React.createElement('div', { className: 'h2-balance-trend-body' }, React.createElement(H2BalanceTrendChart, { series: trendSeries }) ), rows.length ? React.createElement('div', { className: 'h2-balance-trend-legend' }, React.createElement('span', { className: 'h2-balance-trend-legend__item' }, React.createElement('span', { className: 'h2-balance-trend-legend__dot h2-balance-trend-legend__dot--line' }), '余额走势' ), React.createElement('span', { className: 'h2-balance-trend-legend__item' }, React.createElement('span', { className: 'h2-balance-trend-legend__dot h2-balance-trend-legend__dot--area' }), '区间填充' ), trendSeries.hasNegative ? React.createElement('span', { className: 'h2-balance-trend-legend__item', style: { color: '#dc2626' } }, '红点表示欠费时点') : null ) : null ), React.createElement('div', { className: 'h2-balance-drill-table-wrap' }, React.createElement('div', { className: 'h2-balance-drill-table-head' }, React.createElement('div', { className: 'h2-balance-drill-table-head__left' }, React.createElement('span', { className: 'h2-balance-drill-table-head__title' }, '流水明细'), React.createElement('span', { className: 'h2-balance-drill-table-head__count' }, '共 ' + rows.length + ' 条') ), React.createElement(Button, { size: 'small', icon: H2_ICONS.download, onClick: handleExportBalanceDrill, style: { borderRadius: 8, fontWeight: 600, borderColor: '#10b981', color: '#059669' }, 'aria-label': '导出流水明细' }, '导出') ), React.createElement(Table, { className: 'h2-balance-record-table', size: 'small', bordered: false, rowKey: 'key', columns: prepaidBalanceDrillColumns, dataSource: rows, pagination: rows.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无余额变更记录' }, scroll: rows.length > 8 ? { x: 'max-content', y: 320 } : { x: 'max-content' } }) ) ); }, [prepaidBalanceDrill, prepaidBalanceDrillSummary, prepaidBalanceTrendSeries, prepaidBalanceDrillColumns, handleExportBalanceDrill]); var renderStationViewPanel = useCallback(function () { var record = viewModal.record; if (!record) return null; var supplier = h2ResolveStationSupplier(record); var descColumn = { xs: 1, sm: 1, md: 2, lg: 2, xl: 2, xxl: 2 }; var price = h2ResolveCurrentCostPrice(record); var balanceNegative = h2NumOrZero(record.prepaidBalance) < 0; var balanceText = h2FormatYuanSymbol(record.prepaidBalance, { keepZero: true }); var renderText = function (v) { return v != null && v !== '' ? v : '—'; }; var renderViewContractFiles = function (files) { if (!files || !files.length) return '—'; return React.createElement('div', { className: 'h2-station-view-files' }, files.map(function (f, idx) { return React.createElement('button', { key: f.uid || f.name || idx, type: 'button', className: 'h2-station-view-file-link', title: '点击预览 ' + (f.name || '附件'), onClick: function () { openFilePreview(f, 'doc'); } }, f.name || '附件'); }) ); }; var renderViewLicenseImages = function (files, opts) { opts = opts || {}; if (!files || !files.length) { return React.createElement('span', { className: 'h2-station-view-license-empty' }, '未上传'); } return React.createElement('div', { className: 'h2-station-view-license-block' }, opts.showCount ? React.createElement('div', { className: 'h2-station-view-license-count' }, '共 ' + files.length + ' 张') : null, React.createElement('div', { className: 'h2-station-view-license-images' }, files.map(function (f, idx) { var imgUrl = h2ResolveLicenseDisplayUrl(f); return React.createElement('button', { key: f.uid || f.name || idx, type: 'button', className: 'h2-station-view-license-thumb', title: '点击预览 ' + (f.name || '证照'), onClick: function () { openFilePreview(f, 'license'); } }, React.createElement('img', { src: imgUrl, alt: f.name || '证照图片' }) ); }) ) ); }; var renderViewSection = function (title, items) { return React.createElement('div', { className: 'h2-station-view-section' }, React.createElement('div', { className: 'h2-station-view-section__title' }, title), React.createElement(Descriptions, { bordered: true, size: 'small', column: descColumn }, items.map(function (item) { return React.createElement(Descriptions.Item, { key: item.key, label: item.label, span: item.span || 1 }, item.value); }) ) ); }; var renderEmptySection = function (title, hint) { return React.createElement('div', { className: 'h2-station-view-section' }, React.createElement('div', { className: 'h2-station-view-section__title' }, title), React.createElement('div', { className: 'h2-station-view-empty' }, hint) ); }; var contractValue = '—'; if (record.isSigned) { contractValue = React.createElement('div', null, React.createElement('span', null, (record.contractStart || '—') + ' 至 ' + (record.contractEnd || '—')), record.contractEnd ? React.createElement('div', { style: { marginTop: 4 } }, h2RenderContractRemainTag(record.contractEnd)) : null ); } var stationItems = [ { key: 'name', label: '加氢站名称', value: renderText(record.name) }, { key: 'region', label: '省 / 市', value: renderText(h2FormatRegion(record.region)) }, { key: 'detail', label: '详细地址', value: renderText(record.addressDetail), span: 2 }, { key: 'status', label: '营业状态', value: renderBusinessStatusTag(record.businessStatus) }, { key: 'hours', label: '营业时间', value: renderText(h2DisplayBusinessHours(record.businessHours)) }, { key: 'signed', label: '是否签约', value: record.isSigned ? '已签约' : '未签约' } ]; if (record.isSigned) { stationItems.push( { key: 'contract', label: '合作时间', value: contractValue, span: 2 }, { key: 'files', label: '签约附件', value: renderViewContractFiles(record.contractFiles), span: 2 } ); } stationItems.push( { key: 'contact', label: '联系人', value: renderText(record.contact) }, { key: 'phone', label: '联系电话', value: renderText(record.phone) }, { key: 'balance', label: '预付余额', value: React.createElement('span', { style: { fontWeight: 700, color: balanceNegative ? '#dc2626' : '#059669', fontVariantNumeric: 'tabular-nums' } }, balanceText) }, { key: 'price', label: '当前成本价格', value: price != null ? h2FormatYuanSymbol(price, { keepZero: true }) + ' / kg' : '—' } ); var supplierItems = supplier ? [ { key: 'name', label: '供应商名称', value: renderText(supplier.name), span: 2 }, { key: 'signingCompany', label: '签约公司', value: renderText(supplier.signingCompany), span: 2 }, { key: 'type', label: '供应商类型', value: renderText(supplier.type || '加氢站') }, { key: 'city', label: '省 / 市', value: renderText(h2FormatRegion(supplier.city)) }, { key: 'address', label: '详细地址', value: renderText(supplier.address), span: 2 }, { key: 'region', label: '所属区域', value: renderText(supplier.region) }, { key: 'dept', label: '归属部门', value: renderText(supplier.dept) }, { key: 'manager', label: '负责人', value: renderText(supplier.manager) }, { key: 'contactName', label: '联系人', value: renderText(supplier.contactName) }, { key: 'contactMobile', label: '联系电话', value: renderText(supplier.contactMobile) }, { key: 'bl', label: '营业执照', value: renderViewLicenseImages(supplier.businessLicenseFiles), span: 2 }, { key: 'fl', label: '其他证照', value: renderViewLicenseImages(supplier.fillingLicenseFiles, { showCount: true }), span: 2 } ] : []; var paymentItems = supplier ? [ { key: 'taxId', label: '纳税人识别号', value: renderText(supplier.taxId), span: 2 }, { key: 'invoicePhone', label: '注册电话', value: renderText(supplier.invoicePhone) }, { key: 'invoiceAddress', label: '注册地址', value: renderText(supplier.invoiceAddress), span: 2 }, { key: 'bankName', label: '开户行', value: renderText(supplier.bankName), span: 2 }, { key: 'bankAccount', label: '银行账号', value: renderText(supplier.bankAccount), span: 2 }, { key: 'businessAddress', label: '营业地址', value: renderText(supplier.businessAddress), span: 2 } ] : []; return React.createElement('div', { className: 'h2-station-view-panel' }, renderViewSection('加氢站信息', stationItems), supplier ? renderViewSection('供应商信息', supplierItems) : renderEmptySection('供应商信息', '暂未关联供应商'), supplier ? renderViewSection('付款信息', paymentItems) : renderEmptySection('付款信息', '暂无付款信息') ); }, [viewModal.record, openFilePreview]); var renderRefuelDrillMoney = function (text, tone) { return React.createElement('span', { className: 'h2-refuel-drill-money h2-refuel-drill-money--' + tone }, text); }; var refuelRecordColumns = [ { title: '序号', key: 'seq', width: 52, align: 'center', render: function (_, __, index) { return React.createElement('span', { className: 'h2-refuel-drill-seq' }, index + 1); } }, { title: '加氢时间', dataIndex: 'hydrogenTime', key: 'hydrogenTime', width: 158, render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-time' }, v || '—'); } }, { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 108, render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-plate' }, v || '—'); } }, { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 168, ellipsis: true }, { title: '加氢量(kg)', dataIndex: 'hydrogenKg', key: 'hydrogenKg', width: 96, align: 'right', render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-kg' }, h2FormatKgNum(v)); } }, { title: '成本单价(元/kg)', dataIndex: 'costUnitPrice', key: 'costUnitPrice', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'cost'); } }, { title: '成本总价(元)', dataIndex: 'costAmount', key: 'costAmount', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'cost'); } }, { title: '加氢单价(元/kg)', dataIndex: 'customerUnitPrice', key: 'customerUnitPrice', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'customer'); } }, { title: '加氢总价(元)', dataIndex: 'customerAmount', key: 'customerAmount', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'customer'); } }, { title: '承担方式', dataIndex: 'settlementStatus', key: 'settlementStatus', width: 120, render: function (v) { var info = H2_SETTLEMENT_STATUS_MAP[v]; if (!info) return '—'; return React.createElement(Tag, { color: info.color, style: { margin: 0, borderRadius: 6, fontWeight: 600 } }, info.label); } }, { title: '订单编号', dataIndex: 'orderNo', key: 'orderNo', width: 148, align: 'center', ellipsis: true, render: function (v) { if (!v) return React.createElement('span', { style: { color: '#cbd5e1' } }, '—'); return React.createElement('span', { className: 'h2-refuel-drill-order-no', title: v }, v); } } ]; var refuelModalSummary = useMemo(function () { var records = refuelModal.records || []; var totalKg = 0; var totalCost = 0; var totalCustomer = 0; var i; for (i = 0; i < records.length; i++) { totalKg += records[i].hydrogenKg || 0; totalCost += records[i].costAmount || 0; totalCustomer += records[i].customerAmount || 0; } return { count: records.length, totalKg: totalKg, totalCost: totalCost, totalCustomer: totalCustomer }; }, [refuelModal.records]); var renderRefuelDrillPanel = useCallback(function () { var records = refuelModal.records || []; var summary = refuelModalSummary; var stationName = (refuelModal.station && refuelModal.station.name) || '—'; var stats = [ { key: 'count', label: '加氢次数', value: String(summary.count || 0), mod: 'count', valueMod: 'count' }, { key: 'kg', label: '加氢量合计(kg)', value: h2FormatKgNum(summary.totalKg), mod: 'kg', valueMod: 'kg' }, { key: 'cost', label: '成本总价', value: h2FormatYuanSymbol(summary.totalCost, { keepZero: true }), mod: 'cost', valueMod: 'cost' }, { key: 'customer', label: '加氢总价', value: h2FormatYuanSymbol(summary.totalCustomer, { keepZero: true }), mod: 'customer', valueMod: 'customer' } ]; return React.createElement('div', { className: 'h2-refuel-drill-panel' }, React.createElement('div', { className: 'h2-refuel-drill-station-card' }, React.createElement('div', { className: 'h2-refuel-drill-station-card__name' }, stationName), React.createElement('div', { className: 'h2-refuel-drill-station-card__meta' }, '车辆加氢记录 · 共 ' + records.length + ' 笔') ), React.createElement('div', { className: 'h2-refuel-drill-stats', role: 'group', 'aria-label': '加氢统计' }, stats.map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-refuel-drill-stat h2-refuel-drill-stat--' + item.mod, role: 'article', 'aria-label': item.label + ' ' + item.value }, React.createElement('div', { className: 'h2-refuel-drill-stat__label' }, item.label), React.createElement('div', { className: 'h2-refuel-drill-stat__value h2-refuel-drill-stat__value--' + item.valueMod }, item.value) ); }) ), React.createElement('div', { className: 'h2-refuel-drill-table-wrap' }, React.createElement('div', { className: 'h2-refuel-drill-table-head' }, React.createElement('div', { className: 'h2-refuel-drill-table-head__left' }, React.createElement('span', { className: 'h2-refuel-drill-table-head__title' }, '加氢明细'), React.createElement('span', { className: 'h2-refuel-drill-table-head__count' }, '共 ' + records.length + ' 条') ), React.createElement(Button, { size: 'small', icon: H2_ICONS.download, onClick: handleExportRefuelDrill, style: { borderRadius: 8, fontWeight: 600, borderColor: '#10b981', color: '#059669' }, 'aria-label': '导出加氢明细' }, '导出') ), React.createElement(Table, { className: 'h2-refuel-record-table', size: 'small', bordered: false, rowKey: 'id', columns: refuelRecordColumns, dataSource: records, pagination: records.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无加氢记录' }, scroll: records.length > 8 ? { x: 'max-content', y: 320 } : { x: 'max-content' } }) ) ); }, [refuelModal, refuelModalSummary, refuelRecordColumns, handleExportRefuelDrill]); var statementRecordColumns = [ { title: '序号', key: 'seq', width: 52, align: 'center', render: function (_, __, index) { return React.createElement('span', { className: 'h2-refuel-drill-seq' }, index + 1); } }, { title: '加氢时间', dataIndex: 'hydrogenTime', key: 'hydrogenTime', width: 158, render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-time' }, v || '—'); } }, { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 108, render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-plate' }, v || '—'); } }, { title: '加氢量(kg)', dataIndex: 'hydrogenKg', key: 'hydrogenKg', width: 96, align: 'right', render: function (v) { return React.createElement('span', { className: 'h2-refuel-drill-kg' }, h2FormatKgNum(v)); } }, { title: '成本单价(元/kg)', dataIndex: 'costUnitPrice', key: 'costUnitPrice', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'cost'); } }, { title: '成本总价(元)', dataIndex: 'costTotal', key: 'costTotal', width: 118, align: 'right', render: function (v) { return renderRefuelDrillMoney(h2FormatYuanSymbol(v, { keepZero: true }), 'cost'); } }, { title: '订单编号', dataIndex: 'orderNo', key: 'orderNo', width: 148, align: 'center', ellipsis: true, render: function (v) { if (!v) return React.createElement('span', { style: { color: '#cbd5e1' } }, '—'); return React.createElement('span', { className: 'h2-refuel-drill-order-no', title: v }, v); } } ]; var renderStatementBalanceReadonly = function (balance) { var n = h2NumOrZero(balance); var isArrears = n < 0; return React.createElement('div', { className: 'h2-statement-balance-readonly' }, React.createElement('span', { className: 'h2-statement-balance-readonly__amount ' + (isArrears ? 'h2-statement-balance-readonly__amount--negative' : 'h2-statement-balance-readonly__amount--positive') }, h2FormatYuanNum(n)), isArrears ? React.createElement(Tag, { color: 'error', className: 'lc-station-signed-tag' }, '已欠费') : null ); }; var renderStatementPanel = useCallback(function () { var record = statementModal.record; var stationName = (record && record.name) || '—'; var isDraft = statementModal.phase === 'draft'; var rows = isDraft ? (statementModal.draftRows || []) : []; var summary = isDraft ? statementDraftSummary : { count: 0, totalKg: 0, totalCost: 0 }; var lastEndDate = h2GetLastStatementEndDate(statementRecords, stationName); var stats = [ { key: 'count', label: '加氢次数', value: String(summary.count || 0), mod: 'count', valueMod: 'count' }, { key: 'kg', label: '加氢总量(kg)', value: h2FormatKgNum(summary.totalKg), mod: 'kg', valueMod: 'kg' }, { key: 'cost', label: '成本总金额', value: h2FormatYuanSymbol(summary.totalCost, { keepZero: true }), mod: 'cost', valueMod: 'cost' } ]; return React.createElement('div', { className: 'h2-statement-panel' }, React.createElement('div', { className: 'h2-statement-station-card' }, React.createElement('div', { className: 'h2-statement-station-card__name' }, stationName), React.createElement('div', { className: 'h2-statement-station-card__meta' }, isDraft ? '对账记录 · 待提交' : '选择账单日期 · 生成对账记录' ) ), React.createElement('div', { className: 'h2-statement-form-wrap' }, React.createElement('div', { className: 'h2-statement-date-grid' }, formItem('账单开始日期', true, React.createElement(Input, { type: 'date', style: { width: '100%', borderRadius: 8 }, disabled: isDraft, value: statementModal.startDate || '', onChange: function (e) { setStatementModal(function (m) { return Object.assign({}, m, { startDate: e.target.value, phase: 'select', draftRows: [] }); }); } })), formItem('账单结束日期', true, React.createElement(Input, { type: 'date', style: { width: '100%', borderRadius: 8 }, disabled: isDraft, value: statementModal.endDate || '', onChange: function (e) { setStatementModal(function (m) { return Object.assign({}, m, { endDate: e.target.value, phase: 'select', draftRows: [] }); }); } })) ), React.createElement('div', { className: 'h2-statement-last-end' }, '上次对账单结束时间:', React.createElement('strong', null, lastEndDate || '暂无') ), !isDraft ? React.createElement('div', { className: 'h2-statement-form-hint' }, '数据来源:', React.createElement('strong', null, '车辆氢费明细'), ' 中标记为「已对账」且尚未结算的加氢记录;明细仅展示成本价与成本总价。' ) : null ), isDraft ? React.createElement('div', { className: 'h2-statement-stats', role: 'group', 'aria-label': '对账统计' }, stats.map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-statement-stat h2-statement-stat--' + item.mod, role: 'article', 'aria-label': item.label + ' ' + item.value }, React.createElement('div', { className: 'h2-statement-stat__label' }, item.label), React.createElement('div', { className: 'h2-statement-stat__value h2-statement-stat__value--' + item.valueMod }, item.value) ); }) ) : null, isDraft ? React.createElement('div', { className: 'h2-statement-table-wrap' }, React.createElement('div', { className: 'h2-statement-table-head' }, React.createElement('div', { className: 'h2-statement-table-head__left' }, React.createElement('span', { className: 'h2-statement-table-head__title' }, '对账明细(成本字段)'), React.createElement('span', { className: 'h2-statement-table-head__count' }, '共 ' + rows.length + ' 条') ), React.createElement(Button, { size: 'small', icon: H2_ICONS.download, disabled: !rows.length, onClick: handleExportStatement, style: { borderRadius: 8, fontWeight: 600, borderColor: '#7c3aed', color: '#6d28d9' }, 'aria-label': '导出对账单明细' }, '导出') ), React.createElement(Table, { className: 'h2-statement-record-table', size: 'small', bordered: false, rowKey: 'id', columns: statementRecordColumns, dataSource: rows, pagination: rows.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无对账明细' }, scroll: rows.length > 8 ? { x: 'max-content', y: 320 } : { x: 'max-content' } }) ) : null, isDraft ? React.createElement('div', { className: 'h2-statement-settlement-wrap' }, formItem('结算后加氢站预付款余额', true, renderStatementBalanceReadonly(statementModal.balanceAfterSettlement)), React.createElement('div', { className: 'h2-statement-receipt-grid' }, formItem('收票日期', true, dayjs && DatePicker ? React.createElement(DatePicker, { style: { width: '100%', borderRadius: 8 }, format: 'YYYY-MM-DD', placeholder: '请选择收票日期', allowClear: true, value: h2ToDateDayjs(statementModal.receiptDate), onChange: function (d, dateStr) { setStatementModal(function (m) { return Object.assign({}, m, { receiptDate: dateStr || '' }); }); } }) : React.createElement(Input, { type: 'date', style: { width: '100%', borderRadius: 8 }, placeholder: '请选择收票日期', value: statementModal.receiptDate || '', onChange: function (e) { setStatementModal(function (m) { return Object.assign({}, m, { receiptDate: e.target.value }); }); } })), formItem('收票金额', true, React.createElement(Input, { className: 'h2-statement-receipt-amount-input', inputMode: 'decimal', prefix: '¥', style: { width: '100%', borderRadius: 8 }, placeholder: '请输入收票金额', value: statementModal.receiptAmount || '', onChange: function (e) { var next = h2SanitizeReceiptAmountInput(e.target.value); setStatementModal(function (m) { return Object.assign({}, m, { receiptAmount: next }); }); }, onBlur: function (e) { var formatted = h2FormatReceiptAmountInput(e.target.value); setStatementModal(function (m) { return Object.assign({}, m, { receiptAmount: formatted }); }); } })) ), formItem('发票附件', true, React.createElement(ContractFilesUpload, { fileList: statementModal.invoiceFiles || [], showHint: false, wrapClassName: 'h2-statement-invoice-upload', uploadClassName: 'h2-statement-upload-btn-wrap', buttonClassName: 'h2-statement-upload-btn', onChange: function (info) { setStatementModal(function (m) { return Object.assign({}, m, { invoiceFiles: info.fileList || [] }); }); } })) ) : null ); }, [statementModal, statementRecords, statementDraftSummary, statementRecordColumns, handleExportStatement]); var statementHistoryColumns = [ { title: '对账日期', dataIndex: 'reconcileDate', key: 'reconcileDate', width: 150 }, { title: '对账人', dataIndex: 'reconciler', key: 'reconciler', width: 100, ellipsis: true }, { title: '账单开始日期', dataIndex: 'startDate', key: 'startDate', width: 118 }, { title: '账单结束日期', dataIndex: 'endDate', key: 'endDate', width: 118 }, { title: '加氢次数', dataIndex: 'refuelCount', key: 'refuelCount', width: 88, align: 'right' }, { title: '加氢金额', dataIndex: 'totalCost', key: 'totalCost', width: 110, align: 'right', render: function (v) { return h2FormatYuanSymbol(v, { keepZero: true }); } }, { title: '对账后加氢站预付款余额', dataIndex: 'balanceAfterSettlement', key: 'balanceAfterSettlement', width: 168, align: 'right', render: function (v) { var n = h2NumOrZero(v); var isArrears = n < 0; return React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 6, flexWrap: 'wrap' } }, React.createElement('span', { className: 'h2-statement-history-balance ' + (isArrears ? 'h2-statement-history-balance--negative' : 'h2-statement-history-balance--positive') }, h2FormatYuanNum(n)), isArrears ? React.createElement(Tag, { color: 'error', className: 'lc-station-signed-tag' }, '已欠费') : null ); } }, { title: '操作', key: 'action', width: 96, fixed: 'right', render: function (_, row) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { openStatementDetailModal(row); } }, '查看明细'); } } ]; var renderStatementHistoryPanel = useCallback(function () { var record = statementHistoryModal.record; var stationName = (record && record.name) || '—'; var rows = h2GetStatementRecordsByStation(statementRecords, stationName); return React.createElement('div', { className: 'h2-statement-history-panel' }, React.createElement('div', { className: 'h2-statement-history-station-card' }, React.createElement('div', { className: 'h2-statement-history-station-card__name' }, stationName), React.createElement('div', { className: 'h2-statement-history-station-card__meta' }, '历史对账记录 · 共 ' + rows.length + ' 条') ), React.createElement('div', { className: 'h2-statement-history-table-wrap' }, React.createElement(Table, { size: 'small', bordered: false, rowKey: 'id', columns: statementHistoryColumns, dataSource: rows, pagination: rows.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无对账记录' }, scroll: { x: 'max-content' } }) ) ); }, [statementHistoryModal, statementRecords, statementHistoryColumns, openStatementDetailModal]); var renderStatementDetailPanel = useCallback(function () { var stmt = statementDetailModal.statementRecord; var rows = statementDetailModal.detailRows || []; if (!stmt) return null; var summary = h2CalcStatementSummary(rows); var invoiceFiles = stmt.invoiceFiles || []; return React.createElement('div', { className: 'h2-statement-panel' }, React.createElement('div', { className: 'h2-statement-station-card' }, React.createElement('div', { className: 'h2-statement-station-card__name' }, stmt.stationName || '—'), React.createElement('div', { className: 'h2-statement-station-card__meta' }, (stmt.startDate || '—') + ' 至 ' + (stmt.endDate || '—') ) ), React.createElement('div', { className: 'h2-statement-stats', role: 'group', 'aria-label': '对账统计' }, [ { key: 'count', label: '加氢次数', value: String(summary.count || stmt.refuelCount || 0), mod: 'count', valueMod: 'count' }, { key: 'kg', label: '加氢总量(kg)', value: h2FormatKgNum(summary.totalKg || stmt.totalKg), mod: 'kg', valueMod: 'kg' }, { key: 'cost', label: '成本总金额', value: h2FormatYuanSymbol(summary.totalCost || stmt.totalCost, { keepZero: true }), mod: 'cost', valueMod: 'cost' } ].map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-statement-stat h2-statement-stat--' + item.mod }, React.createElement('div', { className: 'h2-statement-stat__label' }, item.label), React.createElement('div', { className: 'h2-statement-stat__value h2-statement-stat__value--' + item.valueMod }, item.value) ); }) ), React.createElement('div', { className: 'h2-statement-detail-receipt-wrap' }, React.createElement('div', { className: 'h2-statement-detail-receipt-grid' }, React.createElement('div', { className: 'h2-statement-detail-receipt-item' }, React.createElement('div', { className: 'h2-statement-detail-receipt-item__label' }, '收票时间'), React.createElement('div', { className: 'h2-statement-detail-receipt-item__value' }, stmt.receiptDate || '—') ), React.createElement('div', { className: 'h2-statement-detail-receipt-item' }, React.createElement('div', { className: 'h2-statement-detail-receipt-item__label' }, '收票金额'), React.createElement('div', { className: 'h2-statement-detail-receipt-item__value h2-statement-detail-receipt-item__value--amount' }, stmt.receiptAmount != null ? h2FormatYuanSymbol(stmt.receiptAmount, { keepZero: true }) : '—' ) ) ), React.createElement('div', { className: 'h2-statement-detail-receipt-files' }, React.createElement('div', { className: 'h2-statement-detail-receipt-item__label' }, '发票附件'), invoiceFiles.length ? React.createElement('div', { className: 'h2-statement-detail-receipt-file-list' }, invoiceFiles.map(function (file) { return React.createElement('div', { key: file.uid || file.name, className: 'h2-statement-detail-receipt-file-item' }, React.createElement('span', { className: 'h2-statement-detail-receipt-file-name', title: file.name }, file.name || '未命名附件'), React.createElement(Button, { type: 'link', size: 'small', icon: H2_ICONS.download, onClick: function () { h2DownloadUploadFile(file); } }, '下载') ); }) ) : React.createElement('div', { style: { fontSize: 13, color: '#94a3b8', marginTop: 8 } }, '暂无发票附件') ) ), React.createElement('div', { className: 'h2-statement-table-wrap' }, React.createElement('div', { className: 'h2-statement-table-head' }, React.createElement('div', { className: 'h2-statement-table-head__left' }, React.createElement('span', { className: 'h2-statement-table-head__title' }, '对账明细'), React.createElement('span', { className: 'h2-statement-table-head__count' }, '共 ' + rows.length + ' 条') ) ), React.createElement(Table, { className: 'h2-statement-record-table', size: 'small', bordered: false, rowKey: 'id', columns: statementRecordColumns, dataSource: rows, pagination: rows.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small' } : false, locale: { emptyText: '暂无明细' }, scroll: rows.length > 8 ? { x: 'max-content', y: 320 } : { x: 'max-content' } }) ) ); }, [statementDetailModal, statementRecordColumns]); var renderPriceConfigMoney = function (text, tone) { return React.createElement('span', { className: 'h2-price-config-money h2-price-config-money--' + tone }, text); }; var costPriceLogColumns = [ { title: '操作人', dataIndex: 'operator', key: 'operator', width: 100, ellipsis: true }, { title: '操作时间', dataIndex: 'operateTime', key: 'operateTime', width: 150 }, { title: '调整前成本价格', dataIndex: 'beforeCostPrice', key: 'beforeCostPrice', width: 130, align: 'right', render: function (v) { return renderPriceConfigMoney(h2FormatCostPriceDisplay(v), 'before'); } }, { title: '调整后成本价格', dataIndex: 'afterCostPrice', key: 'afterCostPrice', width: 130, align: 'right', render: function (v) { return renderPriceConfigMoney(h2FormatCostPriceDisplay(v), 'after'); } }, { title: '生效时间', dataIndex: 'effectiveTime', key: 'effectiveTime', width: 150 } ]; var businessStatusLogColumns = [ { title: '操作时间', dataIndex: 'operateTime', key: 'operateTime', width: 150 }, { title: '操作人', dataIndex: 'operator', key: 'operator', width: 100, ellipsis: true }, { title: '修改前状态', dataIndex: 'beforeStatus', key: 'beforeStatus', width: 110, render: function (v) { return renderBusinessStatusText(v, 'muted'); } }, { title: '修改后状态', dataIndex: 'afterStatus', key: 'afterStatus', width: 110, render: function (v) { return renderBusinessStatusTag(v); } } ]; var columns = [ { title: '加氢站名称', dataIndex: 'name', key: 'name', width: 300, fixed: 'left', ellipsis: true, render: function (v, r) { var addressLine = h2FormatStationAddressLine(r); return React.createElement('div', { className: 'lc-station-name-cell' }, React.createElement('div', { className: 'lc-station-name-row' }, React.createElement('div', { className: 'lc-station-name' }, v || '—'), h2RenderStationSignedTag(r) ), React.createElement('div', { className: 'lc-station-address-line', title: addressLine }, addressLine) ); } }, { title: '合作时间', key: 'contractRange', width: 220, render: function (_, r) { if (!r.isSigned) return React.createElement('span', { style: { color: '#94a3b8' } }, '—'); return React.createElement('div', null, React.createElement('div', { style: { fontSize: 12, fontWeight: 600, color: '#334155' } }, (r.contractStart || '—') + ' 至 ' + (r.contractEnd || '—') ), r.contractEnd ? h2RenderContractRemainTag(r.contractEnd) : null ); } }, { title: '营业状态', dataIndex: 'businessStatus', key: 'businessStatus', width: 110, render: renderBusinessStatusTag }, { title: '营业时间', dataIndex: 'businessHours', key: 'businessHours', width: 140, render: function (v) { return h2DisplayBusinessHours(v); } }, { title: '当前成本价格(元/kg)', key: 'currentCostPrice', width: 128, align: 'right', render: function (_, r) { var price = h2ResolveCurrentCostPrice(r); if (price == null) { return React.createElement('span', { style: { color: '#94a3b8' } }, '—'); } return React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums', fontWeight: 600, color: '#0f172a' } }, '¥' + h2FormatYuanNum(price)); } }, { title: '加氢次数', dataIndex: 'refuelCount', key: 'refuelCount', width: 110, align: 'right', sortOrder: refuelSort.key === 'refuelCount' ? refuelSort.order : null, sorter: function () { return 0; }, sortDirections: ['descend', 'ascend'], showSorterTooltip: { title: '点击切换倒序 / 正序' }, render: function (v) { return React.createElement('span', { style: { fontVariantNumeric: 'tabular-nums', fontWeight: 600, color: '#0f172a' } }, String(v || 0)); } }, { title: '加氢量(kg)', dataIndex: 'refuelTotalKg', key: 'refuelTotalKg', width: 160, align: 'right', sortOrder: refuelSort.key === 'refuelTotalKg' ? refuelSort.order : null, sorter: function () { return 0; }, sortDirections: ['descend', 'ascend'], showSorterTooltip: { title: '点击切换倒序 / 正序' }, render: function (v, record) { return React.createElement('div', { className: 'lc-refuel-kg-row' }, renderRefuelFreqShortTag(record.refuelFreqKey), renderRefuelDrillLink(h2FormatKgNum(v), record, '查看加氢记录,合计 ' + h2FormatKgNum(v) + ' kg') ); } }, { title: '预付余额(元)', dataIndex: 'prepaidBalance', key: 'prepaidBalance', width: 188, align: 'right', className: 'h2-col-prepaid-balance', onCell: function () { return { className: 'h2-cell-prepaid-balance' }; }, render: function (v, record) { return renderPrepaidBalance(v, record); } }, { title: '联系方式', key: 'contactInfo', width: 120, render: function (_, r) { var mobile = r.mobilePhone || r.phone || ''; var landline = r.landlinePhone || ''; return React.createElement('div', null, React.createElement('div', { className: 'lc-station-contact-name' }, r.contact || '—'), mobile ? React.createElement('div', { className: 'lc-station-contact-phone' }, mobile) : null, landline ? React.createElement('div', { className: 'lc-station-contact-phone' }, landline) : null, !mobile && !landline ? React.createElement('div', { className: 'lc-station-contact-phone' }, '—') : null ); } }, { title: '操作', key: 'action', width: 108, fixed: 'right', render: function (_, record) { return React.createElement('div', { className: 'h2-row-actions' }, React.createElement(Button, { type: 'link', size: 'small', className: 'lc-action-btn', onClick: function () { openView(record); } }, '查看'), React.createElement(Dropdown, { trigger: ['click'], placement: 'bottomRight', menu: { items: getRowMoreMenuItems(record) } }, React.createElement(Tooltip, { title: '更多' }, React.createElement('span', { className: 'h2-action-more-btn', role: 'button', tabIndex: 0, 'aria-label': '更多操作', onClick: function (e) { e.stopPropagation(); } }, h2MoreIcon()) ) ) ); } } ]; var formItem = function (label, required, node, extra) { return React.createElement(Form.Item, { label: required ? React.createElement('span', null, React.createElement('span', { style: { color: '#ef4444', marginRight: 4 } }, '*'), label) : label, extra: extra }, node); }; var renderBusinessStatusPanel = useCallback(function () { var record = businessModal.record; var logs = businessModal.statusLogs || []; var stationName = (record && record.name) || '—'; var originStatus = businessModal.originBusinessStatus || (record && record.businessStatus) || '营业中'; var originHours = (record && record.businessHours) || '—'; var displayHours = businessModal.businessHours || originHours; var pendingChanged = (businessModal.businessStatus || '营业中') !== originStatus; var stats = [ { key: 'status', label: '当前营业状态', value: renderBusinessStatusTag(originStatus), mod: 'status', valueMod: '' }, { key: 'hours', label: '当前营业时间', value: h2DisplayBusinessHours(displayHours), mod: 'hours', valueMod: 'hours' } ]; return React.createElement('div', { className: 'h2-business-status-panel' }, React.createElement('div', { className: 'h2-business-status-station-card' }, React.createElement('div', { className: 'h2-business-status-station-card__name' }, stationName), React.createElement('div', { className: 'h2-business-status-station-card__meta' }, '营业状态维护 · 共 ' + logs.length + ' 条记录') ), React.createElement('div', { className: 'h2-business-status-stats', role: 'group', 'aria-label': '营业状态统计' }, stats.map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-business-status-stat h2-business-status-stat--' + item.mod, role: 'article', 'aria-label': item.label }, React.createElement('div', { className: 'h2-business-status-stat__label' }, item.label), React.createElement('div', { className: 'h2-business-status-stat__value' + (item.valueMod ? ' h2-business-status-stat__value--' + item.valueMod : '') }, item.value) ); }) ), React.createElement('div', { className: 'h2-business-status-form-wrap' }, React.createElement(Form, { layout: 'vertical', requiredMark: false }, formItem('营业状态', true, h2RenderOptionButtonGroup( H2_BUSINESS_STATUS_BTN_OPTIONS, businessModal.businessStatus || '营业中', function (v) { setBusinessModal(function (m) { return Object.assign({}, m, { businessStatus: v }); }); }, { ariaLabel: '营业状态' } )), formItem('营业时间', false, React.createElement(BusinessHoursInput, { value: businessModal.businessHours, fieldStyle: { width: '100%', borderRadius: 8 }, onChange: function (v) { setBusinessModal(function (m) { return Object.assign({}, m, { businessHours: v }); }); } })) ), React.createElement('div', { className: 'h2-business-status-form-hint', role: 'note' }, pendingChanged ? '保存后将记录营业状态变更,并更新列表展示。' : '选择「非全天营业」后需设置开始与结束时间;暂停或停止营业时建议配置具体时段。' ) ), React.createElement('div', { className: 'h2-business-status-table-wrap' }, React.createElement('div', { className: 'h2-business-status-table-head' }, React.createElement('span', { className: 'h2-business-status-table-head__title' }, '营业状态变更记录'), React.createElement('span', { className: 'h2-business-status-table-head__count' }, '共 ' + logs.length + ' 条') ), React.createElement(Table, { className: 'h2-business-status-record-table', size: 'small', bordered: false, rowKey: 'id', columns: businessStatusLogColumns, dataSource: logs, pagination: logs.length > 5 ? { pageSize: 5, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无状态变更记录' }, scroll: logs.length > 5 ? { x: 520, y: 240 } : { x: 520 } }) ) ); }, [businessModal, businessStatusLogColumns]); var renderPriceConfigPanel = useCallback(function () { var record = priceModal.record; var logs = priceModal.priceLogs || []; var stationName = (record && record.name) || '—'; var currentPrice = record ? h2ResolveCurrentCostPrice(record) : null; var pendingCount = h2CountPendingCostPriceLogs(logs); var currentPriceText = currentPrice != null ? h2FormatYuanSymbol(currentPrice, { keepZero: true }) + '/kg' : '—'; var stats = [ { key: 'current', label: '当前成本价格', value: currentPriceText, mod: 'current', valueMod: 'current' }, { key: 'history', label: '历史调整次数', value: String(logs.length), mod: 'history', valueMod: 'history' }, { key: 'pending', label: '待生效配置', value: String(pendingCount), mod: 'pending', valueMod: 'pending' } ]; var effectiveTimeValue = h2ToDateTimeDayjs(priceModal.effectiveTime); var effectivePicker = dayjs && DatePicker ? React.createElement(DatePicker, { showTime: true, needConfirm: false, style: { width: '100%', borderRadius: 8 }, format: 'YYYY-MM-DD HH:mm', placeholder: '请选择生效时间', allowClear: true, value: effectiveTimeValue, onChange: function (d, dateStr) { setPriceModal(function (m) { var next = dateStr || (d && d.isValid && d.isValid() ? d.format('YYYY-MM-DD HH:mm') : ''); return Object.assign({}, m, { effectiveTime: next }); }); } }) : React.createElement(Input, { type: 'datetime-local', style: { borderRadius: 8 }, value: priceModal.effectiveTime ? priceModal.effectiveTime.replace(' ', 'T') : '', onChange: function (e) { var raw = e.target.value || ''; setPriceModal(function (m) { return Object.assign({}, m, { effectiveTime: raw ? raw.replace('T', ' ') : '' }); }); } }); return React.createElement('div', { className: 'h2-price-config-panel' }, React.createElement('div', { className: 'h2-price-config-station-card' }, React.createElement('div', { className: 'h2-price-config-station-card__name' }, stationName), React.createElement('div', { className: 'h2-price-config-station-card__meta' }, '成本价格配置 · 共 ' + logs.length + ' 条记录') ), React.createElement('div', { className: 'h2-price-config-stats', role: 'group', 'aria-label': '价格统计' }, stats.map(function (item) { return React.createElement('div', { key: item.key, className: 'h2-price-config-stat h2-price-config-stat--' + item.mod, role: 'article', 'aria-label': item.label + ' ' + item.value }, React.createElement('div', { className: 'h2-price-config-stat__label' }, item.label), React.createElement('div', { className: 'h2-price-config-stat__value h2-price-config-stat__value--' + item.valueMod }, item.value) ); }) ), React.createElement('div', { className: 'h2-price-config-form-wrap' }, React.createElement(Form, { layout: 'vertical', requiredMark: false }, formItem('成本价格(元/kg)', true, React.createElement(Input, { value: priceModal.costUnitPrice, placeholder: '请输入成本价格', style: { borderRadius: 8 }, onChange: function (e) { setPriceModal(function (m) { return Object.assign({}, m, { costUnitPrice: e.target.value }); }); } })), formItem('生效时间', true, effectivePicker) ), React.createElement('div', { className: 'h2-price-config-form-hint', role: 'note' }, '到达生效时间后,列表「当前成本价格」将自动更新为调整后价格' ) ), React.createElement('div', { className: 'h2-price-config-table-wrap' }, React.createElement('div', { className: 'h2-price-config-table-head' }, React.createElement('span', { className: 'h2-price-config-table-head__title' }, '价格调整记录'), React.createElement('span', { className: 'h2-price-config-table-head__count' }, '共 ' + logs.length + ' 条') ), React.createElement(Table, { className: 'h2-price-config-record-table', size: 'small', bordered: false, rowKey: 'id', columns: costPriceLogColumns, dataSource: logs, pagination: logs.length > 5 ? { pageSize: 5, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, locale: { emptyText: '暂无价格调整记录' }, scroll: logs.length > 5 ? { x: 680, y: 240 } : { x: 680 } }) ) ); }, [priceModal, costPriceLogColumns]); var getRechargeStationOptions = useCallback(function (currentLineId) { var usedIds = {}; (rechargeModal.lines || []).forEach(function (line) { if (line.id !== currentLineId && line.stationId != null) usedIds[line.stationId] = true; }); return listData .filter(function (r) { return !usedIds[r.id]; }) .map(function (r) { return { value: r.id, label: r.name || '—' }; }); }, [listData, rechargeModal.lines]); var rechargeModalColumns = useMemo(function () { return [ { title: '站点', key: 'station', width: 180, fixed: 'left', render: function (_, line) { return React.createElement(Select, { showSearch: true, allowClear: true, placeholder: '请选择站点', style: { width: '100%' }, value: line.stationId, options: getRechargeStationOptions(line.id), optionFilterProp: 'label', onChange: function (v) { handleRechargeStationSelect(line.id, v); } }); } }, { title: '当前余额', key: 'currentBalance', width: 108, align: 'right', render: function (_, line) { if (!line.stationId) { return React.createElement('span', { className: 'h2-recharge-readonly h2-recharge-readonly--muted' }, '—'); } var num = h2NumOrZero(line.currentBalance); return React.createElement('span', { className: 'h2-recharge-readonly', style: { color: num < 0 ? '#dc2626' : '#059669', fontWeight: 600, fontVariantNumeric: 'tabular-nums' } }, '¥' + (line.currentBalance || '0.00')); } }, { title: '付款金额', key: 'payAmount', width: 130, render: function (_, line) { return React.createElement(Input, { className: 'h2-statement-receipt-amount-input', inputMode: 'decimal', prefix: '¥', placeholder: '请输入金额', style: { width: '100%', borderRadius: 8 }, value: line.payAmount || '', onChange: function (e) { updateRechargeLineField(line.id, 'payAmount', h2SanitizeReceiptAmountInput(e.target.value)); }, onBlur: function (e) { updateRechargeLineField(line.id, 'payAmount', h2FormatReceiptAmountInput(e.target.value)); } }); } }, { title: '企业全称', key: 'companyName', width: 160, render: function (_, line) { return h2RenderRechargeAutoField(line.companyName, !!line.stationId, '未关联供应商'); } }, { title: '收款银行账号', key: 'bankAccount', width: 150, render: function (_, line) { return h2RenderRechargeAutoField(line.bankAccount, !!line.stationId, '未配置账号'); } }, { title: '收款开户行全称', key: 'bankName', width: 160, render: function (_, line) { return h2RenderRechargeAutoField(line.bankName, !!line.stationId, '未配置开户行'); } }, { title: '转账用途', key: 'transferPurpose', width: 200, render: function (_, line) { return h2RenderRechargeAutoField(line.transferPurpose, !!line.stationId, '—'); } }, { title: '操作', key: 'action', width: 72, fixed: 'right', render: function (_, line) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, disabled: (rechargeModal.lines || []).length <= 1, onClick: function () { removeRechargeLine(line.id); } }, '删除'); } } ]; }, [rechargeModal.lines, getRechargeStationOptions, handleRechargeStationSelect, updateRechargeLineField, removeRechargeLine]); var listColumnsWithoutAction = useMemo(function () { return columns.filter(function (c) { return c.key !== 'action'; }); }, [columns]); var renderRechargeModalPanel = useCallback(function () { var lines = rechargeModal.lines || []; return React.createElement('div', { className: 'h2-recharge-panel' }, React.createElement('p', { className: 'h2-recharge-hint' }, '选择站点后将根据供应商信息自动带出企业全称、收款银行账号、开户行及转账用途(均为只读);仅需填写付款金额。支持添加多行,一次发起多个站点的充值付款。' ), React.createElement('div', { className: 'h2-recharge-toolbar' }, React.createElement('span', { className: 'h2-recharge-toolbar__count' }, '共 ' + lines.length + ' 条付款信息'), React.createElement(Button, { type: 'dashed', icon: H2_ICONS.plus, style: { borderRadius: 8, fontWeight: 600, borderColor: '#10b981', color: '#059669' }, onClick: addRechargeLine }, '添加站点') ), React.createElement('div', { className: 'h2-recharge-table-wrap' }, React.createElement(Table, { className: 'h2-recharge-record-table', size: 'small', bordered: false, rowKey: 'id', columns: rechargeModalColumns, dataSource: lines, pagination: false, locale: { emptyText: '请添加付款站点' }, scroll: { x: 1180 } }) ) ); }, [rechargeModal.lines, rechargeModalColumns, addRechargeLine]); var renderAlertStationPanel = useCallback(function () { var stations = alertStationModal.stations || []; return React.createElement('div', { className: 'h2-alert-station-panel' }, React.createElement('div', { className: 'h2-alert-station-head' }, React.createElement('div', null, React.createElement('div', { className: 'h2-alert-station-head__title' }, alertStationModal.title || '站点列表'), React.createElement('div', { className: 'h2-alert-station-head__count' }, '共 ' + stations.length + ' 个站点 · 字段与列表页一致' ) ), React.createElement(Button, { type: 'primary', style: H2_PRIMARY_BTN_STYLE, disabled: !stations.length, onClick: handleBatchRechargeFromAlert }, '一键发起充值单') ), React.createElement('div', { className: 'h2-alert-station-table-wrap' }, React.createElement(Table, { className: 'lc-list-table', rowKey: 'id', columns: listColumnsWithoutAction, dataSource: stations, pagination: stations.length > 8 ? { pageSize: 8, showSizeChanger: false, size: 'small', showTotal: function (t) { return '共 ' + t + ' 条'; } } : false, size: 'middle', scroll: { x: 1182 }, locale: { emptyText: '暂无站点' } }) ) ); }, [alertStationModal, listColumnsWithoutAction, handleBatchRechargeFromAlert]); var usedBindAccountMap = useMemo(function () { var excludeId = subView === 'edit' && editRecord ? editRecord.id : null; return h2CollectUsedBindAccountMap(listData, excludeId); }, [listData, subView, editRecord]); var createViewNode = subView === 'create' ? h2BuildStationCreateView({ pageMode: 'create', showSupplier: true, accountEditable: true, showReset: false, station: createStation, supplier: createSupplier, supplierMode: createSupplierMode, linkedSupplierId: createLinkedSupplierId, supplierReadonly: createSupplierReadonly, usedBindAccountMap: usedBindAccountMap, updateStation: updateCreateStation, updateSupplier: updateCreateSupplier, syncSupplierFromStation: syncCreateSupplierFromStation, handleSupplierModeChange: handleCreateSupplierModeChange, handleLinkSupplierChange: handleCreateLinkSupplierChange, handleSupplierCityChange: handleCreateSupplierCityChange, onCancel: handleCreateCancel, onSubmit: handleCreateSubmit, submitting: createSubmitting, submitLabel: '提交创建' }) : null; var editViewNode = subView === 'edit' ? h2BuildStationCreateView({ pageMode: 'edit', showSupplier: true, accountEditable: isAdminUser, showReset: false, station: editStation, supplier: editSupplier, supplierMode: editSupplierMode, linkedSupplierId: editLinkedSupplierId, supplierReadonly: editSupplierMode === 'link' && !!editLinkedSupplierId, usedBindAccountMap: usedBindAccountMap, updateStation: updateEditStation, updateSupplier: updateEditSupplier, syncSupplierFromStation: syncEditSupplierFromStation, handleSupplierModeChange: handleEditSupplierModeChange, handleLinkSupplierChange: handleEditLinkSupplierChange, handleSupplierCityChange: handleEditSupplierCityChange, onCancel: handleEditCancel, onSubmit: handleEditSubmit, submitting: editSubmitting, submitLabel: '保存修改' }) : null; var emptyNode = React.createElement('div', { style: { padding: '40px 0', textAlign: 'center' } }, H2_ICONS.empty, React.createElement('div', { style: { color: '#94a3b8', marginTop: 12 } }, '暂无符合检索条件的站点') ); return React.createElement('div', { className: 'h2-station-page lc-edit-page' + (subView === 'create' || subView === 'edit' ? ' h2-station-page--create' : '') }, React.createElement('style', null, H2_PAGE_STYLE), createViewNode, editViewNode, subView === 'list' ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' } }, React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 } }, React.createElement(Breadcrumb, { items: [{ title: '加氢站管理' }, { title: '站点信息' }] }), React.createElement(Space, { size: 8 }, React.createElement(Button, { type: 'default', icon: H2_ICONS.doc, style: H2_REQ_BTN_STYLE, onClick: function () { setUserManualModalOpen(true); }, 'aria-label': '查看使用说明书' }, '查看使用说明书'), React.createElement(Button, { type: 'default', icon: H2_ICONS.doc, style: H2_REQ_BTN_STYLE, onClick: function () { setRequirementModalOpen(true); }, 'aria-label': '查看需求说明' }, '查看需求说明') ) ), React.createElement(Card, { className: 'lc-filter-card', title: '筛选条件', bordered: false }, React.createElement('div', { className: 'lc-filter-grid' }, renderFilterField('加氢站名称', React.createElement(Input, { placeholder: '请输入加氢站名称', value: listFilters.name, onChange: function (e) { setListFilters(function (p) { return Object.assign({}, p, { name: e.target.value }); }); }, onPressEnter: handleListFilterQuery, allowClear: true, style: { width: '100%', height: 32, borderRadius: 8 } })), renderFilterField('是否签约', React.createElement(Select, { placeholder: '全部', allowClear: true, value: listFilters.signed, onChange: function (v) { setListFilters(function (p) { return Object.assign({}, p, { signed: v }); }); }, options: [{ value: 'yes', label: '签约站点' }, { value: 'no', label: '普通站点' }], style: { width: '100%' }, dropdownStyle: { borderRadius: 8 } })), renderFilterField('地区(省-市)', React.createElement(Cascader, { options: H2_REGION_CASCADER_OPTIONS, value: listFilters.region && listFilters.region.length ? listFilters.region : undefined, onChange: function (v) { setListFilters(function (p) { return Object.assign({}, p, { region: v || undefined }); }); }, placeholder: '请选择省 / 市', allowClear: true, style: { width: '100%', borderRadius: 8 }, changeOnSelect: true })), renderFilterField('营业状态', React.createElement(Select, { placeholder: '全部', allowClear: true, value: listFilters.businessStatus, onChange: function (v) { setListFilters(function (p) { return Object.assign({}, p, { businessStatus: v }); }); }, options: H2_BUSINESS_STATUS_OPTIONS, style: { width: '100%' }, dropdownStyle: { borderRadius: 8 } })) ), React.createElement('div', { className: 'lc-filter-actions' }, React.createElement(Button, { onClick: handleListFilterReset, style: { borderRadius: 8 } }, '重置'), React.createElement(Button, { type: 'primary', onClick: handleListFilterQuery, style: H2_PRIMARY_BTN_STYLE }, '查询') ) ), React.createElement('div', { className: 'lc-alert-stats-row' }, renderAlertStatCard(H2_STATION_KPI_CARDS[0], { active: categoryTab === 'all' && !appliedFilters.signed, count: categoryCounts.all || 0, onClick: function () { handleKpiCardClick('all'); }, icon: h2KpiCardIcon('all') }), H2_SIGNED_FILTER_CARDS.map(function (card) { return renderAlertStatCard(card, { active: appliedFilters.signed === card.key, count: card.key === 'yes' ? signedStats.signed : signedStats.unsigned, onClick: function () { handleSignedFilterCardClick(card.key); }, icon: h2SignedFilterIcon(card.key) }); }), H2_STATION_KPI_CARDS.slice(1).map(function (card) { var isAlertKpi = card.key === 'balanceAlert' || card.key === 'arrears'; return renderAlertStatCard(card, { active: isAlertKpi ? (alertStationModal.open && alertStationModal.type === card.key) : (categoryTab === card.key && !appliedFilters.signed), count: categoryCounts[card.key] || 0, onClick: function () { if (isAlertKpi) openAlertStationModal(card.key); else handleKpiCardClick(card.key); }, icon: h2KpiIcon(card.key) }); }) ), React.createElement('div', { className: 'lc-table-section' }, React.createElement('div', { className: 'lc-table-toolbar' }, React.createElement('div', { className: 'lc-table-toolbar-actions' }, React.createElement(Button, { type: 'default', icon: H2_ICONS.upload, style: { borderRadius: 8, fontWeight: 600, borderColor: '#10b981', color: '#059669' }, onClick: function () { setImportModalOpen(true); }, 'aria-label': '批量导入加氢站点' }, '批量导入'), React.createElement(Button, { type: 'default', icon: H2_ICONS.doc, style: { borderRadius: 8, fontWeight: 600, borderColor: '#10b981', color: '#059669' }, onClick: function () { openRechargeModal(); }, 'aria-label': '发起充值单' }, '发起充值单'), React.createElement(Button, { type: 'primary', style: H2_PRIMARY_BTN_STYLE, onClick: openCreate, 'aria-label': '新建加氢站点' }, '新建站点') ) ), React.createElement('div', { className: 'lc-table-card' }, React.createElement(Table, { className: 'lc-list-table', rowKey: 'id', columns: columns, dataSource: displayList, pagination: tablePagination, size: 'middle', scroll: { x: 1290 }, locale: { emptyText: emptyNode }, onChange: handleListTableChange }) ) ) ) : null, React.createElement(Modal, { title: '确认删除', open: deleteModal.open, centered: true, onCancel: function () { setDeleteModal({ open: false, record: null }); }, onOk: function () { if (deleteModal.record) { setListData(function (prev) { return prev.filter(function (r) { return r.id !== deleteModal.record.id; }); }); message.success('已删除'); } setDeleteModal({ open: false, record: null }); }, okButtonProps: { danger: true }, okText: '删除', cancelText: '取消' }, '确定删除站点「' + ((deleteModal.record && deleteModal.record.name) || '') + '」吗?此操作不可撤销。'), React.createElement(Modal, { className: 'h2-prd-modal h2-user-manual-modal', title: React.createElement('span', { style: { fontWeight: 700 } }, '使用说明书 · 站点信息'), open: userManualModalOpen, onCancel: function () { setUserManualModalOpen(false); }, width: 900, centered: true, destroyOnClose: true, styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8, paddingBottom: 16 } }, footer: React.createElement(Button, { onClick: function () { setUserManualModalOpen(false); }, style: { borderRadius: 8 } }, '关闭') }, renderH2StationUserManualDocPanel()), React.createElement(Modal, { className: 'h2-prd-modal', title: React.createElement('span', { style: { fontWeight: 700 } }, '需求说明 · 站点信息'), open: requirementModalOpen, onCancel: function () { setRequirementModalOpen(false); }, width: 840, centered: true, destroyOnClose: true, styles: { body: { maxHeight: '72vh', overflow: 'auto', paddingTop: 8, paddingBottom: 16 } }, footer: React.createElement(Button, { onClick: function () { setRequirementModalOpen(false); }, style: { borderRadius: 8 } }, '关闭') }, renderH2StationRequirementDocPanel()), React.createElement(Modal, { title: '批量导入加氢站点', open: importModalOpen, onCancel: closeImportModal, width: 560, centered: true, destroyOnClose: true, okText: '确认导入', cancelText: '取消', okButtonProps: { disabled: !importPreview || !importPreview.valid.length, style: H2_PRIMARY_BTN_STYLE }, onOk: handleConfirmImport }, React.createElement('div', { className: 'h2-import-template-bar' }, React.createElement('div', { className: 'h2-import-template-bar-text' }, React.createElement('div', { style: { fontWeight: 700, marginBottom: 4, color: '#0f172a' } }, '第一步:下载 CSV 导入模板'), '模板含加氢站名称、省、市、详细地址、是否签约、签约起止时间、营业状态、营业时间、联系人、联系电话;与新建表单字段一致。' ), React.createElement(Button, { type: 'primary', ghost: true, style: { borderColor: '#10b981', color: '#059669', fontWeight: 600, flexShrink: 0, borderRadius: 8 }, onClick: downloadImportTemplate }, '下载模板') ), React.createElement(Alert, { type: 'info', showIcon: true, style: { marginBottom: 14, borderRadius: 10 }, message: '第二步:填写模板后上传文件', description: '支持 .csv(推荐)、.xlsx、.xls;原型阶段 Excel 请另存为 CSV UTF-8 后上传。上传后系统将校验并预览可导入条数。' }), React.createElement(Upload.Dragger, { accept: '.csv,.xlsx,.xls', multiple: false, maxCount: 1, fileList: importFileList, beforeUpload: function (file) { parseImportFile(file); return false; }, onRemove: function () { setImportFileList([]); setImportPreview(null); return true; } }, React.createElement('p', { style: { margin: '8px 0 4px', fontWeight: 600, color: '#334155' } }, '点击或拖拽文件到此处上传'), React.createElement('p', { style: { margin: 0, fontSize: 12, color: '#94a3b8' } }, '单次上传一个文件') ), importPreview ? React.createElement('div', { className: 'h2-import-preview' }, React.createElement('div', { style: { fontWeight: 700, marginBottom: 6 } }, '解析结果 · ' + (importPreview.fileName || '')), React.createElement('div', null, React.createElement('span', { style: { color: '#059669', fontWeight: 600 } }, '可导入 ' + importPreview.valid.length + ' 条'), importPreview.errors.length ? React.createElement('span', { style: { marginLeft: 12, color: '#dc2626', fontWeight: 600 } }, '失败 ' + importPreview.errors.length + ' 条') : null ), importPreview.errors.length ? React.createElement('ul', { className: 'h2-import-error-list' }, importPreview.errors.slice(0, 8).map(function (err, ei) { return React.createElement('li', { key: ei }, '第 ' + err.lineNo + ' 行「' + err.name + '」:' + err.reasons.join(';') ); }).concat( importPreview.errors.length > 8 ? [React.createElement('li', { key: 'more' }, '… 另有 ' + (importPreview.errors.length - 8) + ' 条错误未展示')] : [] ) ) : null ) : null ), React.createElement(Modal, { className: 'h2-business-status-modal', title: '营业状态', open: businessModal.open, onCancel: closeBusinessModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closeBusinessModal, style: { borderRadius: 8 } }, '取消'), React.createElement(Button, { type: 'primary', onClick: handleSaveBusinessSetting, style: H2_PRIMARY_BTN_STYLE }, '保存') ), centered: true, width: 760, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderBusinessStatusPanel() ), React.createElement(Modal, { className: 'h2-refuel-drill-modal', title: '加氢记录', open: refuelModal.open, onCancel: closeRefuelModal, footer: React.createElement(Button, { type: 'primary', onClick: closeRefuelModal, style: H2_PRIMARY_BTN_STYLE }, '知道了'), width: 980, centered: true, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderRefuelDrillPanel() ), React.createElement(Modal, { className: 'h2-station-view-modal', title: '查看加氢站点', open: viewModal.open, onCancel: closeViewModal, footer: React.createElement(Button, { type: 'primary', onClick: closeViewModal, style: H2_PRIMARY_BTN_STYLE }, '知道了'), width: 920, centered: true, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderStationViewPanel() ), React.createElement(Modal, { className: 'h2-file-preview-modal', title: filePreviewModal.name || '附件预览', open: filePreviewModal.open, onCancel: closeFilePreviewModal, footer: React.createElement(Button, { type: 'primary', onClick: closeFilePreviewModal, style: H2_PRIMARY_BTN_STYLE }, '关闭'), width: filePreviewModal.type === 'pdf' ? 920 : 720, centered: true, destroyOnClose: true }, React.createElement('div', { className: 'h2-file-preview-body' }, filePreviewModal.type === 'pdf' && filePreviewModal.url ? React.createElement('iframe', { title: filePreviewModal.name || '附件预览', src: filePreviewModal.url }) : React.createElement('img', { src: filePreviewModal.url, alt: filePreviewModal.name || '附件预览' }) ) ), React.createElement(Modal, { className: 'h2-balance-drill-modal', title: '余额变更明细', open: prepaidBalanceDrill.open, onCancel: closePrepaidBalanceDrill, footer: React.createElement(Button, { type: 'primary', onClick: closePrepaidBalanceDrill, style: H2_PRIMARY_BTN_STYLE }, '知道了'), width: 920, centered: true, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderPrepaidBalanceDrillPanel() ), React.createElement(Modal, { className: 'h2-statement-modal', title: '生成对账单', open: statementModal.open, onCancel: closeStatementModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closeStatementModal, style: { borderRadius: 8 } }, '取消'), statementModal.phase === 'draft' ? React.createElement(Button, { type: 'primary', onClick: handleSubmitStatement, style: H2_PRIMARY_BTN_STYLE }, '提交对账单') : React.createElement(Button, { type: 'primary', onClick: handleGenerateStatement, style: H2_PRIMARY_BTN_STYLE }, '生成对账单') ), centered: true, width: 820, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderStatementPanel() ), React.createElement(Modal, { className: 'h2-statement-history-modal', title: '查看对账记录', open: statementHistoryModal.open, onCancel: closeStatementHistoryModal, footer: React.createElement(Button, { type: 'primary', onClick: closeStatementHistoryModal, style: H2_PRIMARY_BTN_STYLE }, '知道了'), centered: true, width: 1080, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderStatementHistoryPanel() ), React.createElement(Modal, { className: 'h2-statement-modal', title: '对账明细', open: statementDetailModal.open, onCancel: closeStatementDetailModal, footer: React.createElement(Button, { type: 'primary', onClick: closeStatementDetailModal, style: H2_PRIMARY_BTN_STYLE }, '知道了'), centered: true, width: 820, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderStatementDetailPanel() ), React.createElement(Modal, { className: 'h2-price-config-modal', title: '价格配置', open: priceModal.open, onCancel: closePriceModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closePriceModal, style: { borderRadius: 8 } }, '取消'), React.createElement(Button, { type: 'primary', onClick: handleSavePriceConfig, style: H2_PRIMARY_BTN_STYLE }, '保存') ), centered: true, width: 820, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderPriceConfigPanel() ), React.createElement(Modal, { title: '余额提醒设置', open: balanceAlertModal.open, onCancel: closeBalanceAlertModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closeBalanceAlertModal, style: { borderRadius: 8 } }, '取消'), React.createElement(Button, { type: 'primary', onClick: handleSaveBalanceAlert, style: H2_PRIMARY_BTN_STYLE }, '保存') ), centered: true, width: 480, destroyOnClose: true }, balanceAlertModal.record ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 14 } }, React.createElement('div', { style: { padding: '12px 14px', borderRadius: 10, background: '#f8fafc', border: '1px solid #e2e8f0', fontSize: 13, color: '#475569', lineHeight: 1.55 } }, React.createElement('div', { style: { fontWeight: 700, color: '#0f172a', marginBottom: 4 } }, balanceAlertModal.record.name || '—'), '当预付余额低于所设金额时,站点将列入「预付余额预警站点」。当前预付余额:', React.createElement('strong', { style: { color: h2NumOrZero(balanceAlertModal.record.prepaidBalance) < 0 ? '#dc2626' : '#059669', fontVariantNumeric: 'tabular-nums' } }, h2FormatYuanNum(balanceAlertModal.record.prepaidBalance)), ' 元' ), React.createElement(Form, { layout: 'vertical', requiredMark: false }, React.createElement(Form.Item, { label: React.createElement('span', null, React.createElement('span', { style: { color: '#ef4444', marginRight: 4 } }, '*'), '提醒金额(元)') }, React.createElement(Input, { className: 'h2-statement-receipt-amount-input', inputMode: 'decimal', prefix: '¥', style: { width: '100%', borderRadius: 8 }, placeholder: '请输入提醒金额', value: balanceAlertModal.threshold || '', onChange: function (e) { var next = h2SanitizeReceiptAmountInput(e.target.value); setBalanceAlertModal(function (m) { return Object.assign({}, m, { threshold: next }); }); }, onBlur: function (e) { var formatted = h2FormatReceiptAmountInput(e.target.value); setBalanceAlertModal(function (m) { return Object.assign({}, m, { threshold: formatted }); }); } }) ) ) ) : null ), React.createElement(Modal, { className: 'h2-recharge-modal', title: '发起充值单', open: rechargeModal.open, onCancel: closeRechargeModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closeRechargeModal, style: { borderRadius: 8 } }, '取消'), React.createElement(Button, { type: 'primary', onClick: handleSubmitRecharge, style: H2_PRIMARY_BTN_STYLE }, '发起付款流程') ), centered: true, width: 1120, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderRechargeModalPanel() ), React.createElement(Modal, { className: 'h2-alert-station-modal', title: alertStationModal.title || '站点列表', open: alertStationModal.open, onCancel: closeAlertStationModal, footer: React.createElement(Space, null, React.createElement(Button, { onClick: closeAlertStationModal, style: { borderRadius: 8 } }, '关闭'), React.createElement(Button, { type: 'primary', disabled: !(alertStationModal.stations || []).length, onClick: handleBatchRechargeFromAlert, style: H2_PRIMARY_BTN_STYLE }, '一键发起充值单') ), centered: true, width: 1200, destroyOnClose: true, styles: { body: { maxHeight: '78vh', overflow: 'auto' } } }, renderAlertStationPanel() ) ); }; if (typeof window !== 'undefined') window.Component = Component;