Initial commit: ONE-OS project
Made-with: Cursor
This commit is contained in:
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
506
blueway/数据集管理.jsx
Normal file
506
blueway/数据集管理.jsx
Normal file
@@ -0,0 +1,506 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 数据集管理 - 模仿 GitLab 项目列表风格(纯 React,不依赖 antd,兼容 Axhub)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
// GitLab 风格色值(Pajamas 近似)
|
||||
var colors = {
|
||||
pageBg: '#fafafa',
|
||||
cardBg: '#ffffff',
|
||||
border: '#e5e5e5',
|
||||
borderLight: '#ebebeb',
|
||||
text: '#333333',
|
||||
textSecondary: '#666666',
|
||||
textMuted: '#999999',
|
||||
primary: '#1f75cb',
|
||||
primaryHover: '#1068bf',
|
||||
link: '#1f75cb',
|
||||
success: '#108548',
|
||||
warning: '#c17d10',
|
||||
danger: '#dd2b0e',
|
||||
hoverBg: '#f5f5f5',
|
||||
tabActive: '#333333',
|
||||
tabInactive: '#666666'
|
||||
};
|
||||
|
||||
var filterState = useState({
|
||||
keyword: '',
|
||||
datasetType: '',
|
||||
status: ''
|
||||
});
|
||||
var filters = filterState[0];
|
||||
var setFilters = filterState[1];
|
||||
|
||||
var activeTabState = useState('all');
|
||||
var activeTab = activeTabState[0];
|
||||
var setActiveTab = activeTabState[1];
|
||||
|
||||
var sortState = useState('updated_desc');
|
||||
var sortBy = sortState[0];
|
||||
var setSortBy = sortState[1];
|
||||
|
||||
var listState = useState([
|
||||
{ id: '1', name: '销售数据集', type: '表格', recordCount: 12000, createTime: '2025-02-20 10:00', updateTime: '2025-02-24 14:00', status: '已发布', remark: '销售业务数据', isStarred: true, owner: '当前用户' },
|
||||
{ id: '2', name: '客户画像数据集', type: '表格', recordCount: 8500, createTime: '2025-02-19 14:30', updateTime: '2025-02-23 09:00', status: '草稿', remark: '客户标签与画像', isStarred: false, owner: '当前用户' },
|
||||
{ id: '3', name: '订单明细数据集', type: '表格', recordCount: 25600, createTime: '2025-02-18 09:15', updateTime: '2025-02-22 16:30', status: '已发布', remark: '订单明细与物流', isStarred: true, owner: '当前用户' },
|
||||
{ id: '4', name: '库存数据集', type: '文件', recordCount: 5200, createTime: '2025-02-17 11:00', updateTime: '2025-02-21 10:00', status: '已下线', remark: '仓库库存快照', isStarred: false, owner: '其他用户' }
|
||||
]);
|
||||
var dataList = listState[0];
|
||||
var setDataList = listState[1];
|
||||
|
||||
var pageState = useState(1);
|
||||
var page = pageState[0];
|
||||
var setPage = pageState[1];
|
||||
var pageSizeState = useState(20);
|
||||
var pageSize = pageSizeState[0];
|
||||
|
||||
var modalState = useState({ open: false, record: null, mode: 'view' });
|
||||
var modalOpen = modalState[0].open;
|
||||
var modalRecord = modalState[0].record;
|
||||
var modalMode = modalState[0].mode;
|
||||
var setModalState = modalState[1];
|
||||
|
||||
var dropdownOpenIdState = useState(null);
|
||||
var dropdownOpenId = dropdownOpenIdState[0];
|
||||
var setDropdownOpenId = dropdownOpenIdState[1];
|
||||
|
||||
var confirmState = useState({ open: false, record: null });
|
||||
var confirmOpen = confirmState[0].open;
|
||||
var confirmRecord = confirmState[0].record;
|
||||
var setConfirmState = confirmState[1];
|
||||
|
||||
var toastState = useState({ show: false, text: '' });
|
||||
var toastShow = toastState[0].show;
|
||||
var toastText = toastState[0].text;
|
||||
var setToastState = toastState[1];
|
||||
|
||||
var typeOptions = useMemo(function () {
|
||||
return [
|
||||
{ value: '表格', label: '表格' },
|
||||
{ value: '文件', label: '文件' },
|
||||
{ value: 'API', label: 'API' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var statusOptions = useMemo(function () {
|
||||
return [
|
||||
{ value: '草稿', label: '草稿' },
|
||||
{ value: '已发布', label: '已发布' },
|
||||
{ value: '已下线', label: '已下线' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var sortOptions = useMemo(function () {
|
||||
return [
|
||||
{ value: 'updated_desc', label: '最近更新' },
|
||||
{ value: 'created_desc', label: '最近创建' },
|
||||
{ value: 'name_asc', label: '名称 A-Z' },
|
||||
{ value: 'name_desc', label: '名称 Z-A' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var filteredList = useMemo(function () {
|
||||
var list = dataList.slice();
|
||||
if (activeTab === 'mine') list = list.filter(function (r) { return r.owner === '当前用户'; });
|
||||
if (activeTab === 'published') list = list.filter(function (r) { return r.status === '已发布'; });
|
||||
if (activeTab === 'draft') list = list.filter(function (r) { return r.status === '草稿'; });
|
||||
if (activeTab === 'starred') list = list.filter(function (r) { return r.isStarred; });
|
||||
if (filters.keyword) {
|
||||
var kw = filters.keyword.trim().toLowerCase();
|
||||
list = list.filter(function (r) {
|
||||
return (r.name && r.name.toLowerCase().indexOf(kw) !== -1) ||
|
||||
(r.remark && r.remark.toLowerCase().indexOf(kw) !== -1);
|
||||
});
|
||||
}
|
||||
if (filters.datasetType) list = list.filter(function (r) { return r.type === filters.datasetType; });
|
||||
if (filters.status) list = list.filter(function (r) { return r.status === filters.status; });
|
||||
if (sortBy === 'updated_desc') list.sort(function (a, b) { return (b.updateTime || b.createTime || '').localeCompare(a.updateTime || a.createTime || ''); });
|
||||
else if (sortBy === 'created_desc') list.sort(function (a, b) { return (b.createTime || '').localeCompare(a.createTime || ''); });
|
||||
else if (sortBy === 'name_asc') list.sort(function (a, b) { return (a.name || '').localeCompare(b.name || ''); });
|
||||
else if (sortBy === 'name_desc') list.sort(function (a, b) { return (b.name || '').localeCompare(a.name || ''); });
|
||||
return list;
|
||||
}, [dataList, activeTab, filters, sortBy]);
|
||||
|
||||
var displayList = useMemo(function () {
|
||||
var start = (page - 1) * pageSize;
|
||||
return filteredList.slice(start, start + pageSize);
|
||||
}, [filteredList, page, pageSize]);
|
||||
|
||||
var totalCount = filteredList.length;
|
||||
|
||||
var handleQuery = useCallback(function () { setPage(1); }, []);
|
||||
var handleReset = useCallback(function () {
|
||||
setFilters({ keyword: '', datasetType: '', status: '' });
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
var openModal = useCallback(function (record, mode) {
|
||||
setModalState({ open: true, record: record || null, mode: mode || 'view' });
|
||||
setDropdownOpenId(null);
|
||||
}, []);
|
||||
var closeModal = useCallback(function () {
|
||||
setModalState({ open: false, record: null, mode: 'view' });
|
||||
}, []);
|
||||
|
||||
var showToast = useCallback(function (text) {
|
||||
setToastState({ show: true, text: text });
|
||||
setTimeout(function () { setToastState({ show: false, text: '' }); }, 2000);
|
||||
}, []);
|
||||
|
||||
var toggleStar = useCallback(function (record, e) {
|
||||
if (e) e.stopPropagation();
|
||||
setDataList(dataList.map(function (r) {
|
||||
if (r.id === record.id) return Object.assign({}, r, { isStarred: !r.isStarred });
|
||||
return r;
|
||||
}));
|
||||
}, [dataList]);
|
||||
|
||||
var openConfirm = useCallback(function (record) {
|
||||
setConfirmState({ open: true, record: record });
|
||||
setDropdownOpenId(null);
|
||||
}, []);
|
||||
var closeConfirm = useCallback(function () {
|
||||
setConfirmState({ open: false, record: null });
|
||||
}, []);
|
||||
var handleDelete = useCallback(function (record) {
|
||||
setDataList(dataList.filter(function (r) { return r.id !== record.id; }));
|
||||
closeConfirm();
|
||||
showToast('已删除');
|
||||
}, [dataList]);
|
||||
|
||||
function statusTag(status) {
|
||||
var bg = status === '已发布' ? '#e7f5ec' : (status === '草稿' ? '#f0f0f0' : '#fdf3e6');
|
||||
var color = status === '已发布' ? colors.success : (status === '草稿' ? colors.textSecondary : colors.warning);
|
||||
return React.createElement('span', {
|
||||
style: {
|
||||
display: 'inline-block',
|
||||
padding: '2px 8px',
|
||||
borderRadius: 4,
|
||||
fontSize: 12,
|
||||
background: bg,
|
||||
color: color
|
||||
}
|
||||
}, status);
|
||||
}
|
||||
|
||||
var tabs = [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'mine', label: '我创建的' },
|
||||
{ key: 'starred', label: '已加星' },
|
||||
{ key: 'published', label: '已发布' },
|
||||
{ key: 'draft', label: '草稿' }
|
||||
];
|
||||
|
||||
return React.createElement('div', {
|
||||
style: { minHeight: '100vh', background: colors.pageBg, padding: '24px 24px 48px', position: 'relative' }
|
||||
},
|
||||
// 标题栏
|
||||
React.createElement('div', {
|
||||
style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 12 }
|
||||
},
|
||||
React.createElement('h1', { style: { margin: 0, fontSize: 24, fontWeight: 600, color: colors.text } }, '数据集'),
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
onClick: function () { openModal(null, 'add'); },
|
||||
style: {
|
||||
padding: '6px 16px',
|
||||
border: 'none',
|
||||
borderRadius: 6,
|
||||
background: colors.primary,
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
cursor: 'pointer',
|
||||
fontWeight: 500
|
||||
}
|
||||
}, '新建数据集')
|
||||
),
|
||||
|
||||
// 搜索与筛选
|
||||
React.createElement('div', {
|
||||
style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderRadius: 6, padding: '12px 16px', marginBottom: 0 }
|
||||
},
|
||||
React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12 } },
|
||||
React.createElement('div', { style: { display: 'flex', width: 280 } },
|
||||
React.createElement('input', {
|
||||
type: 'text',
|
||||
placeholder: '搜索数据集…',
|
||||
value: filters.keyword,
|
||||
onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.keyword = e.target.value; return g; }); },
|
||||
onKeyDown: function (e) { if (e.key === 'Enter') handleQuery(); },
|
||||
style: {
|
||||
flex: 1,
|
||||
padding: '6px 12px',
|
||||
border: '1px solid ' + colors.border,
|
||||
borderRight: 'none',
|
||||
borderRadius: '6px 0 0 6px',
|
||||
fontSize: 14,
|
||||
outline: 'none'
|
||||
}
|
||||
}),
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
onClick: handleQuery,
|
||||
style: {
|
||||
padding: '6px 12px',
|
||||
border: '1px solid ' + colors.border,
|
||||
borderRadius: '0 6px 6px 0',
|
||||
background: colors.pageBg,
|
||||
cursor: 'pointer',
|
||||
fontSize: 14
|
||||
}
|
||||
}, '搜索')
|
||||
),
|
||||
React.createElement('select', {
|
||||
value: filters.datasetType,
|
||||
onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.datasetType = e.target.value; return g; }); },
|
||||
style: { width: 100, padding: '6px 8px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14 }
|
||||
},
|
||||
React.createElement('option', { value: '' }, '类型'),
|
||||
typeOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); })
|
||||
),
|
||||
React.createElement('select', {
|
||||
value: filters.status,
|
||||
onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.status = e.target.value; return g; }); },
|
||||
style: { width: 100, padding: '6px 8px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14 }
|
||||
},
|
||||
React.createElement('option', { value: '' }, '状态'),
|
||||
statusOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); })
|
||||
),
|
||||
React.createElement('button', { type: 'button', onClick: handleQuery, style: { padding: '6px 14px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '搜索'),
|
||||
React.createElement('button', { type: 'button', onClick: handleReset, style: { padding: '6px 14px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '重置'),
|
||||
React.createElement('span', { style: { marginLeft: 'auto', color: colors.textMuted, fontSize: 12 } },
|
||||
'排序:',
|
||||
React.createElement('select', {
|
||||
value: sortBy,
|
||||
onChange: function (e) { setSortBy(e.target.value); },
|
||||
style: { width: 120, marginLeft: 4, padding: '4px 8px', border: 'none', background: 'transparent', fontSize: 12, color: colors.textSecondary, cursor: 'pointer' }
|
||||
},
|
||||
sortOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); })
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// Tab
|
||||
React.createElement('div', {
|
||||
style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderTop: 'none', borderRadius: '0 0 6px 6px', padding: '0 16px', marginBottom: 16, display: 'flex', gap: 8 }
|
||||
},
|
||||
tabs.map(function (t) {
|
||||
var isActive = activeTab === t.key;
|
||||
return React.createElement('button', {
|
||||
key: t.key,
|
||||
type: 'button',
|
||||
onClick: function () { setActiveTab(t.key); setPage(1); },
|
||||
style: {
|
||||
padding: '12px 4px',
|
||||
margin: 0,
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
cursor: 'pointer',
|
||||
fontSize: 14,
|
||||
color: isActive ? colors.tabActive : colors.tabInactive,
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
borderBottom: '2px solid ' + (isActive ? colors.primary : 'transparent'),
|
||||
marginBottom: -1
|
||||
}
|
||||
}, t.label);
|
||||
})
|
||||
),
|
||||
|
||||
// 列表
|
||||
React.createElement('div', {
|
||||
style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderRadius: 6, overflow: 'hidden' }
|
||||
},
|
||||
displayList.length === 0
|
||||
? React.createElement('div', { style: { padding: 48, textAlign: 'center', color: colors.textMuted } }, activeTab === 'all' && !filters.keyword ? '暂无数据集,点击「新建数据集」创建' : '没有匹配的数据集')
|
||||
: displayList.map(function (row) {
|
||||
var menuOpen = dropdownOpenId === row.id;
|
||||
return React.createElement('div', {
|
||||
key: row.id,
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
padding: '12px 16px',
|
||||
borderBottom: '1px solid ' + colors.borderLight,
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.15s'
|
||||
},
|
||||
onMouseEnter: function (e) { e.currentTarget.style.background = colors.hoverBg; },
|
||||
onMouseLeave: function (e) { e.currentTarget.style.background = 'transparent'; },
|
||||
onClick: function (e) {
|
||||
if (e.target.closest('button') || e.target.closest('[data-dropdown]')) return;
|
||||
openModal(row, 'view');
|
||||
}
|
||||
},
|
||||
React.createElement('div', {
|
||||
style: {
|
||||
width: 40, height: 40, borderRadius: 6, background: colors.pageBg, border: '1px solid ' + colors.border,
|
||||
marginRight: 12, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18, color: colors.primary
|
||||
}
|
||||
}, '\uD83D\uDCCA'),
|
||||
React.createElement('div', { style: { flex: 1, minWidth: 0 } },
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8, marginBottom: 4 } },
|
||||
React.createElement('span', { style: { fontSize: 14, fontWeight: 600, color: colors.link } }, row.name),
|
||||
statusTag(row.status),
|
||||
React.createElement('span', { style: { fontSize: 12, color: colors.textMuted } }, row.type + ' · ' + (row.recordCount != null ? row.recordCount.toLocaleString() + ' 条' : '-'))
|
||||
),
|
||||
row.remark ? React.createElement('div', { style: { fontSize: 13, color: colors.textSecondary, marginBottom: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, row.remark) : null,
|
||||
React.createElement('div', { style: { fontSize: 12, color: colors.textMuted } }, '更新于 ' + (row.updateTime || row.createTime || '-'))
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }, onClick: function (e) { e.stopPropagation(); } },
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
title: row.isStarred ? '取消加星' : '加星',
|
||||
onClick: function (e) { toggleStar(row, e); },
|
||||
style: { border: 'none', background: 'none', cursor: 'pointer', padding: 4, fontSize: 16, color: row.isStarred ? '#c17d10' : colors.textMuted }
|
||||
}, '\u2605'),
|
||||
React.createElement('div', { style: { position: 'relative' }, 'data-dropdown': true },
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
onClick: function (e) { e.stopPropagation(); setDropdownOpenId(menuOpen ? null : row.id); },
|
||||
style: { border: 'none', background: 'none', cursor: 'pointer', padding: '2px 8px', fontSize: 16, color: colors.textSecondary }
|
||||
}, '\u22EE'),
|
||||
menuOpen ? React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: '100%',
|
||||
marginTop: 4,
|
||||
background: colors.cardBg,
|
||||
border: '1px solid ' + colors.border,
|
||||
borderRadius: 6,
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||
minWidth: 100,
|
||||
zIndex: 10
|
||||
}
|
||||
},
|
||||
React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13 }, onClick: function () { openModal(row, 'view'); setDropdownOpenId(null); } }, '查看'),
|
||||
React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13 }, onClick: function () { openModal(row, 'edit'); setDropdownOpenId(null); } }, '编辑'),
|
||||
React.createElement('div', { style: { borderTop: '1px solid ' + colors.borderLight } }),
|
||||
React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13, color: colors.danger }, onClick: function () { openConfirm(row); } }, '删除')
|
||||
) : null
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
),
|
||||
|
||||
// 分页
|
||||
totalCount > 0 ? React.createElement('div', {
|
||||
style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', padding: '16px 0', gap: 16, fontSize: 13, color: colors.textSecondary }
|
||||
},
|
||||
React.createElement('span', null, '共 ' + totalCount + ' 项'),
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
disabled: page <= 1,
|
||||
onClick: function () { setPage(page - 1); },
|
||||
style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page <= 1 ? 'not-allowed' : 'pointer', opacity: page <= 1 ? 0.6 : 1 }
|
||||
}, '上一页'),
|
||||
React.createElement('span', null, page + ' / ' + Math.max(1, Math.ceil(totalCount / pageSize))),
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
disabled: page >= Math.ceil(totalCount / pageSize),
|
||||
onClick: function () { setPage(page + 1); },
|
||||
style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page >= Math.ceil(totalCount / pageSize) ? 'not-allowed' : 'pointer', opacity: page >= Math.ceil(totalCount / pageSize) ? 0.6 : 1 }
|
||||
}, '下一页')
|
||||
)
|
||||
) : null,
|
||||
|
||||
// 弹窗(查看/编辑/新增)
|
||||
modalOpen && React.createElement('div', {
|
||||
style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
onClick: function (e) { if (e.target === e.currentTarget) closeModal(); }
|
||||
},
|
||||
React.createElement('div', {
|
||||
style: { background: colors.cardBg, borderRadius: 8, width: 520, maxWidth: '90vw', maxHeight: '90vh', overflow: 'auto', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' },
|
||||
onClick: function (e) { e.stopPropagation(); }
|
||||
},
|
||||
React.createElement('div', { style: { padding: '16px 24px', borderBottom: '1px solid ' + colors.border, fontSize: 16, fontWeight: 600 } },
|
||||
modalMode === 'view' ? '查看数据集' : (modalMode === 'edit' ? '编辑数据集' : '新建数据集')
|
||||
),
|
||||
(modalRecord || modalMode === 'add') && React.createElement('div', { style: { padding: 24 } },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '数据集名称'),
|
||||
modalMode === 'view' ? (modalRecord && modalRecord.name) : React.createElement('input', {
|
||||
type: 'text',
|
||||
placeholder: '请输入名称',
|
||||
defaultValue: modalRecord && modalRecord.name,
|
||||
style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '类型'),
|
||||
modalMode === 'view' ? (modalRecord && modalRecord.type) : React.createElement('select', {
|
||||
defaultValue: modalRecord && modalRecord.type,
|
||||
style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' }
|
||||
}, typeOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); }))
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '备注'),
|
||||
modalMode === 'view' ? (modalRecord && modalRecord.remark) || '-' : React.createElement('textarea', {
|
||||
placeholder: '选填',
|
||||
defaultValue: modalRecord && modalRecord.remark,
|
||||
rows: 3,
|
||||
style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box', resize: 'vertical' }
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { padding: '12px 24px', borderTop: '1px solid ' + colors.border, display: 'flex', justifyContent: 'flex-end', gap: 8 } },
|
||||
modalMode === 'view'
|
||||
? React.createElement('button', { type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '关闭')
|
||||
: [
|
||||
React.createElement('button', { key: 'cancel', type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'),
|
||||
React.createElement('button', {
|
||||
key: 'save',
|
||||
type: 'button',
|
||||
onClick: function () { showToast(modalMode === 'add' ? '创建成功' : '已保存'); closeModal(); },
|
||||
style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.primary, color: '#fff', cursor: 'pointer', fontSize: 14 }
|
||||
}, modalMode === 'add' ? '创建' : '保存')
|
||||
]
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// 删除确认弹窗
|
||||
confirmOpen && confirmRecord && React.createElement('div', {
|
||||
style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1001, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
onClick: function (e) { if (e.target === e.currentTarget) closeConfirm(); }
|
||||
},
|
||||
React.createElement('div', {
|
||||
style: { background: colors.cardBg, borderRadius: 8, padding: 24, width: 400, maxWidth: '90vw', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' },
|
||||
onClick: function (e) { e.stopPropagation(); }
|
||||
},
|
||||
React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 8 } }, '删除数据集'),
|
||||
React.createElement('div', { style: { color: colors.textSecondary, marginBottom: 24 } }, '确定要删除「' + (confirmRecord.name || '') + '」吗?此操作不可恢复。'),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } },
|
||||
React.createElement('button', { type: 'button', onClick: closeConfirm, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'),
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
onClick: function () { handleDelete(confirmRecord); },
|
||||
style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.danger, color: '#fff', cursor: 'pointer', fontSize: 14 }
|
||||
}, '删除')
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// 轻提示
|
||||
toastShow && React.createElement('div', {
|
||||
style: {
|
||||
position: 'fixed',
|
||||
left: '50%',
|
||||
bottom: 48,
|
||||
transform: 'translateX(-50%)',
|
||||
padding: '8px 16px',
|
||||
background: 'rgba(0,0,0,0.75)',
|
||||
color: '#fff',
|
||||
borderRadius: 6,
|
||||
fontSize: 14,
|
||||
zIndex: 1002
|
||||
}
|
||||
}, toastText)
|
||||
);
|
||||
};
|
||||
125
docs/ARCO-DESIGN-SPEC.md
Normal file
125
docs/ARCO-DESIGN-SPEC.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Arco Design 设计规范学习笔记
|
||||
|
||||
> 参考:[Arco Design 设计原则](https://arco.design/docs/spec/philosophy)
|
||||
|
||||
## 一、设计语言定位:务实的浪漫主义
|
||||
|
||||
Arco Design 用「**务实的浪漫主义**」形容自己的设计语言,也代表其工作模式。
|
||||
|
||||
- **务实**:清晰与一致 → 清晰的效率提升是基础,品牌一致性是目的
|
||||
- **浪漫**:韵律与开放 → 推敲设计的韵律,用包容开放的体系解决问题
|
||||
|
||||
设计原则相当于语言体系中的「修辞手法」,在实际设计中提供**模式化的思考结构**,让整个设计系统更完整、可复用。
|
||||
|
||||
---
|
||||
|
||||
## 二、八大设计原则
|
||||
|
||||
### 1. 及时反馈
|
||||
|
||||
**系统应让用户清楚当前状态,并及时给出对应反馈。**
|
||||
|
||||
- 操作后要有明确、即时的状态变化(如加载、成功、失败)
|
||||
- 避免用户不确定「有没有生效」「进行到哪一步」
|
||||
|
||||
---
|
||||
|
||||
### 2. 贴近现实
|
||||
|
||||
**用用户的语言、用词和熟悉的概念,而不是系统术语。**
|
||||
|
||||
- 遵循现实世界的惯例
|
||||
- 让信息符合自然思考逻辑
|
||||
- 避免技术黑话、内部用语
|
||||
|
||||
---
|
||||
|
||||
### 3. 系统一致性
|
||||
|
||||
**同一用语、功能、操作在系统内保持一致。**
|
||||
|
||||
- 相同功能用相同名称、相同交互方式
|
||||
- 相同类型的反馈用相同形式(如成功/错误提示)
|
||||
- 有利于降低学习成本、建立心智模型
|
||||
|
||||
---
|
||||
|
||||
### 4. 防止错误发生
|
||||
|
||||
**比「出错后再提示」更好的是:用设计提前避免错误。**
|
||||
|
||||
- 在用户做出选择/动作之前,就减少容易混淆或错误的选择
|
||||
- 通过禁用、约束、确认、默认值等方式预防问题
|
||||
- 错误提示是兜底,不是主要手段
|
||||
|
||||
---
|
||||
|
||||
### 5. 遵从习惯
|
||||
|
||||
**减少用户对操作目标的记忆负荷,动作和选项应尽量可见。**
|
||||
|
||||
- 例如:填完表单后,下一步应是「生成/提交表单」,而不是笼统的「完成」
|
||||
- 步骤、选项要符合用户对流程的预期
|
||||
- 减少「需要记住才能操作」的情况
|
||||
|
||||
---
|
||||
|
||||
### 6. 突出重点
|
||||
|
||||
**用户的行为多是「扫」而不是「读」或「看」。**
|
||||
|
||||
- 设计中应**突出重点**,弱化或剔除无关信息
|
||||
- 重要对话/流程中不堆砌无关内容
|
||||
- 通过层级、对比、留白引导注意力
|
||||
|
||||
---
|
||||
|
||||
### 7. 错误帮助
|
||||
|
||||
**错误信息要用自然语言表达,而不是仅显示错误码(如 404)。**
|
||||
|
||||
- 准确说明问题所在
|
||||
- 给出建设性的解决建议
|
||||
- 帮助用户从错误中恢复,将损失降到最低
|
||||
- 提供清晰的说明和下一步指引
|
||||
|
||||
---
|
||||
|
||||
### 8. 人性化帮助
|
||||
|
||||
**理想情况是系统本身无需帮助文档;若需要帮助,应分层、适度。**
|
||||
|
||||
帮助的优先级建议:
|
||||
|
||||
1. **无需提示**:设计自解释,用户自然能懂
|
||||
2. **一次性提示**:如首次使用的引导、Tooltip
|
||||
3. **常驻提示**:如表单旁的说明、占位符
|
||||
4. **帮助文档**:作为最后兜底,便于深度查阅
|
||||
|
||||
---
|
||||
|
||||
## 三、设计规范结构速览(Arco 官网)
|
||||
|
||||
设计相关文档大致包括:
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------------|------|
|
||||
| 设计价值观 | 务实与浪漫的价值观定义 |
|
||||
| 设计原则 | 上述八大原则(本文档重点) |
|
||||
| 样式指南 | 视觉与交互的样式规范 |
|
||||
| 组件用法 | 各组件(Button、Form、Table 等)的设计与使用规范 |
|
||||
|
||||
开发侧还有:快速上手、暗黑模式、颜色、设计变量(Token)、国际化、定制主题等,与设计规范配合使用。
|
||||
|
||||
---
|
||||
|
||||
## 四、在项目中的使用建议
|
||||
|
||||
1. **做界面时**:先对照「及时反馈、贴近现实、一致性、防错、遵从习惯、突出重点」检查一版交互与文案。
|
||||
2. **写错误与帮助**:遵循「错误帮助」和「人性化帮助」,避免只扔错误码,并控制提示的侵入性。
|
||||
3. **统一用语与交互**:建立一份产品用词表与组件使用规范,对齐「系统一致性」。
|
||||
4. **用 Arco 组件时**:优先按官方组件用法与样式指南使用,便于保持「务实的浪漫主义」下的整体体验。
|
||||
|
||||
---
|
||||
|
||||
*文档根据 [Arco Design 设计原则](https://arco.design/docs/spec/philosophy) 整理,便于团队统一理解与落地。*
|
||||
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "one-os",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@arco-design/web-react": "^2.67.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.28.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^5.4.10"
|
||||
}
|
||||
}
|
||||
15
src/App.tsx
Normal file
15
src/App.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import VehicleManage from '@/pages/VehicleManage';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<VehicleManage />} />
|
||||
<Route path="/vehicle/:id" element={<VehicleManage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import '@arco-design/web-react/dist/css/arco.css';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
116
src/pages/VehicleManage/VehiclePlateModal.tsx
Normal file
116
src/pages/VehicleManage/VehiclePlateModal.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Modal, Form, Input, Upload, Image, Message } from '@arco-design/web-react';
|
||||
import type { VehicleRecord } from '@/types/vehicle';
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
vehicle: VehicleRecord | null;
|
||||
onCancel: () => void;
|
||||
onOk: (vin: string, plateNo: string) => void;
|
||||
};
|
||||
|
||||
export default function VehiclePlateModal({ visible, vehicle, onCancel, onOk }: Props) {
|
||||
const [ocrLoading, setOcrLoading] = useState(false);
|
||||
const [licenseImageUrl, setLicenseImageUrl] = useState<string>('');
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleUploadChange = (fileList: { file?: File; url?: string }[]) => {
|
||||
const file = fileList[0]?.file;
|
||||
if (!file) return;
|
||||
const url = URL.createObjectURL(file);
|
||||
setLicenseImageUrl(url);
|
||||
setOcrLoading(true);
|
||||
// 模拟 OCR 识别,实际对接 OCR 接口
|
||||
setTimeout(() => {
|
||||
form.setFieldsValue({ vin: vehicle?.vin ?? '', plateNo: vehicle?.plateNo ?? '' });
|
||||
setOcrLoading(false);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
form.validate().then((values) => {
|
||||
const v = (values.vin ?? '').trim();
|
||||
const p = (values.plateNo ?? '').trim();
|
||||
if (!v || !p) {
|
||||
Message.warning('请填写车辆识别代号与车牌号');
|
||||
return;
|
||||
}
|
||||
if (vehicle && v !== vehicle.vin) {
|
||||
Message.error('车辆识别代号与该车辆不匹配');
|
||||
return;
|
||||
}
|
||||
onOk(v, p);
|
||||
reset();
|
||||
}).catch(() => {});
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setLicenseImageUrl('');
|
||||
setOcrLoading(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
reset();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="确认上牌信息"
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
style={{ width: 720 }}
|
||||
unmountOnExit
|
||||
afterClose={reset}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 24, minHeight: 280 }}>
|
||||
<div style={{ flex: '0 0 320px' }}>
|
||||
<div style={{ marginBottom: 8, color: 'var(--color-text-2)' }}>行驶证照片</div>
|
||||
{licenseImageUrl ? (
|
||||
<Image
|
||||
width={320}
|
||||
height={200}
|
||||
src={licenseImageUrl}
|
||||
style={{ objectFit: 'contain', background: 'var(--color-fill-2)', borderRadius: 4 }}
|
||||
/>
|
||||
) : (
|
||||
<Upload
|
||||
accept="image/*"
|
||||
listType="picture-card"
|
||||
limit={1}
|
||||
onChange={handleUploadChange}
|
||||
customRequest={(option) => {
|
||||
const { file } = option;
|
||||
if (file) handleUploadChange([{ file: file as File }]);
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: 20, textAlign: 'center' }}>上传行驶证照片</div>
|
||||
</Upload>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
{ocrLoading ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 200 }}>
|
||||
<span style={{ color: 'var(--color-text-2)' }}>识别中,请勿关闭页面</span>
|
||||
</div>
|
||||
) : (
|
||||
<Form form={form} layout="vertical" initialValues={{ vin: vehicle?.vin ?? '', plateNo: vehicle?.plateNo ?? '' }}>
|
||||
<Form.Item label="车辆识别代号(VIN)" field="vin" rules={[{ required: true, message: '请填写车辆识别代号' }]}>
|
||||
<Input placeholder="根据行驶证照片反写,可编辑" />
|
||||
</Form.Item>
|
||||
<Form.Item label="车牌号" field="plateNo" rules={[{ required: true, message: '请填写车牌号' }]}>
|
||||
<Input placeholder="根据行驶证照片反写,可编辑" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
384
src/pages/VehicleManage/index.tsx
Normal file
384
src/pages/VehicleManage/index.tsx
Normal file
@@ -0,0 +1,384 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import {
|
||||
Breadcrumb,
|
||||
Form,
|
||||
Grid,
|
||||
Select,
|
||||
Cascader,
|
||||
Input,
|
||||
Button,
|
||||
Space,
|
||||
Table,
|
||||
Card,
|
||||
Message,
|
||||
Dropdown,
|
||||
Menu,
|
||||
Checkbox,
|
||||
Modal,
|
||||
Upload,
|
||||
} from '@arco-design/web-react';
|
||||
import type { TableColumnProps } from '@arco-design/web-react/es/Table';
|
||||
import {
|
||||
IconExport,
|
||||
IconImport,
|
||||
IconMore,
|
||||
IconSearch,
|
||||
IconRefresh,
|
||||
} from '@arco-design/web-react/icon';
|
||||
import {
|
||||
regionOptions,
|
||||
vehicleTypeOptions,
|
||||
brandOptions,
|
||||
modelOptions,
|
||||
customerOptions,
|
||||
businessDeptOptions,
|
||||
contractNoOptions,
|
||||
registeredOwnerOptions,
|
||||
mockVehicleList,
|
||||
} from '@/services/vehicle';
|
||||
import type { VehicleRecord, VehicleFilterForm } from '@/types/vehicle';
|
||||
import VehiclePlateModal from './VehiclePlateModal';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
const defaultFilter: VehicleFilterForm = {};
|
||||
|
||||
export default function VehicleManage() {
|
||||
const [filter, setFilter] = useState<VehicleFilterForm>(defaultFilter);
|
||||
const [plateNoQuick, setPlateNoQuick] = useState('');
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<VehicleRecord[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [plateModalVisible, setPlateModalVisible] = useState(false);
|
||||
const [plateModalVehicle, setPlateModalVehicle] = useState<VehicleRecord | null>(null);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const loadList = useCallback(
|
||||
(p = page, ps = pageSize) => {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
const res = mockVehicleList({
|
||||
...filter,
|
||||
plateNo: plateNoQuick || undefined,
|
||||
page: p,
|
||||
pageSize: ps,
|
||||
});
|
||||
setData(res.list);
|
||||
setTotal(res.total);
|
||||
setLoading(false);
|
||||
}, 300);
|
||||
},
|
||||
[filter, plateNoQuick, page, pageSize]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
loadList();
|
||||
}, [loadList]);
|
||||
|
||||
const onFilterSubmit = (values: VehicleFilterForm) => {
|
||||
setFilter(values);
|
||||
setPage(1);
|
||||
loadList(1, pageSize);
|
||||
};
|
||||
|
||||
const onFilterReset = () => {
|
||||
form.resetFields();
|
||||
setFilter(defaultFilter);
|
||||
setPlateNoQuick('');
|
||||
setPage(1);
|
||||
loadList(1, pageSize);
|
||||
};
|
||||
|
||||
const onExport = () => {
|
||||
if (selectedRowKeys.length === 0) {
|
||||
Message.warning('请先勾选要导出的车辆');
|
||||
return;
|
||||
}
|
||||
Message.info(`导出 ${selectedRowKeys.length} 条记录(联调时对接导出接口)`);
|
||||
};
|
||||
|
||||
const onBatchImport = () => {
|
||||
Message.info('批量导入(联调时对接导入接口)');
|
||||
};
|
||||
|
||||
const onView = (record: VehicleRecord) => {
|
||||
Message.info(`查看车辆详情:${record.plateNo || record.vin}`);
|
||||
// 实际可: navigate(`/vehicle/detail/${record.id}`);
|
||||
};
|
||||
|
||||
const onPlate = (record: VehicleRecord) => {
|
||||
setPlateModalVehicle(record);
|
||||
setPlateModalVisible(true);
|
||||
};
|
||||
|
||||
const onPlateModalOk = (vin: string, plateNo: string) => {
|
||||
if (plateModalVehicle && (vin !== plateModalVehicle.vin || !plateNo)) {
|
||||
Message.error('车辆识别代号与该车辆不匹配');
|
||||
return;
|
||||
}
|
||||
Message.success('上牌信息已更新');
|
||||
setPlateModalVisible(false);
|
||||
setPlateModalVehicle(null);
|
||||
loadList();
|
||||
};
|
||||
|
||||
const moreMenu = (record: VehicleRecord) => (
|
||||
<Menu>
|
||||
<Menu.Item key="plate" onClick={() => onPlate(record)}>车辆上牌</Menu.Item>
|
||||
<Menu.Item key="transfer">车辆过户</Menu.Item>
|
||||
<Menu.Item key="move">车辆异动</Menu.Item>
|
||||
<Menu.Item key="allocate">车辆调拨</Menu.Item>
|
||||
<Menu.Item key="scrap">车辆报废</Menu.Item>
|
||||
<Menu.Item key="inspect">车辆年审</Menu.Item>
|
||||
<Menu.Item key="sale">车辆出售</Menu.Item>
|
||||
<Menu.Item key="fault">故障提报</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const columns: TableColumnProps<VehicleRecord>[] = [
|
||||
{ title: '运营城市', dataIndex: 'operationCity', width: 120, fixed: 'left' },
|
||||
{ title: '车辆识别代号', dataIndex: 'vin', width: 180, fixed: 'left' },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', width: 100, fixed: 'left' },
|
||||
{ title: '车辆编号', dataIndex: 'vehicleNo', width: 100, render: (v) => v || '-' },
|
||||
{ title: '车辆类型', dataIndex: 'vehicleType', width: 100 },
|
||||
{ title: '品牌', dataIndex: 'brand', width: 90 },
|
||||
{ title: '型号', dataIndex: 'model', width: 90 },
|
||||
{ title: '车身颜色', dataIndex: 'bodyColor', width: 90 },
|
||||
{ title: '归属停车场', dataIndex: 'parkingLot', width: 110 },
|
||||
{ title: '客户名称', dataIndex: 'customerName', width: 100 },
|
||||
{ title: '业务部门', dataIndex: 'businessDept', width: 100 },
|
||||
{ title: '业务负责人', dataIndex: 'businessOwner', width: 100 },
|
||||
{ title: '运营状态', dataIndex: 'operationStatus', width: 90 },
|
||||
{ title: '库位状态', dataIndex: 'libStatus', width: 180 },
|
||||
{ title: '出库状态', dataIndex: 'outboundStatus', width: 100 },
|
||||
{ title: '整备状态', dataIndex: 'prepStatus', width: 90 },
|
||||
{ title: '过户状态', dataIndex: 'transferStatus', width: 120 },
|
||||
{ title: '维修状态', dataIndex: 'repairStatus', width: 110 },
|
||||
{ title: '证照状态', dataIndex: 'licenseStatus', width: 90 },
|
||||
{ title: '报废状态', dataIndex: 'scrapStatus', width: 90 },
|
||||
{ title: '登记所有权', dataIndex: 'registeredOwner', width: 160 },
|
||||
{ title: '在线状态', dataIndex: 'onlineStatus', width: 90 },
|
||||
{ title: '出厂年份', dataIndex: 'manufactureYear', width: 90 },
|
||||
{ title: '行驶公里数(KM)', dataIndex: 'mileage', width: 120, render: (v) => v?.toFixed(2) ?? '-' },
|
||||
{ title: '采购入库时间', dataIndex: 'purchaseTime', width: 120 },
|
||||
{ title: '行驶证注册日期', dataIndex: 'licenseRegisterDate', width: 130 },
|
||||
{ title: '行驶证检验有效期', dataIndex: 'licenseExpiry', width: 130 },
|
||||
{ title: '上次交车时间', dataIndex: 'lastDeliveryTime', width: 120 },
|
||||
{ title: '上次交车里程(KM)', dataIndex: 'lastDeliveryMileage', width: 130, render: (v) => (v ? Number(v).toFixed(2) : '-') },
|
||||
{ title: '上次还车时间', dataIndex: 'lastReturnTime', width: 120 },
|
||||
{ title: '上次还车里程(KM)', dataIndex: 'lastReturnMileage', width: 130, render: (v) => (v ? Number(v).toFixed(2) : '-') },
|
||||
{ title: '强制报废日期', dataIndex: 'forceScrapDate', width: 120 },
|
||||
{ title: '合同编号', dataIndex: 'contractNo', width: 110 },
|
||||
{ title: '当前位置', dataIndex: 'currentLocation', width: 300 },
|
||||
{ title: 'GPS最后上传时间', dataIndex: 'gpsLastTime', width: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'op',
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button type="text" size="small" onClick={() => onView(record)}>
|
||||
查看
|
||||
</Button>
|
||||
<Dropdown trigger="hover" position="br" droplist={moreMenu(record)}>
|
||||
<Button type="text" size="small" icon={<IconMore />}>
|
||||
更多
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const watchedBrand = Form.useWatch('brand', form);
|
||||
const modelOptionsByBrand = watchedBrand ? (modelOptions as Record<string, { value: string; label: string }[]>)[watchedBrand] ?? [] : [];
|
||||
|
||||
return (
|
||||
<div style={{ padding: 16, background: 'var(--color-fill-2)', minHeight: '100vh' }}>
|
||||
<Breadcrumb style={{ marginBottom: 16 }}>
|
||||
<Breadcrumb.Item>运维管理</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>车辆管理</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
|
||||
<Card title="筛选" style={{ marginBottom: 16 }}>
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
initialValues={defaultFilter}
|
||||
onSubmit={onFilterSubmit}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Form.Item label="运营城市" field="operationCity">
|
||||
<Cascader
|
||||
allowClear
|
||||
placeholder="请选择省-市"
|
||||
options={regionOptions}
|
||||
style={{ width: '100%' }}
|
||||
changeOnSelect
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="车辆类型" field="vehicleType">
|
||||
<Select allowClear placeholder="请选择" options={vehicleTypeOptions} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="品牌" field="brand">
|
||||
<Select allowClear placeholder="请选择" options={brandOptions} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="型号" field="model">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="请选择"
|
||||
options={modelOptionsByBrand}
|
||||
style={{ width: '100%' }}
|
||||
disabled={!watchedBrand}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="客户名称" field="customerName">
|
||||
<Select allowClear placeholder="请选择" options={customerOptions} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="归属业务部门" field="businessDept">
|
||||
<Select allowClear placeholder="请选择" options={businessDeptOptions} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="合同编号" field="contractNo">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="输入模糊匹配"
|
||||
options={contractNoOptions}
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item label="登记所有权" field="registeredOwner">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="输入模糊匹配"
|
||||
options={registeredOwnerOptions}
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit" icon={<IconSearch />}>
|
||||
查询
|
||||
</Button>
|
||||
<Button icon={<IconRefresh />} onClick={onFilterReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<Space>
|
||||
<Input
|
||||
placeholder="输入车牌号快速筛选"
|
||||
value={plateNoQuick}
|
||||
onChange={setPlateNoQuick}
|
||||
style={{ width: 200 }}
|
||||
allowClear
|
||||
onPressEnter={() => loadList(1, pageSize)}
|
||||
/>
|
||||
<Button type="primary" icon={<IconSearch />} onClick={() => { setPage(1); loadList(1, pageSize); }}>
|
||||
筛选
|
||||
</Button>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button icon={<IconExport />} onClick={onExport}>
|
||||
导出(联动多选)
|
||||
</Button>
|
||||
<Button icon={<IconImport />} onClick={onBatchImport}>
|
||||
批量导入
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={data}
|
||||
columns={[
|
||||
{
|
||||
title: (
|
||||
<Checkbox
|
||||
checked={selectedRowKeys.length === data.length && data.length > 0}
|
||||
indeterminate={selectedRowKeys.length > 0 && selectedRowKeys.length < data.length}
|
||||
onChange={(checked) => setSelectedRowKeys(checked ? data.map((r) => r.id) : [])}
|
||||
/>
|
||||
),
|
||||
width: 48,
|
||||
fixed: 'left',
|
||||
render: (_, record) => (
|
||||
<Checkbox
|
||||
checked={selectedRowKeys.includes(record.id)}
|
||||
onChange={(checked) =>
|
||||
setSelectedRowKeys((prev) =>
|
||||
checked ? [...prev, record.id] : prev.filter((k) => k !== record.id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
...columns,
|
||||
]}
|
||||
scroll={{ x: 3800 }}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total,
|
||||
showTotal: true,
|
||||
showJumper: true,
|
||||
sizeCanChange: true,
|
||||
pageSizeChange: (ps) => {
|
||||
setPageSize(ps);
|
||||
setPage(1);
|
||||
loadList(1, ps);
|
||||
},
|
||||
onChange: (p) => {
|
||||
setPage(p);
|
||||
loadList(p, pageSize);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<VehiclePlateModal
|
||||
visible={plateModalVisible}
|
||||
vehicle={plateModalVehicle}
|
||||
onCancel={() => { setPlateModalVisible(false); setPlateModalVehicle(null); }}
|
||||
onOk={onPlateModalOk}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
145
src/services/vehicle.ts
Normal file
145
src/services/vehicle.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { VehicleRecord, VehicleFilterForm } from '@/types/vehicle';
|
||||
|
||||
/** 模拟省-市二级地区 */
|
||||
export const regionOptions = [
|
||||
{
|
||||
value: 'guangdong',
|
||||
label: '广东省',
|
||||
children: [
|
||||
{ value: 'guangzhou', label: '广州市' },
|
||||
{ value: 'shenzhen', label: '深圳市' },
|
||||
{ value: 'dongguan', label: '东莞市' },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'zhejiang',
|
||||
label: '浙江省',
|
||||
children: [
|
||||
{ value: 'hangzhou', label: '杭州市' },
|
||||
{ value: 'ningbo', label: '宁波市' },
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'jiangsu',
|
||||
label: '江苏省',
|
||||
children: [
|
||||
{ value: 'nanjing', label: '南京市' },
|
||||
{ value: 'suzhou', label: '苏州市' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/** 车辆类型(从车辆类型表拉取) */
|
||||
export const vehicleTypeOptions = [
|
||||
{ value: 'light_truck', label: '轻卡' },
|
||||
{ value: 'van', label: '厢式车' },
|
||||
{ value: 'suv', label: 'SUV' },
|
||||
{ value: 'sedan', label: '轿车' },
|
||||
];
|
||||
|
||||
/** 品牌(从型号参数表拉取) */
|
||||
export const brandOptions = [
|
||||
{ value: 'brand_a', label: '品牌A' },
|
||||
{ value: 'brand_b', label: '品牌B' },
|
||||
{ value: 'brand_c', label: '品牌C' },
|
||||
];
|
||||
|
||||
/** 型号(从型号参数表拉取,可按品牌联动) */
|
||||
export const modelOptions: Record<string, { value: string; label: string }[]> = {
|
||||
brand_a: [
|
||||
{ value: 'model_a1', label: '型号A1' },
|
||||
{ value: 'model_a2', label: '型号A2' },
|
||||
],
|
||||
brand_b: [
|
||||
{ value: 'model_b1', label: '型号B1' },
|
||||
{ value: 'model_b2', label: '型号B2' },
|
||||
],
|
||||
brand_c: [
|
||||
{ value: 'model_c1', label: '型号C1' },
|
||||
],
|
||||
};
|
||||
|
||||
/** 客户名称(租赁/自营合同客户,含「无」) */
|
||||
export const customerOptions = [
|
||||
{ value: '__none__', label: '无' },
|
||||
{ value: 'customer_1', label: '客户甲' },
|
||||
{ value: 'customer_2', label: '客户乙' },
|
||||
{ value: 'customer_3', label: '客户丙' },
|
||||
];
|
||||
|
||||
/** 归属业务部门(含「无」) */
|
||||
export const businessDeptOptions = [
|
||||
{ value: '__none__', label: '无' },
|
||||
{ value: 'dept_1', label: '业务一部' },
|
||||
{ value: 'dept_2', label: '业务二部' },
|
||||
{ value: 'dept_3', label: '业务三部' },
|
||||
];
|
||||
|
||||
/** 合同编号列表(支持模糊匹配,此处为示例) */
|
||||
export const contractNoOptions = [
|
||||
{ value: 'HT-2024-001', label: 'HT-2024-001' },
|
||||
{ value: 'HT-2024-002', label: 'HT-2024-002' },
|
||||
{ value: 'HT-2024-003', label: 'HT-2024-003' },
|
||||
{ value: 'ZY-2024-001', label: 'ZY-2024-001' },
|
||||
];
|
||||
|
||||
/** 登记所有权列表(支持模糊匹配) */
|
||||
export const registeredOwnerOptions = [
|
||||
{ value: 'owner_1', label: '某某物流有限公司' },
|
||||
{ value: 'owner_2', label: '某某租赁有限公司' },
|
||||
];
|
||||
|
||||
function randomItem<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
/** 生成模拟列表数据 */
|
||||
export function mockVehicleList(params: VehicleFilterForm & { plateNo?: string; page?: number; pageSize?: number }): { list: VehicleRecord[]; total: number } {
|
||||
const total = 48;
|
||||
const list: VehicleRecord[] = [];
|
||||
const cities = ['广东省广州市', '广东省深圳市', '浙江省杭州市', '江苏省南京市'];
|
||||
for (let i = 1; i <= (params.pageSize ?? 20); i++) {
|
||||
const idx = ((params.page ?? 1) - 1) * (params.pageSize ?? 20) + i;
|
||||
if (idx > total) break;
|
||||
list.push({
|
||||
id: `v-${idx}`,
|
||||
operationCity: randomItem(cities),
|
||||
vin: `L${String(idx).padStart(6, '0')}${Math.random().toString(36).slice(2, 11).toUpperCase()}`,
|
||||
plateNo: `粤A${String(10000 + idx).slice(-5)}`,
|
||||
vehicleNo: idx <= 10 ? `VN-${idx}` : '',
|
||||
vehicleType: randomItem(vehicleTypeOptions).label,
|
||||
brand: randomItem(brandOptions).label,
|
||||
model: randomItem(modelOptions.brand_a).label,
|
||||
bodyColor: ['白', '黑', '银', '蓝'][idx % 4],
|
||||
parkingLot: idx % 3 === 0 ? '-' : `停车场${(idx % 3) + 1}`,
|
||||
customerName: idx % 4 === 0 ? '-' : `客户${(idx % 4)}`,
|
||||
businessDept: idx % 4 === 0 ? '-' : `业务${(idx % 4)}部`,
|
||||
businessOwner: idx % 4 === 0 ? '-' : `负责人${idx % 5}`,
|
||||
operationStatus: randomItem(['待运营', '库存', '租赁', '自营', '退出运营']),
|
||||
libStatus: randomItem(['库存车-可交付车', '已交付车-租赁交车', '新车入库-待验车']),
|
||||
outboundStatus: randomItem(['无', '租赁交车', '异动出库']),
|
||||
preemptStatus: '-',
|
||||
prepStatus: randomItem(['待整备', '整备中', '正常', '无']),
|
||||
transferStatus: randomItem(['无', '过户中', '内部过户完成']),
|
||||
repairStatus: randomItem(['待服务站接单', '维修中', '正常']),
|
||||
licenseStatus: randomItem(['正常', '异常']),
|
||||
scrapStatus: randomItem(['无', '报废中', '已报废']),
|
||||
registeredOwner: '某某物流有限公司',
|
||||
onlineStatus: randomItem(['在线', '离线']),
|
||||
manufactureYear: `${2020 + (idx % 5)}`,
|
||||
mileage: Number((10000 + idx * 500 + Math.random() * 200).toFixed(2)),
|
||||
purchaseTime: `2022-0${(idx % 9) + 1}-15`,
|
||||
licenseRegisterDate: `2022-0${(idx % 9) + 1}-01`,
|
||||
licenseExpiry: `2025-0${(idx % 9) + 1}-01`,
|
||||
lastDeliveryTime: idx % 2 === 0 ? `2024-01-${String(10 + (idx % 20)).padStart(2, '0')}` : '-',
|
||||
lastDeliveryMileage: idx % 2 === 0 ? Number((12000 + idx * 100).toFixed(2)) : 0,
|
||||
lastReturnTime: idx % 2 === 1 ? `2024-02-${String(5 + (idx % 20)).padStart(2, '0')}` : '-',
|
||||
lastReturnMileage: idx % 2 === 1 ? Number((12500 + idx * 100).toFixed(2)) : 0,
|
||||
forceScrapDate: `2030-12-31`,
|
||||
contractNo: idx % 3 === 0 ? '-' : `HT-2024-00${(idx % 3) + 1}`,
|
||||
currentLocation: `广东省广州市天河区某某路${idx}号`,
|
||||
gpsLastTime: `2024-02-13 ${String(10 + (idx % 12)).padStart(2, '0')}:${String(idx % 60).padStart(2, '0')}`,
|
||||
});
|
||||
}
|
||||
return { list, total };
|
||||
}
|
||||
129
src/types/vehicle.ts
Normal file
129
src/types/vehicle.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/** 车辆管理相关类型 */
|
||||
|
||||
export type OperationCity = { province: string; city: string };
|
||||
export type VehicleTypeItem = { value: string; label: string };
|
||||
export type ModelParamItem = { brand: string; model: string };
|
||||
export type CustomerItem = { value: string; label: string };
|
||||
export type DeptItem = { value: string; label: string };
|
||||
|
||||
export const OPERATION_STATUS_OPTIONS = [
|
||||
{ value: 'pending', label: '待运营' },
|
||||
{ value: 'in_stock', label: '库存' },
|
||||
{ value: 'lease', label: '租赁' },
|
||||
{ value: 'self', label: '自营' },
|
||||
{ value: 'exit', label: '退出运营' },
|
||||
] as const;
|
||||
|
||||
export const LIB_STATUS_OPTIONS = [
|
||||
{ value: 'new_pending', label: '新车入库-待验车' },
|
||||
{ value: 'new_license', label: '新车入库-证照办理' },
|
||||
{ value: 'stock_ok', label: '库存车-可交付车' },
|
||||
{ value: 'stock_no', label: '库存车-不可交付车' },
|
||||
{ value: 'stock_slow', label: '库存车-呆滞车' },
|
||||
{ value: 'out_lease', label: '已交付车-租赁交车' },
|
||||
{ value: 'out_self', label: '已交付车-自营交车' },
|
||||
{ value: 'out_replace', label: '已交付车-替换交车' },
|
||||
{ value: 'exit_scrap', label: '退出运营-报废车' },
|
||||
{ value: 'exit_third', label: '退出运营-三方退租车' },
|
||||
{ value: 'exit_sale', label: '退出运营-过户售车' },
|
||||
] as const;
|
||||
|
||||
export const OUTBOUND_STATUS_OPTIONS = [
|
||||
{ value: 'move', label: '异动出库' },
|
||||
{ value: 'transfer', label: '调拨出库' },
|
||||
{ value: 'show', label: '展示出库' },
|
||||
{ value: 'lease_deliver', label: '租赁交车' },
|
||||
{ value: 'self_deliver', label: '自营交车' },
|
||||
{ value: 'replace_deliver', label: '替换交车' },
|
||||
{ value: 'sale', label: '过户售车' },
|
||||
{ value: 'lease_return', label: '外租退车' },
|
||||
{ value: 'scrap', label: '报废出库' },
|
||||
{ value: 'none', label: '无' },
|
||||
] as const;
|
||||
|
||||
export const PREP_STATUS_OPTIONS = [
|
||||
{ value: 'pending', label: '待整备' },
|
||||
{ value: 'doing', label: '整备中' },
|
||||
{ value: 'normal', label: '正常' },
|
||||
{ value: 'none', label: '无' },
|
||||
] as const;
|
||||
|
||||
export const TRANSFER_STATUS_OPTIONS = [
|
||||
{ value: 'doing', label: '过户中' },
|
||||
{ value: 'internal_done', label: '内部过户完成' },
|
||||
{ value: 'sale_done', label: '销售过户完成' },
|
||||
{ value: 'none', label: '无' },
|
||||
] as const;
|
||||
|
||||
export const REPAIR_STATUS_OPTIONS = [
|
||||
{ value: 'pending', label: '待服务站接单' },
|
||||
{ value: 'doing', label: '维修中' },
|
||||
{ value: 'normal', label: '正常' },
|
||||
] as const;
|
||||
|
||||
export const LICENSE_STATUS_OPTIONS = [
|
||||
{ value: 'normal', label: '正常' },
|
||||
{ value: 'abnormal', label: '异常' },
|
||||
] as const;
|
||||
|
||||
export const SCRAP_STATUS_OPTIONS = [
|
||||
{ value: 'doing', label: '报废中' },
|
||||
{ value: 'done', label: '已报废' },
|
||||
{ value: 'none', label: '无' },
|
||||
] as const;
|
||||
|
||||
export const ONLINE_STATUS_OPTIONS = [
|
||||
{ value: 'online', label: '在线' },
|
||||
{ value: 'offline', label: '离线' },
|
||||
] as const;
|
||||
|
||||
export interface VehicleRecord {
|
||||
id: string;
|
||||
operationCity: string;
|
||||
vin: string;
|
||||
plateNo: string;
|
||||
vehicleNo: string;
|
||||
vehicleType: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
bodyColor: string;
|
||||
parkingLot: string;
|
||||
customerName: string;
|
||||
businessDept: string;
|
||||
businessOwner: string;
|
||||
operationStatus: string;
|
||||
libStatus: string;
|
||||
outboundStatus: string;
|
||||
preemptStatus: string;
|
||||
prepStatus: string;
|
||||
transferStatus: string;
|
||||
repairStatus: string;
|
||||
licenseStatus: string;
|
||||
scrapStatus: string;
|
||||
registeredOwner: string;
|
||||
onlineStatus: string;
|
||||
manufactureYear: string;
|
||||
mileage: number;
|
||||
purchaseTime: string;
|
||||
licenseRegisterDate: string;
|
||||
licenseExpiry: string;
|
||||
lastDeliveryTime: string;
|
||||
lastDeliveryMileage: number;
|
||||
lastReturnTime: string;
|
||||
lastReturnMileage: number;
|
||||
forceScrapDate: string;
|
||||
contractNo: string;
|
||||
currentLocation: string;
|
||||
gpsLastTime: string;
|
||||
}
|
||||
|
||||
export interface VehicleFilterForm {
|
||||
operationCity?: string[];
|
||||
vehicleType?: string;
|
||||
brand?: string;
|
||||
model?: string;
|
||||
customerName?: string;
|
||||
businessDept?: string;
|
||||
contractNo?: string;
|
||||
registeredOwner?: string;
|
||||
}
|
||||
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"baseUrl": ".",
|
||||
"paths": { "@/*": ["src/*"] }
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
11
tsconfig.node.json
Normal file
11
tsconfig.node.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
10
vite.config.ts
Normal file
10
vite.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: { '@': path.resolve(__dirname, 'src') },
|
||||
},
|
||||
});
|
||||
BIN
web端/.DS_Store
vendored
Normal file
BIN
web端/.DS_Store
vendored
Normal file
Binary file not shown.
577
web端/业务管理/交车任务.jsx
Normal file
577
web端/业务管理/交车任务.jsx
Normal file
@@ -0,0 +1,577 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 数字化资产ONEOS运管平台 - 业务管理 - 交车任务
|
||||
|
||||
const Component = function() {
|
||||
var useState = React.useState;
|
||||
var useMemo = React.useMemo;
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var Table = antd.Table;
|
||||
var Card = antd.Card;
|
||||
var Tabs = antd.Tabs;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Popover = antd.Popover;
|
||||
var Modal = antd.Modal;
|
||||
var message = antd.message;
|
||||
var App = antd.App;
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
|
||||
// 筛选条件(表单输入)
|
||||
var _contractCode = useState(undefined);
|
||||
var _projectName = useState(undefined);
|
||||
var _customerName = useState(undefined);
|
||||
var _planDateRange = useState(null);
|
||||
var _creator = useState(undefined);
|
||||
// 已应用的筛选条件(点击查询后生效)
|
||||
var _applied = useState({
|
||||
contractCode: undefined,
|
||||
projectName: undefined,
|
||||
customerName: undefined,
|
||||
planDateRange: null,
|
||||
creator: undefined
|
||||
});
|
||||
|
||||
// Tab:进行中 / 已完成
|
||||
var _activeTab = useState('ongoing');
|
||||
|
||||
// 筛选展开(默认显示前 3 项,与车辆租赁合同一致)
|
||||
var _filterExpanded = useState(false);
|
||||
|
||||
// 需求说明弹窗
|
||||
var _reqSpecOpen = useState(false);
|
||||
|
||||
// 停用/启用:重新选择预计交车日期弹窗
|
||||
var _rescheduleModalVisible = useState(false);
|
||||
var _rescheduleTask = useState(null);
|
||||
var _rescheduleDateRange = useState(null);
|
||||
|
||||
// 模拟选项(可模糊搜索)
|
||||
var contractOptions = [
|
||||
{ value: 'JXZL20260216YW101235A', label: 'JXZL20260216YW101235A' },
|
||||
{ value: 'JXZL20260216YW101236A', label: 'JXZL20260216YW101236A' },
|
||||
{ value: 'JXZL20260215YW101234A', label: 'JXZL20260215YW101234A' },
|
||||
{ value: 'SHZL20260210YW101200A', label: 'SHZL20260210YW101200A' }
|
||||
];
|
||||
var projectOptions = [
|
||||
{ value: 'p1', label: '嘉兴氢能运输项目' },
|
||||
{ value: 'p2', label: '上海物流租赁项目' },
|
||||
{ value: 'p3', label: '杭州城配租赁项目' }
|
||||
];
|
||||
var customerOptions = [
|
||||
{ value: 'c1', label: '嘉兴某某物流有限公司' },
|
||||
{ value: 'c2', label: '上海某某运输公司' },
|
||||
{ value: 'c3', label: '杭州某某租赁有限公司' }
|
||||
];
|
||||
var creatorOptions = [
|
||||
{ value: 'u1', label: '张经理' },
|
||||
{ value: 'u2', label: '李专员' },
|
||||
{ value: 'u3', label: '王专员' }
|
||||
];
|
||||
|
||||
// 进行中列表数据(未完成交车单提交),用 state 以便停用/启用后刷新
|
||||
var _ongoingList = useState([
|
||||
{ id: 'o1', contractCode: 'JXZL20260216YW101235A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', planDeliveryDisplay: '2026-03-01至2026-03-05', planDeliveryEnd: '2026-03-05', billingStartDate: '2026-03-01', creator: '张经理', createdAt: '2026-02-20', lastUpdater: '张经理', lastUpdatedAt: '2026-02-24', enabled: true },
|
||||
{ id: 'o2', contractCode: 'JXZL20260216YW101236A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市浦东新区张江高科技园区', planDeliveryDisplay: '2026-03-10', planDeliveryEnd: '2026-03-10', billingStartDate: '-', creator: '李专员', createdAt: '2026-02-21', lastUpdater: '李专员', lastUpdatedAt: '2026-02-22', enabled: false },
|
||||
{ id: 'o3', contractCode: 'JXZL20260215YW101234A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市余杭区未来科技城', planDeliveryDisplay: '2026-02-28至2026-03-02', planDeliveryEnd: '2026-03-02', billingStartDate: '2026-02-28', creator: '王专员', createdAt: '2026-02-18', lastUpdater: '王专员', lastUpdatedAt: '2026-02-25', enabled: true },
|
||||
{ id: 'o4', contractCode: 'JXZL20260101YW101200A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市秀洲区洪兴西路288号', planDeliveryDisplay: '2026-01-05至2026-01-10', planDeliveryEnd: '2026-01-10', billingStartDate: '-', creator: '张经理', createdAt: '2026-01-02', lastUpdater: '张经理', lastUpdatedAt: '2026-01-15', enabled: false },
|
||||
{ id: 'o5', contractCode: 'JXZL20260218YW101237A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市南湖区广益路与亚中路交叉口', planDeliveryDisplay: '2026-03-08至2026-03-12', planDeliveryEnd: '2026-03-12', billingStartDate: '-', creator: '张经理', createdAt: '2026-02-22', lastUpdater: '张经理', lastUpdatedAt: '2026-02-26', enabled: true },
|
||||
{ id: 'o6', contractCode: 'SHZL20260210YW101201A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市闵行区申滨路1051号', planDeliveryDisplay: '2026-03-15', planDeliveryEnd: '2026-03-15', billingStartDate: '-', creator: '李专员', createdAt: '2026-02-19', lastUpdater: '李专员', lastUpdatedAt: '2026-02-23', enabled: true },
|
||||
{ id: 'o7', contractCode: 'JXZL20260220YW101238A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 4, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市萧山区市心北路与建设一路交叉口', planDeliveryDisplay: '2026-03-20至2026-03-25', planDeliveryEnd: '2026-03-25', billingStartDate: '-', creator: '王专员', createdAt: '2026-02-23', lastUpdater: '王专员', lastUpdatedAt: '2026-02-27', enabled: true },
|
||||
{ id: 'o8', contractCode: 'JXZL20260214YW101233A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市经开区昌盛路与文昌路交叉口', planDeliveryDisplay: '2026-03-05', planDeliveryEnd: '2026-03-05', billingStartDate: '-', creator: '张经理', createdAt: '2026-02-17', lastUpdater: '李专员', lastUpdatedAt: '2026-02-24', enabled: true },
|
||||
{ id: 'o9', contractCode: 'SHZL20260212YW101202A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市嘉定区安亭镇墨玉南路与博园路交叉口', planDeliveryDisplay: '2026-03-18至2026-03-22', planDeliveryEnd: '2026-03-22', billingStartDate: '-', creator: '李专员', createdAt: '2026-02-24', lastUpdater: '李专员', lastUpdatedAt: '2026-02-28', enabled: true },
|
||||
{ id: 'o10', contractCode: 'JXZL20260222YW101239A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市西湖区文三路与古翠路交叉口', planDeliveryDisplay: '2026-03-25至2026-03-30', planDeliveryEnd: '2026-03-30', billingStartDate: '-', creator: '王专员', createdAt: '2026-02-25', lastUpdater: '王专员', lastUpdatedAt: '2026-02-28', enabled: true }
|
||||
]);
|
||||
var ongoingList = _ongoingList[0];
|
||||
var setOngoingList = _ongoingList[1];
|
||||
|
||||
// 已完成列表数据(已提交交车单),交车数量可点开气泡
|
||||
var completedList = [
|
||||
{ id: 'c1', contractCode: 'JXZL20260215YW101234A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市余杭区未来科技城', vehicles: [{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A10001', actualDeliveryDate: '2026-02-28', deliverer: '运维李' }, { vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '浙B20002', actualDeliveryDate: '2026-03-01', deliverer: '运维王' }], planDeliveryDisplay: '2026-02-28至2026-03-01', billingStartDate: '2026-02-28', creator: '王专员', createdAt: '2026-02-18', lastUpdater: '王专员', lastUpdatedAt: '2026-03-01' },
|
||||
{ id: 'c2', contractCode: 'SHZL20260210YW101200A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '沪A30003', actualDeliveryDate: '2026-02-25', deliverer: '运维赵' }], planDeliveryDisplay: '2026-02-25', billingStartDate: '2026-02-25', creator: '李专员', createdAt: '2026-02-15', lastUpdater: '李专员', lastUpdatedAt: '2026-02-25' },
|
||||
{ id: 'c3', contractCode: 'JXZL20260208YW101231A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', vehicles: [{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A10002', actualDeliveryDate: '2026-02-20', deliverer: '运维李' }, { vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '浙A10003', actualDeliveryDate: '2026-02-21', deliverer: '运维王' }], planDeliveryDisplay: '2026-02-20至2026-02-21', billingStartDate: '2026-02-20', creator: '张经理', createdAt: '2026-02-10', lastUpdater: '张经理', lastUpdatedAt: '2026-02-21' },
|
||||
{ id: 'c4', contractCode: 'SHZL20260205YW101198A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市闵行区申滨路1051号', vehicles: [{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B2', plateNo: '沪B40004', actualDeliveryDate: '2026-02-18', deliverer: '运维赵' }], planDeliveryDisplay: '2026-02-18', billingStartDate: '2026-02-18', creator: '李专员', createdAt: '2026-02-08', lastUpdater: '李专员', lastUpdatedAt: '2026-02-18' },
|
||||
{ id: 'c5', contractCode: 'JXZL20260212YW101232A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市萧山区市心北路与建设一路交叉口', vehicles: [{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '浙C50001', actualDeliveryDate: '2026-02-22', deliverer: '运维李' }, { vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '浙C50002', actualDeliveryDate: '2026-02-23', deliverer: '运维王' }, { vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌C', model: '型号C1', plateNo: '浙C50003', actualDeliveryDate: '2026-02-24', deliverer: '运维赵' }], planDeliveryDisplay: '2026-02-22至2026-02-24', billingStartDate: '2026-02-22', creator: '王专员', createdAt: '2026-02-14', lastUpdater: '王专员', lastUpdatedAt: '2026-02-24' },
|
||||
{ id: 'c6', contractCode: 'JXZL20260201YW101228A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市秀洲区洪兴西路288号', vehicles: [{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '浙A10004', actualDeliveryDate: '2026-02-15', deliverer: '运维李' }], planDeliveryDisplay: '2026-02-15', billingStartDate: '2026-02-15', creator: '张经理', createdAt: '2026-02-05', lastUpdater: '张经理', lastUpdatedAt: '2026-02-15' },
|
||||
{ id: 'c7', contractCode: 'SHZL20260203YW101199A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市嘉定区安亭镇墨玉南路与博园路交叉口', vehicles: [{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '沪A30005', actualDeliveryDate: '2026-02-16', deliverer: '运维赵' }, { vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '沪A30006', actualDeliveryDate: '2026-02-17', deliverer: '运维李' }], planDeliveryDisplay: '2026-02-16至2026-02-17', billingStartDate: '2026-02-16', creator: '李专员', createdAt: '2026-02-06', lastUpdater: '李专员', lastUpdatedAt: '2026-02-17' },
|
||||
{ id: 'c8', contractCode: 'JXZL20260128YW101227A', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, deliveryRegion: '浙江省-杭州市', deliveryLocation: '杭州市西湖区文三路与古翠路交叉口', vehicles: [{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B2', plateNo: '浙B20004', actualDeliveryDate: '2026-02-10', deliverer: '运维王' }, { vehicleType: '4.5吨货车-轻型厢式货车', brand: '品牌C', model: '型号C1', plateNo: '浙B20005', actualDeliveryDate: '2026-02-11', deliverer: '运维赵' }], planDeliveryDisplay: '2026-02-10至2026-02-11', billingStartDate: '2026-02-10', creator: '王专员', createdAt: '2026-01-30', lastUpdater: '王专员', lastUpdatedAt: '2026-02-11' },
|
||||
{ id: 'c9', contractCode: 'JXZL20260206YW101230A', projectName: '嘉兴氢能运输项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, deliveryRegion: '浙江省-嘉兴市', deliveryLocation: '嘉兴市经开区昌盛路与文昌路交叉口', vehicles: [{ vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A2', plateNo: '浙A10005', actualDeliveryDate: '2026-02-19', deliverer: '运维李' }], planDeliveryDisplay: '2026-02-19', billingStartDate: '2026-02-19', creator: '张经理', createdAt: '2026-02-09', lastUpdater: '张经理', lastUpdatedAt: '2026-02-19' },
|
||||
{ id: 'c10', contractCode: 'SHZL20260215YW101203A', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, deliveryRegion: '上海市-上海市', deliveryLocation: '上海市浦东新区金桥镇金海路1000号', vehicles: [{ vehicleType: '18吨厢式货车-重型厢式货车', brand: '品牌B', model: '型号B1', plateNo: '沪C60001', actualDeliveryDate: '2026-02-26', deliverer: '运维赵' }, { vehicleType: '4.5吨冷链车-轻型厢式货车', brand: '品牌A', model: '型号A1', plateNo: '沪C60002', actualDeliveryDate: '2026-02-27', deliverer: '运维李' }], planDeliveryDisplay: '2026-02-26至2026-02-27', billingStartDate: '2026-02-26', creator: '李专员', createdAt: '2026-02-18', lastUpdater: '李专员', lastUpdatedAt: '2026-02-27' }
|
||||
];
|
||||
|
||||
var applied = _applied[0];
|
||||
var setApplied = _applied[1];
|
||||
|
||||
var getProjectLabel = function(v) { var o = projectOptions.find(function(x) { return x.value === v; }); return o ? o.label : v; };
|
||||
var getCustomerLabel = function(v) { var o = customerOptions.find(function(x) { return x.value === v; }); return o ? o.label : v; };
|
||||
var getCreatorLabel = function(v) { var o = creatorOptions.find(function(x) { return x.value === v; }); return o ? o.label : v; };
|
||||
|
||||
var filterOngoing = function(list) {
|
||||
var a = applied;
|
||||
if (a.contractCode) list = list.filter(function(r) { return r.contractCode === a.contractCode; });
|
||||
if (a.projectName) { var pl = getProjectLabel(a.projectName); if (pl) list = list.filter(function(r) { return r.projectName === pl; }); }
|
||||
if (a.customerName) { var cl = getCustomerLabel(a.customerName); if (cl) list = list.filter(function(r) { return r.customerName === cl; }); }
|
||||
if (a.creator) { var ul = getCreatorLabel(a.creator); if (ul) list = list.filter(function(r) { return r.creator === ul; }); }
|
||||
if (a.planDateRange && a.planDateRange.length === 2 && window.dayjs) {
|
||||
var start = window.dayjs(a.planDateRange[0]).startOf('day');
|
||||
var end = window.dayjs(a.planDateRange[1]).endOf('day');
|
||||
list = list.filter(function(r) {
|
||||
var d = r.planDeliveryEnd ? window.dayjs(r.planDeliveryEnd) : null;
|
||||
return d && !d.isBefore(start) && !d.isAfter(end);
|
||||
});
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
var filteredOngoing = useMemo(function() {
|
||||
return filterOngoing(ongoingList.slice());
|
||||
}, [ongoingList, applied.contractCode, applied.projectName, applied.customerName, applied.creator, applied.planDateRange]);
|
||||
|
||||
var filteredCompleted = useMemo(function() {
|
||||
var list = completedList.slice();
|
||||
if (applied.contractCode) list = list.filter(function(r) { return r.contractCode === applied.contractCode; });
|
||||
if (applied.projectName) { var pl = getProjectLabel(applied.projectName); if (pl) list = list.filter(function(r) { return r.projectName === pl; }); }
|
||||
if (applied.customerName) { var cl = getCustomerLabel(applied.customerName); if (cl) list = list.filter(function(r) { return r.customerName === cl; }); }
|
||||
if (applied.creator) { var ul = getCreatorLabel(applied.creator); if (ul) list = list.filter(function(r) { return r.creator === ul; }); }
|
||||
if (applied.planDateRange && applied.planDateRange.length === 2 && window.dayjs) {
|
||||
var start = window.dayjs(applied.planDateRange[0]).startOf('day');
|
||||
var end = window.dayjs(applied.planDateRange[1]).endOf('day');
|
||||
list = list.filter(function(r) {
|
||||
var planEnd = r.planDeliveryDisplay && r.planDeliveryDisplay.indexOf('至') >= 0
|
||||
? r.planDeliveryDisplay.split('至')[1]
|
||||
: r.planDeliveryDisplay;
|
||||
var d = planEnd ? window.dayjs(planEnd) : null;
|
||||
return d && !d.isBefore(start) && !d.isAfter(end);
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, [applied.contractCode, applied.projectName, applied.customerName, applied.creator, applied.planDateRange]);
|
||||
|
||||
var handleQuery = function() {
|
||||
setApplied({
|
||||
contractCode: _contractCode[0],
|
||||
projectName: _projectName[0],
|
||||
customerName: _customerName[0],
|
||||
planDateRange: _planDateRange[0] && _planDateRange[0].length === 2 ? _planDateRange[0] : null,
|
||||
creator: _creator[0]
|
||||
});
|
||||
message.info('已按筛选条件查询');
|
||||
};
|
||||
|
||||
var handleReset = function() {
|
||||
_contractCode[1](undefined);
|
||||
_projectName[1](undefined);
|
||||
_customerName[1](undefined);
|
||||
_planDateRange[1](null);
|
||||
_creator[1](undefined);
|
||||
setApplied({
|
||||
contractCode: undefined,
|
||||
projectName: undefined,
|
||||
customerName: undefined,
|
||||
planDateRange: null,
|
||||
creator: undefined
|
||||
});
|
||||
};
|
||||
|
||||
var handleDisableEnable = function(record) {
|
||||
if (record.enabled) {
|
||||
Modal.confirm({
|
||||
title: '是否确认停用该交车任务?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk: function() {
|
||||
setOngoingList(function(prev) {
|
||||
return prev.map(function(r) { return r.id === record.id ? Object.assign({}, r, { enabled: false }) : r; });
|
||||
});
|
||||
message.success('已停用');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 启用:判断预计交车结束日期是否已过期
|
||||
var dayjs = window.dayjs;
|
||||
var endDate = record.planDeliveryEnd ? (dayjs ? dayjs(record.planDeliveryEnd) : null) : null;
|
||||
var now = dayjs ? dayjs() : null;
|
||||
if (endDate && now && endDate.isBefore(now, 'day')) {
|
||||
_rescheduleTask[1](record);
|
||||
_rescheduleDateRange[1](null);
|
||||
_rescheduleModalVisible[1](true);
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '是否确认启用该交车任务?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk: function() {
|
||||
setOngoingList(function(prev) {
|
||||
return prev.map(function(r) { return r.id === record.id ? Object.assign({}, r, { enabled: true }) : r; });
|
||||
});
|
||||
message.success('已启用');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var handleRescheduleConfirm = function() {
|
||||
var task = _rescheduleTask[0];
|
||||
var range = _rescheduleDateRange[0];
|
||||
if (!task || !range || range.length !== 2) {
|
||||
message.warning('请选择预计交车日期(单日请选择同一天为开始和结束)');
|
||||
return;
|
||||
}
|
||||
var dayjs = window.dayjs;
|
||||
var startStr = dayjs ? dayjs(range[0]).format('YYYY-MM-DD') : String(range[0]);
|
||||
var endStr = dayjs ? dayjs(range[1]).format('YYYY-MM-DD') : String(range[1]);
|
||||
var planDisplay = startStr === endStr ? startStr : startStr + '至' + endStr;
|
||||
setOngoingList(function(prev) {
|
||||
return prev.map(function(r) {
|
||||
if (r.id !== task.id) return r;
|
||||
return Object.assign({}, r, { planDeliveryDisplay: planDisplay, planDeliveryEnd: endStr, enabled: true });
|
||||
});
|
||||
});
|
||||
message.success('已重新选择预计交车日期并启用');
|
||||
_rescheduleModalVisible[1](false);
|
||||
_rescheduleTask[1](null);
|
||||
_rescheduleDateRange[1](null);
|
||||
};
|
||||
|
||||
// 进行中表格列
|
||||
var ongoingColumns = [
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 180, ellipsis: true, fixed: 'left' },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true, fixed: 'left' },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true, fixed: 'left' },
|
||||
{ title: '交车数量', dataIndex: 'deliveryCount', key: 'deliveryCount', width: 100 },
|
||||
{ title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 140 },
|
||||
{ title: '交车地点', dataIndex: 'deliveryLocation', key: 'deliveryLocation', width: 200, ellipsis: true },
|
||||
{ title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 180 },
|
||||
{ title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120 },
|
||||
{ title: '创建人', dataIndex: 'creator', key: 'creator', width: 100 },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 120 },
|
||||
{ title: '最后更新人', dataIndex: 'lastUpdater', key: 'lastUpdater', width: 100 },
|
||||
{ title: '最后更新时间', dataIndex: 'lastUpdatedAt', key: 'lastUpdatedAt', width: 120 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
render: function(_, record) {
|
||||
return React.createElement('span', null,
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务页面'); } }, '查看'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务-编辑交车任务页面'); } }, '编辑'),
|
||||
React.createElement(Button, {
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
onClick: function() { handleDisableEnable(record); }
|
||||
}, record.enabled ? '停用' : '启用')
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 已完成表格列(交车数量重点色+气泡)
|
||||
var vehiclePopoverContent = function(vehicles) {
|
||||
if (!vehicles || !vehicles.length) return React.createElement('span', null, '-');
|
||||
var headers = ['车辆类型', '品牌', '型号', '车牌号', '实际交车日期', '交车人'];
|
||||
return React.createElement('div', { style: { padding: 4, minWidth: 420 } },
|
||||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
headers.map(function(h) { return React.createElement('th', { key: h, style: { padding: '6px 8px', textAlign: 'left', borderBottom: '1px solid #eee', fontWeight: 600 } }, h); })
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
vehicles.map(function(v, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.vehicleType || '-'),
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.brand || '-'),
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.model || '-'),
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.plateNo || '-'),
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.actualDeliveryDate || '-'),
|
||||
React.createElement('td', { style: { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, v.deliverer || '-')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
var completedColumns = [
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 180, ellipsis: true, fixed: 'left' },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true, fixed: 'left' },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true, fixed: 'left' },
|
||||
{
|
||||
title: '交车数量',
|
||||
dataIndex: 'deliveryCount',
|
||||
key: 'deliveryCount',
|
||||
width: 100,
|
||||
render: function(count, record) {
|
||||
var content = vehiclePopoverContent(record.vehicles);
|
||||
return React.createElement(Popover, { content: content, title: '交车车辆明细', trigger: 'click' },
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 500 } }, count)
|
||||
);
|
||||
}
|
||||
},
|
||||
{ title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 140 },
|
||||
{ title: '交车地点', dataIndex: 'deliveryLocation', key: 'deliveryLocation', width: 200, ellipsis: true },
|
||||
{ title: '预计交车日期', dataIndex: 'planDeliveryDisplay', key: 'planDeliveryDisplay', width: 180 },
|
||||
{ title: '开始计费日期', dataIndex: 'billingStartDate', key: 'billingStartDate', width: 120 },
|
||||
{ title: '创建人', dataIndex: 'creator', key: 'creator', width: 100 },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 120 },
|
||||
{ title: '最后更新人', dataIndex: 'lastUpdater', key: 'lastUpdater', width: 100 },
|
||||
{ title: '最后更新时间', dataIndex: 'lastUpdatedAt', key: 'lastUpdatedAt', width: 120 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 80,
|
||||
fixed: 'right',
|
||||
render: function(_, record) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { message.info('请参照原型业务管理-交车任务页面'); } }, '查看');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var filterOption = function(input, option) {
|
||||
var label = (option && option.label) ? String(option.label) : '';
|
||||
return label.toLowerCase().indexOf((input || '').toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
|
||||
var filterItemStyle = { marginBottom: 12 };
|
||||
var filterControlStyle = { width: '100%' };
|
||||
|
||||
var filterItems = [
|
||||
React.createElement('div', { key: 'contractCode', style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '合同编码'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择合同编码',
|
||||
style: filterControlStyle,
|
||||
value: _contractCode[0],
|
||||
onChange: function(v) { _contractCode[1](v); },
|
||||
options: contractOptions,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: filterOption
|
||||
})),
|
||||
React.createElement('div', { key: 'projectName', style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '项目名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择项目名称',
|
||||
style: filterControlStyle,
|
||||
value: _projectName[0],
|
||||
onChange: function(v) { _projectName[1](v); },
|
||||
options: projectOptions,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: filterOption
|
||||
})),
|
||||
React.createElement('div', { key: 'customerName', style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '客户名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择客户名称',
|
||||
style: filterControlStyle,
|
||||
value: _customerName[0],
|
||||
onChange: function(v) { _customerName[1](v); },
|
||||
options: customerOptions,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: filterOption
|
||||
})),
|
||||
React.createElement('div', { key: 'planDate', style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '预计交车日期'),
|
||||
React.createElement(RangePicker, {
|
||||
style: filterControlStyle,
|
||||
placeholder: ['请选择预计交车开始时间', '请选择预计交车结束时间'],
|
||||
value: _planDateRange[0] && _planDateRange[0].length === 2 && window.dayjs ? [_planDateRange[0][0], _planDateRange[0][1]] : null,
|
||||
onChange: function(dates) { _planDateRange[1](dates && dates.length === 2 ? dates : null); }
|
||||
})),
|
||||
React.createElement('div', { key: 'creator', style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '创建人'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择创建人',
|
||||
style: filterControlStyle,
|
||||
value: _creator[0],
|
||||
onChange: function(v) { _creator[1](v); },
|
||||
options: creatorOptions,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: filterOption
|
||||
}))
|
||||
];
|
||||
|
||||
var filterCount = _filterExpanded[0] ? 5 : 3;
|
||||
var filterNodes = [];
|
||||
for (var i = 0; i < filterCount && i < filterItems.length; i++) {
|
||||
filterNodes.push(filterItems[i]);
|
||||
}
|
||||
|
||||
// 表格单元格一行显示、根据内容适应宽度
|
||||
var cellNoWrap = function() { return { style: { whiteSpace: 'nowrap' } }; };
|
||||
var ongoingColumnsWithNowrap = ongoingColumns.map(function(col) { return Object.assign({}, col, { onCell: cellNoWrap }); });
|
||||
var completedColumnsWithNowrap = completedColumns.map(function(col) { return Object.assign({}, col, { onCell: cellNoWrap }); });
|
||||
|
||||
var layoutStyle = { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh' };
|
||||
|
||||
var reqTitleStyle = { fontSize: 18, fontWeight: 600, marginBottom: 16, color: 'rgba(0,0,0,0.85)' };
|
||||
var reqSectionStyle = { fontSize: 15, fontWeight: 600, marginTop: 16, marginBottom: 8, color: 'rgba(0,0,0,0.85)' };
|
||||
var reqSubStyle = { fontSize: 14, fontWeight: 500, marginLeft: 16, marginTop: 8, marginBottom: 4, color: 'rgba(0,0,0,0.85)' };
|
||||
var reqItemStyle = { fontSize: 13, marginLeft: 32, marginTop: 4, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.75)' };
|
||||
var reqSubItemStyle = { fontSize: 13, marginLeft: 48, marginTop: 2, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.7)' };
|
||||
var reqBlockStyle = { marginBottom: 8 };
|
||||
|
||||
var reqSpecContent = React.createElement('div', { style: { padding: '0 4px' } },
|
||||
React.createElement('div', { style: reqTitleStyle }, '交车任务'),
|
||||
React.createElement('div', { style: reqBlockStyle },
|
||||
React.createElement('div', { style: reqSectionStyle }, '1.面包屑:'),
|
||||
React.createElement('div', { style: reqSubStyle }, '1.1.业务管理-交车任务')
|
||||
),
|
||||
React.createElement('div', { style: reqBlockStyle },
|
||||
React.createElement('div', { style: reqSectionStyle }, '2.筛选:'),
|
||||
React.createElement('div', { style: reqItemStyle }, '支持通过合同编码、项目名称、客户名称、预计交车日期、创建人进行筛选,右侧为查询、重置按钮;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.4.预计交车日期:日期选择器,默认提示信息为:请选择预计交车开始时间 请选择预计交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.5.创建人:选择器,默认为所有创建人;提示信息为:请输入或选择创建人,支持从输入框输入内容进行模糊搜索,下拉显示结果;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.6.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '2.7.重置:点击清空查询条件至默认')
|
||||
),
|
||||
React.createElement('div', { style: reqBlockStyle },
|
||||
React.createElement('div', { style: reqSectionStyle }, '3.列表:分为2个tab:进行中、已完成,右侧为新增;'),
|
||||
React.createElement('div', { style: reqSubStyle }, '3.1进行中:显示所有已新增成功,但未完成交车单提交的任务;列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.1合同编码:显示对应合同编码;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.2.项目名称:显示对应项目名称;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.3.客户名称:显示对应客户名称;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.4.交车数量:显示交车数;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.5.交车区域:显示交车省-市;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.6.交车地点:显示交车详细地址;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.7.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.8.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.9.创建人:显示交车任务的创建人用户姓名;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.10.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.11.最后更新人:显示交车任务的最后一次更新人用户姓名;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.12.最后更新时间:显示交车任务的最后一次更新时间,格式为YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.1.13.操作:支持查看、编辑、停用/启用;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.1.13.1.查看:跳转查看交车任务;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.1.13.2.编辑:跳转编辑交车任务;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.1.13.3.停用/启用:任务创建后为启用状态,此处为停用,点击停用后,变为启用。停用状态的交车任务不会推送给对应区域运维人员,重新启用时判断预计交车结束日期是否已经过期,如已过期则启用时需要在弹出卡片中重新选择预计交车日期;'),
|
||||
React.createElement('div', { style: reqSubStyle }, '3.2.已完成:显示所有已新增成功并完成交车单提交的任务,列表展示以下内容:合同编码、项目名称、客户名称、交车数量、预计交车日期、开始计费日期、创建人、创建时间、最后更新人、最后更新时间、操作;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.1.合同编码:显示对应合同编码;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.2.项目名称:显示对应项目名称;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.3.客户名称:显示对应客户名称;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.4.交车数量:显示交车数,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、实际交车日期、交车人;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.1.车辆类型:显示车辆类型;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.2.品牌:显示车辆品牌;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.3.型号:显示车辆型号;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.5.实际交车日期:显示实际交车日期,与列表中显示相同,格式为YYYY-MM-DD,如该车还未交车则显示为-;'),
|
||||
React.createElement('div', { style: reqSubItemStyle }, '3.2.4.6.交车人:显示实际交车人用户姓名,如该车还未还车则显示为-;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.5.预计交车日期:显示预计交车日期,支持某天或某个时间段,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.6.开始计费日期:显示开始计费日期,格式为YYYY-MM-DD,在交车单完成电子签章后开始生效;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.7.创建人:显示交车任务的创建人用户姓名;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.8.创建时间:显示交车任务的创建时间,格式为YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.9.最后更新人:显示交车任务的最后一次更新人用户姓名;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.10.最后更新时间:显示交车任务的最后一次更新时间,格式为YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqItemStyle }, '3.2.11.操作:支持查看;')
|
||||
),
|
||||
React.createElement('div', { style: reqBlockStyle },
|
||||
React.createElement('div', { style: reqSectionStyle }, '4.列表右下方为分页功能,支持单页显示条目选择;')
|
||||
)
|
||||
);
|
||||
|
||||
return React.createElement(App, null,
|
||||
React.createElement('div', { style: layoutStyle },
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
|
||||
React.createElement(Breadcrumb, {
|
||||
items: [
|
||||
{ title: '业务管理' },
|
||||
{ title: '交车任务' }
|
||||
]
|
||||
}),
|
||||
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { _reqSpecOpen[1](true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement(Card, { style: { marginBottom: 16 } },
|
||||
React.createElement('div', {
|
||||
style: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr 1fr',
|
||||
gap: '16px 24px',
|
||||
alignItems: 'start',
|
||||
flex: 1,
|
||||
minWidth: 0
|
||||
}
|
||||
}, filterNodes),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } },
|
||||
React.createElement(Button, { onClick: handleReset }, '重置'),
|
||||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function() { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开')
|
||||
)
|
||||
),
|
||||
React.createElement(Card, null,
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
|
||||
React.createElement(Tabs, {
|
||||
activeKey: _activeTab[0],
|
||||
onChange: function(k) { _activeTab[1](k); },
|
||||
items: [
|
||||
{ key: 'ongoing', label: '进行中' },
|
||||
{ key: 'completed', label: '已完成' }
|
||||
]
|
||||
}),
|
||||
React.createElement(Button, { type: 'primary', onClick: function() { message.info('请参照原型业务管理-交车任务-新增交车任务页面'); } }, '新增')
|
||||
),
|
||||
_activeTab[0] === 'ongoing'
|
||||
? React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
columns: ongoingColumnsWithNowrap,
|
||||
dataSource: filteredOngoing,
|
||||
scroll: { x: 1860 },
|
||||
size: 'small',
|
||||
pagination: {
|
||||
showSizeChanger: true,
|
||||
showTotal: function(t) { return '共 ' + t + ' 条'; },
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}
|
||||
})
|
||||
: React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
columns: completedColumnsWithNowrap,
|
||||
dataSource: filteredCompleted,
|
||||
scroll: { x: 1860 },
|
||||
size: 'small',
|
||||
pagination: {
|
||||
showSizeChanger: true,
|
||||
showTotal: function(t) { return '共 ' + t + ' 条'; },
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: _reqSpecOpen[0],
|
||||
onCancel: function() { _reqSpecOpen[1](false); },
|
||||
width: 720,
|
||||
footer: React.createElement(Button, { onClick: function() { _reqSpecOpen[1](false); } }, '关闭'),
|
||||
bodyStyle: { maxHeight: '70vh', overflow: 'auto' }
|
||||
}, reqSpecContent),
|
||||
React.createElement(Modal, {
|
||||
title: '预计交车日期已过期,请重新选择预计交车日期',
|
||||
open: _rescheduleModalVisible[0],
|
||||
onCancel: function() {
|
||||
_rescheduleModalVisible[1](false);
|
||||
_rescheduleTask[1](null);
|
||||
_rescheduleDateRange[1](null);
|
||||
},
|
||||
onOk: handleRescheduleConfirm,
|
||||
okText: '确定',
|
||||
cancelText: '取消'
|
||||
}, React.createElement('div', { style: { padding: '8px 0' } },
|
||||
React.createElement('div', { style: { marginBottom: 8, color: '#666', fontSize: 13 } }, '支持选择单日或开始-结束时间段,单日时开始与结束选同一天即可。'),
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '预计交车日期:'),
|
||||
React.createElement(RangePicker, {
|
||||
placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'],
|
||||
value: _rescheduleDateRange[0] && _rescheduleDateRange[0].length === 2 && window.dayjs ? [_rescheduleDateRange[0][0], _rescheduleDateRange[0][1]] : null,
|
||||
onChange: function(dates) { _rescheduleDateRange[1](dates && dates.length === 2 ? dates : null); }
|
||||
})
|
||||
))
|
||||
)
|
||||
);
|
||||
};
|
||||
300
web端/业务管理/新增交车任务.jsx
Normal file
300
web端/业务管理/新增交车任务.jsx
Normal file
@@ -0,0 +1,300 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型
|
||||
// 数字化资产ONEOS运管平台 - 新增交车任务模块(参照新增租赁合同布局)
|
||||
|
||||
const Component = function() {
|
||||
var antd = window.antd;
|
||||
var Input = antd.Input;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
var Checkbox = antd.Checkbox;
|
||||
var Tooltip = antd.Tooltip;
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
|
||||
var projectId = React.useState('');
|
||||
var selectedProjectId = projectId[0];
|
||||
var setSelectedProjectId = projectId[1];
|
||||
|
||||
var expectedDelivery = React.useState(null);
|
||||
var expectedDeliveryValue = expectedDelivery[0];
|
||||
var setExpectedDelivery = expectedDelivery[1];
|
||||
|
||||
var billingDate = React.useState(null);
|
||||
var billingDateValue = billingDate[0];
|
||||
var setBillingDate = billingDate[1];
|
||||
|
||||
var selectedRowKeys = React.useState([]);
|
||||
var checkedRowKeys = selectedRowKeys[0];
|
||||
var setCheckedRowKeys = selectedRowKeys[1];
|
||||
|
||||
var formErrors = React.useState({});
|
||||
var errors = formErrors[0];
|
||||
var setErrors = formErrors[1];
|
||||
|
||||
var reqSpecState = React.useState(false);
|
||||
var reqSpecOpen = reqSpecState[0];
|
||||
var setReqSpecOpen = reqSpecState[1];
|
||||
|
||||
// Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示
|
||||
var projectList = [
|
||||
{ id: 'p1', name: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [
|
||||
{ key: 'v1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliveryStatus: 'submitted' },
|
||||
{ key: 'v2', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10002', vin: 'LFV2BJCH8K3123457', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v3', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌', deliveryStatus: 'none' },
|
||||
{ key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' },
|
||||
{ key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' }
|
||||
]},
|
||||
{ id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [
|
||||
{ key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' },
|
||||
{ key: 'v8', brand: '福田', model: '欧辉氢能大巴', plateNo: '', vin: 'LZYTBACR2M2345678', monthRent: '19800', serviceFee: '1100', deposit: '45000', remark: '', deliveryStatus: 'none' }
|
||||
]},
|
||||
{ id: 'p3', name: '杭州某某租赁氢能项目', contractCode: 'HZZL20260115YW100089A', customerName: '杭州某某租赁有限公司', deliveryRegion: '浙江省 / 杭州市', deliveryLocation: '浙江省杭州市余杭区未来科技城', vehicles: [
|
||||
{ key: 'v9', brand: '品牌C', model: '型号C1', plateNo: '浙A40004', vin: 'L4234567890ABCDEF', monthRent: '8200', serviceFee: '450', deposit: '20000', remark: '重点客户', deliveryStatus: 'completed' },
|
||||
{ key: 'v10', brand: '品牌C', model: '型号C2', plateNo: '', vin: 'L5234567890ABCDEF', monthRent: '7800', serviceFee: '420', deposit: '19000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v11', brand: '东风', model: '氢燃料电池厢货', plateNo: '浙A50001', vin: 'LFV2BJCH8K5678901', monthRent: '9200', serviceFee: '520', deposit: '22000', remark: '城配', deliveryStatus: 'none' },
|
||||
{ key: 'v12', brand: '开沃', model: '创源氢能轻卡', plateNo: '浙A50002', vin: 'LJXTBACR9N6789012', monthRent: '8800', serviceFee: '480', deposit: '21000', remark: '', deliveryStatus: 'none' }
|
||||
]}
|
||||
];
|
||||
|
||||
var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; });
|
||||
var vehicleListRaw = selectedProject ? selectedProject.vehicles : [];
|
||||
var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; });
|
||||
var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; });
|
||||
|
||||
var handleProjectChange = function(id) {
|
||||
setSelectedProjectId(id || '');
|
||||
setCheckedRowKeys([]);
|
||||
};
|
||||
|
||||
var todayStr = (function() {
|
||||
var d = new Date();
|
||||
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
|
||||
})();
|
||||
|
||||
var validateExpectedDelivery = function() {
|
||||
if (!expectedDeliveryValue || !expectedDeliveryValue.length) return '请选择预计交车日期';
|
||||
var start = expectedDeliveryValue[0] && expectedDeliveryValue[0].format ? expectedDeliveryValue[0].format('YYYY-MM-DD') : null;
|
||||
var end = expectedDeliveryValue[1] && expectedDeliveryValue[1].format ? expectedDeliveryValue[1].format('YYYY-MM-DD') : null;
|
||||
if (!start) return '请选择预计交车日期';
|
||||
if (end && end < start) return '结束日期不能早于开始日期'; // 结束可与开始相同,即单日
|
||||
if (end && end < todayStr) return '结束日期不能早于当前日期';
|
||||
return null;
|
||||
};
|
||||
|
||||
var expectedDeliveryError = validateExpectedDelivery();
|
||||
|
||||
var billingDateError = !billingDateValue ? '请选择开始计费日期' : null;
|
||||
|
||||
var handleSubmit = function() {
|
||||
var err = {};
|
||||
if (!selectedProjectId) err.projectName = '请选择项目名称';
|
||||
if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError;
|
||||
if (billingDateError) err.billingDate = billingDateError;
|
||||
if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车';
|
||||
setErrors(err);
|
||||
if (Object.keys(err).length) return;
|
||||
var count = checkedRowKeys.length;
|
||||
message.success('已为 ' + count + ' 辆车生成交车任务,将按交车区域分配对应运维人员。');
|
||||
};
|
||||
|
||||
var handleCancel = function() {
|
||||
message.info('取消');
|
||||
};
|
||||
|
||||
var onSelectAll = function(checked) {
|
||||
if (checked) setCheckedRowKeys(selectableVehicles.map(function(v) { return v.key; }));
|
||||
else setCheckedRowKeys([]);
|
||||
};
|
||||
|
||||
var onSelectRow = function(record, checked) {
|
||||
if (checked) setCheckedRowKeys(function(prev) { return prev.indexOf(record.key) === -1 ? prev.concat(record.key) : prev; });
|
||||
else setCheckedRowKeys(function(prev) { return prev.filter(function(k) { return k !== record.key; }); });
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
labelRequired: { color: '#ff4d4f', marginRight: 4 },
|
||||
errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 },
|
||||
tableWrap: { marginTop: 16, overflowX: 'auto' },
|
||||
table: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
||||
th: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 },
|
||||
td: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' },
|
||||
inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 },
|
||||
modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' },
|
||||
modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 },
|
||||
modalBody: { padding: 20, overflow: 'auto', flex: 1 }
|
||||
};
|
||||
|
||||
var reqSpecBlock = { marginBottom: 16 };
|
||||
var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 };
|
||||
var reqSpecP = { fontSize: 13, color: '#666', marginBottom: 4 };
|
||||
var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 };
|
||||
|
||||
var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } },
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-新增交车任务')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:必选项,选择器,默认提示文本:请选择或输入项目名称,支持从输入框内输入内容进行模糊搜索,对应自营合同、租赁合同-「项目名称」字段;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.2.合同编码:输入框(禁用状态),根据所选项目名称自动反写合同编码;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.3.客户名称:输入框(禁用状态),根据所选项目名称自动反写客户名称;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.4.交车区域:输入框(禁用状态),根据所选项目名称自动反写交车区域。提交时根据交车区域,为对应区域运维人员分配对应交车任务;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.5.交车地点:输入框(禁用状态),根据所选项目名称自动反写交车地点;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.6.预计交车日期:必填项,日期选择器,支持某天或某个时间段两种模式,格式为YYYY-MM-DD或YYYY-MM-DD至YYYY-MM-DD,结束日期不能早于开始日期,并且结束日期不能早于当前日期;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.7.开始计费日期:必填项,日期选择器,支持单日选择,格式为YYYY-MM-DD;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.下方为列表,列表拉取该车辆租赁合同对应所有车辆信息(该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中),列表字段为:全选/多选、品牌、型号、车牌号、车辆识别代码、车辆月租金、服务费、保证金、备注;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.1.全选/多选:支持全选、多选模式,选择对应车辆后,点击提交自动生成被选中车辆交车任务,需要至少选择1辆,才能进行提交,该合同下已提交过交车任务的车辆不可选,已完成交车的车辆不显示在列表中;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.2.品牌:输入框(禁用状态),根据所选项目名称自动反写品牌;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.3.型号:输入框(禁用状态),根据所选项目名称自动反写型号;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.4.车牌号:输入框(禁用状态),根据所选项目名称自动反写车牌号,车牌号可能为空,为空时显示为-;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.5.车辆识别代码:输入框(禁用状态),根据所选项目名称自动反写车辆识别代码;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.6.车辆月租金:输入框(禁用状态),根据所选项目名称自动反写车辆月租金,后缀为元;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.7.服务费:输入框(禁用状态),根据所选项目名称自动反写服务费,后缀为元;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.8.保证金:输入框(禁用状态),根据所选项目名称自动反写保证金,后缀为元;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.8.9.备注:输入框(禁用状态),根据所选项目名称自动反写备注信息,备注为空时显示为-;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;')
|
||||
));
|
||||
|
||||
var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null;
|
||||
|
||||
var FormItem = function(props) {
|
||||
return React.createElement('div', { style: styles.formCol },
|
||||
React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label),
|
||||
props.children,
|
||||
props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null
|
||||
);
|
||||
};
|
||||
|
||||
var projectOptions = projectList.map(function(p) { return React.createElement(Option, { key: p.id, value: p.id }, p.name); });
|
||||
|
||||
var formRow1 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName },
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入项目名称',
|
||||
style: { width: '100%' },
|
||||
value: selectedProjectId || undefined,
|
||||
onChange: handleProjectChange,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; },
|
||||
status: errors.projectName ? 'error' : undefined
|
||||
}, projectOptions)),
|
||||
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: selectedProject ? selectedProject.contractCode : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: selectedProject ? selectedProject.customerName : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryRegion : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryLocation : '', disabled: true, style: { width: '100%' } }))
|
||||
);
|
||||
|
||||
var formRow2 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery },
|
||||
React.createElement(RangePicker, {
|
||||
style: { width: '100%' },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'],
|
||||
value: expectedDeliveryValue,
|
||||
onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); },
|
||||
status: errors.expectedDelivery ? 'error' : undefined
|
||||
})),
|
||||
React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate },
|
||||
React.createElement(DatePicker, {
|
||||
style: { width: '100%' },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择日期',
|
||||
value: billingDateValue,
|
||||
onChange: function(d, dateStr) { setBillingDate(d); },
|
||||
status: errors.billingDate ? 'error' : undefined
|
||||
}))
|
||||
);
|
||||
|
||||
var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; });
|
||||
var someSelectableChecked = selectableVehicles.some(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; });
|
||||
var tableHeader = React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: Object.assign({}, styles.th, { width: 48 }) },
|
||||
React.createElement(Checkbox, {
|
||||
checked: allSelectableChecked,
|
||||
indeterminate: someSelectableChecked && !allSelectableChecked,
|
||||
onChange: function(e) { onSelectAll(e.target.checked); }
|
||||
})),
|
||||
React.createElement('th', { style: styles.th }, '品牌'),
|
||||
React.createElement('th', { style: styles.th }, '型号'),
|
||||
React.createElement('th', { style: styles.th }, '车牌号'),
|
||||
React.createElement('th', { style: styles.th }, '车辆识别代码'),
|
||||
React.createElement('th', { style: styles.th }, '车辆月租金'),
|
||||
React.createElement('th', { style: styles.th }, '服务费'),
|
||||
React.createElement('th', { style: styles.th }, '保证金'),
|
||||
React.createElement('th', { style: styles.th }, '备注')
|
||||
)
|
||||
);
|
||||
|
||||
var tableBody = React.createElement('tbody', null,
|
||||
vehicleList.length === 0
|
||||
? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '请先选择项目名称,将自动带出该合同下车辆信息'))
|
||||
: vehicleList.map(function(row) {
|
||||
var isSubmitted = row.deliveryStatus === 'submitted';
|
||||
return React.createElement('tr', { key: row.key },
|
||||
React.createElement('td', { style: styles.td },
|
||||
isSubmitted
|
||||
? React.createElement(Tooltip, { title: '该车辆已有交车任务' },
|
||||
React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } },
|
||||
React.createElement(Checkbox, { disabled: true, checked: false }),
|
||||
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交')))
|
||||
: React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.vin, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.monthRent + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.serviceFee + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.deposit + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.remark || '-', disabled: true, style: styles.inputDisabled }))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
var tableEl = React.createElement('div', { style: styles.tableWrap },
|
||||
React.createElement('table', { style: styles.table }, tableHeader, tableBody)
|
||||
);
|
||||
|
||||
if (errors.vehicles) {
|
||||
tableEl = React.createElement('div', null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.errMsg, { marginBottom: 8 }) }, errors.vehicles),
|
||||
tableEl
|
||||
);
|
||||
}
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb },
|
||||
React.createElement('span', null, '业务管理'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', null, '交车任务'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { style: { color: '#1890ff' } }, '新增交车任务')),
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')),
|
||||
React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')),
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
formRow1,
|
||||
formRow2,
|
||||
tableEl)),
|
||||
React.createElement('div', { style: { height: 60 } }),
|
||||
reqSpecModalContent,
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'),
|
||||
React.createElement(Button, { onClick: handleCancel }, '取消'))
|
||||
);
|
||||
};
|
||||
123
web端/业务管理/查看交车任务.jsx
Normal file
123
web端/业务管理/查看交车任务.jsx
Normal file
@@ -0,0 +1,123 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型
|
||||
// 数字化资产ONEOS运管平台 - 查看交车任务模块(只读,布局参照新增交车任务)
|
||||
|
||||
const Component = function() {
|
||||
var antd = window.antd;
|
||||
var Input = antd.Input;
|
||||
var Button = antd.Button;
|
||||
|
||||
// Mock:当前查看的交车任务详情(从列表跳转带入或根据 id 拉取)
|
||||
var task = {
|
||||
projectName: '嘉兴某某物流氢能运输项目',
|
||||
contractCode: 'JXZL20260216YW101235A',
|
||||
customerName: '嘉兴某某物流有限公司',
|
||||
deliveryRegion: '浙江省 / 嘉兴市',
|
||||
deliveryLocation: '浙江省嘉兴市南湖区科技大道1号',
|
||||
planDeliveryDisplay: '2026-03-01至2026-03-05',
|
||||
billingStartDate: '2026-03-06',
|
||||
vehicles: [
|
||||
{ brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车' },
|
||||
{ brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌' },
|
||||
{ brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '' }
|
||||
]
|
||||
};
|
||||
|
||||
var vehicleList = task.vehicles || [];
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 },
|
||||
tableWrap: { marginTop: 16, overflowX: 'auto' },
|
||||
table: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
||||
th: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 },
|
||||
td: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }
|
||||
};
|
||||
|
||||
var FormItemReadOnly = function(props) {
|
||||
return React.createElement('div', { style: styles.formCol },
|
||||
React.createElement('label', { style: styles.label }, props.label),
|
||||
React.createElement(Input, { value: props.value != null ? props.value : '', disabled: true, style: styles.inputDisabled })
|
||||
);
|
||||
};
|
||||
|
||||
var formRow1 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '选择项目名称', value: task.projectName }),
|
||||
React.createElement(FormItemReadOnly, { label: '合同编码', value: task.contractCode }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户名称', value: task.customerName }),
|
||||
React.createElement(FormItemReadOnly, { label: '交车区域', value: task.deliveryRegion }),
|
||||
React.createElement(FormItemReadOnly, { label: '交车地点', value: task.deliveryLocation })
|
||||
);
|
||||
|
||||
var formRow2 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '预计交车日期', value: task.planDeliveryDisplay }),
|
||||
React.createElement(FormItemReadOnly, { label: '开始计费日期', value: task.billingStartDate })
|
||||
);
|
||||
|
||||
var tableHeader = React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.th }, '品牌'),
|
||||
React.createElement('th', { style: styles.th }, '型号'),
|
||||
React.createElement('th', { style: styles.th }, '车牌号'),
|
||||
React.createElement('th', { style: styles.th }, '车辆识别代码'),
|
||||
React.createElement('th', { style: styles.th }, '车辆月租金'),
|
||||
React.createElement('th', { style: styles.th }, '服务费'),
|
||||
React.createElement('th', { style: styles.th }, '保证金'),
|
||||
React.createElement('th', { style: styles.th }, '备注')
|
||||
)
|
||||
);
|
||||
|
||||
var tableBody = React.createElement('tbody', null,
|
||||
vehicleList.length === 0
|
||||
? React.createElement('tr', null, React.createElement('td', { colSpan: 8, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '暂无车辆'))
|
||||
: vehicleList.map(function(row, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.vin, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.monthRent + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.serviceFee + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.deposit + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.remark || '-', disabled: true, style: styles.inputDisabled }))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
var tableEl = React.createElement('div', { style: styles.tableWrap },
|
||||
React.createElement('table', { style: styles.table }, tableHeader, tableBody)
|
||||
);
|
||||
|
||||
var handleBack = function() {
|
||||
if (window.history && window.history.back) window.history.back();
|
||||
else antd.message.info('返回');
|
||||
};
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb },
|
||||
React.createElement('span', null, '业务管理'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', null, '交车任务'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { style: { color: '#1890ff' } }, '查看交车任务'))),
|
||||
React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')),
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
formRow1,
|
||||
formRow2,
|
||||
tableEl)),
|
||||
React.createElement('div', { style: { height: 60 } }),
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement(Button, { onClick: handleBack }, '返回'))
|
||||
);
|
||||
};
|
||||
933
web端/业务管理/租赁账单.jsx
Normal file
933
web端/业务管理/租赁账单.jsx
Normal file
@@ -0,0 +1,933 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 账单管理 - 车辆资产管理后台(按 antd 规范)
|
||||
|
||||
const Component = function() {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
var useEffect = React.useEffect;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var Table = antd.Table;
|
||||
var Tag = antd.Tag;
|
||||
var Modal = antd.Modal;
|
||||
var Input = antd.Input;
|
||||
var message = antd.message;
|
||||
var Space = antd.Space;
|
||||
var Row = antd.Row;
|
||||
var Col = antd.Col;
|
||||
|
||||
// 当前视图:list | detail
|
||||
var viewState = useState('list');
|
||||
var currentView = viewState[0];
|
||||
var setCurrentView = viewState[1];
|
||||
|
||||
// 分页
|
||||
var pageState = useState(1);
|
||||
var currentPage = pageState[0];
|
||||
var setCurrentPage = pageState[1];
|
||||
var pageSizeState = useState(10);
|
||||
var pageSize = pageSizeState[0];
|
||||
var setPageSize = pageSizeState[1];
|
||||
|
||||
// 选中项(Table rowSelection 用)
|
||||
var selectedRowKeysState = useState([]);
|
||||
var selectedRowKeys = selectedRowKeysState[0];
|
||||
var setSelectedRowKeys = selectedRowKeysState[1];
|
||||
|
||||
// 筛选器(表单值)
|
||||
var contractFilterState = useState(undefined);
|
||||
var contractFilter = contractFilterState[0];
|
||||
var setContractFilter = contractFilterState[1];
|
||||
var projectFilterState = useState(undefined);
|
||||
var projectFilter = projectFilterState[0];
|
||||
var setProjectFilter = projectFilterState[1];
|
||||
var customerFilterState = useState(undefined);
|
||||
var customerFilter = customerFilterState[0];
|
||||
var setCustomerFilter = customerFilterState[1];
|
||||
var statusFilterState = useState([]);
|
||||
var statusFilter = statusFilterState[0];
|
||||
var setStatusFilter = statusFilterState[1];
|
||||
|
||||
// 已应用的筛选条件(点击查询后生效)
|
||||
var appliedFilterState = useState({ contract: '', project: '', customer: '', status: [] });
|
||||
var appliedFilter = appliedFilterState[0];
|
||||
var setAppliedFilter = appliedFilterState[1];
|
||||
|
||||
// 弹窗:应付金额明细 | 服务费明细
|
||||
var popoverState = useState({ type: null, data: null });
|
||||
var popover = popoverState[0];
|
||||
var setPopover = popoverState[1];
|
||||
|
||||
// 照片查看器
|
||||
var photoViewerState = useState({ visible: false, photos: [], currentIndex: 0 });
|
||||
var photoViewer = photoViewerState[0];
|
||||
var setPhotoViewer = photoViewerState[1];
|
||||
|
||||
var viewingBillIdState = useState(null);
|
||||
var viewingBillId = viewingBillIdState[0];
|
||||
var setViewingBillId = viewingBillIdState[1];
|
||||
|
||||
// 详情模式:view | payment
|
||||
var detailModeState = useState('view');
|
||||
var detailMode = detailModeState[0];
|
||||
var setDetailMode = detailModeState[1];
|
||||
|
||||
// 付款表单数据
|
||||
var paymentFormState = useState({
|
||||
discountAmount: '',
|
||||
discountReason: '',
|
||||
vehicleList: [],
|
||||
hydrogenList: [],
|
||||
violationList: [],
|
||||
returnFeeList: []
|
||||
});
|
||||
var paymentForm = paymentFormState[0];
|
||||
var setPaymentForm = paymentFormState[1];
|
||||
|
||||
// 列表内成本编辑
|
||||
var editingListCellState = useState(null);
|
||||
var editingListCell = editingListCellState[0];
|
||||
var setEditingListCell = editingListCellState[1];
|
||||
var editingInputValueState = useState('');
|
||||
var editingInputValue = editingInputValueState[0];
|
||||
var setEditingInputValue = editingInputValueState[1];
|
||||
var listEditableCostsState = useState({});
|
||||
var listEditableCosts = listEditableCostsState[0];
|
||||
var setListEditableCosts = listEditableCostsState[1];
|
||||
|
||||
// 模拟选项与数据(与原文一致)
|
||||
var contractOptions = useMemo(function() {
|
||||
return [
|
||||
{ value: 'HT001', label: 'HT001 - 租赁合同A' },
|
||||
{ value: 'HT002', label: 'HT002 - 租赁合同B' },
|
||||
{ value: 'HT003', label: 'HT003 - 租赁合同C' }
|
||||
];
|
||||
}, []);
|
||||
var projectOptions = useMemo(function() {
|
||||
return [
|
||||
{ value: 'XM001', label: 'XM001 - 北京项目' },
|
||||
{ value: 'XM002', label: 'XM002 - 上海项目' },
|
||||
{ value: 'XM003', label: 'XM003 - 广州项目' }
|
||||
];
|
||||
}, []);
|
||||
var customerOptions = useMemo(function() {
|
||||
return [
|
||||
{ value: 'KH001', label: 'KH001 - 科技公司A' },
|
||||
{ value: 'KH002', label: 'KH002 - 制造企业B' },
|
||||
{ value: 'KH003', label: 'KH003 - 物流公司C' }
|
||||
];
|
||||
}, []);
|
||||
var statusOptions = [
|
||||
{ value: 'paid', label: '已付款' },
|
||||
{ value: 'partial', label: '部分付款' },
|
||||
{ value: 'unpaid', label: '未付款' }
|
||||
];
|
||||
|
||||
var mockBillList = useMemo(function() {
|
||||
var list = [];
|
||||
for (var i = 1; i <= 25; i++) {
|
||||
var statuses = ['paid', 'partial', 'unpaid'];
|
||||
var status = statuses[i % 3];
|
||||
list.push({
|
||||
id: 'BILL' + i,
|
||||
contractCode: 'HT00' + (i % 3 + 1),
|
||||
projectName: 'XM00' + (i % 3 + 1) + ' - 项目' + i,
|
||||
customerName: 'KH00' + (i % 3 + 1) + ' - 客户' + i,
|
||||
period: i,
|
||||
payableAmount: (15000 + i * 500).toFixed(2),
|
||||
paidAmount: status === 'paid' ? (15000 + i * 500).toFixed(2) : status === 'partial' ? (8000).toFixed(2) : '0.00',
|
||||
discountAmount: (i % 5 === 0 ? 200 : 0).toFixed(2),
|
||||
remark: status !== 'unpaid' ? '已付款备注' + i : '-',
|
||||
vehicleCost: (5000 + i * 100).toFixed(2),
|
||||
hydrogenCost: (300 + i * 20).toFixed(2),
|
||||
otherCost: (200 + i * 10).toFixed(2),
|
||||
status: status
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, []);
|
||||
|
||||
var mockPayableDetail = useMemo(function() {
|
||||
return [
|
||||
{ startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京A12345', rent: '8000.00', serviceFee: '500.00', deposit: '2000.00' },
|
||||
{ startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京B67890', rent: '7000.00', serviceFee: '400.00', deposit: '1500.00' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var mockBillDetail = useMemo(function() {
|
||||
return {
|
||||
startDate: '2025-01-01',
|
||||
endDate: '2025-01-31',
|
||||
contractCode: 'HT001',
|
||||
projectName: 'XM001 - 北京项目',
|
||||
customerName: 'KH001 - 科技公司A',
|
||||
department: '运营部',
|
||||
responsible: '张三',
|
||||
paymentCycle: '先付(付款周期:1个月)',
|
||||
discountTotal: '200.00',
|
||||
discountReason: '长期合作客户优惠,首期账单减免部分金额'
|
||||
};
|
||||
}, []);
|
||||
|
||||
var mockVehicleList = useMemo(function() {
|
||||
return [
|
||||
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', planDelivery: '2024-12-25', actualDelivery: '2024-12-28', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '8000.00', paidMonthlyRent: '7600.00', serviceFee: '500.00', paidServiceFee: '480.00', deposit: '2000.00', paidDeposit: '2000.00', serviceItems: [{ name: '保养服务', price: '300.00', effectiveDate: '2025-01-01' }, { name: '保险', price: '200.00', effectiveDate: '2025-01-01' }] },
|
||||
{ brand: '宝马', model: '530Li', plateNo: '京B67890', planDelivery: '2024-12-20', actualDelivery: '2024-12-22', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '7000.00', paidMonthlyRent: '7000.00', serviceFee: '400.00', paidServiceFee: '380.00', deposit: '1500.00', paidDeposit: '1500.00', serviceItems: [{ name: '保养服务', price: '250.00', effectiveDate: '2025-01-01' }] }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var mockReturnFeeData = useMemo(function() {
|
||||
return {
|
||||
totalAmount: '2850.00',
|
||||
paidTotalAmount: '2700.00',
|
||||
list: [
|
||||
{ feeName: '车辆外观损伤费', amount: '800.00', paidAmount: '760.00', photos: ['https://picsum.photos/80/80?random=1', 'https://picsum.photos/80/80?random=2'], attachments: [{ name: '外观损伤说明.pdf' }] },
|
||||
{ feeName: '轮胎磨损费', amount: '1200.00', paidAmount: '1150.00', photos: ['https://picsum.photos/80/80?random=3'], attachments: [{ name: '轮胎检测报告.pdf' }, { name: '维修单据.pdf' }] },
|
||||
{ feeName: '内饰清洁费', amount: '450.00', paidAmount: '430.00', photos: ['https://picsum.photos/80/80?random=4', 'https://picsum.photos/80/80?random=5', 'https://picsum.photos/80/80?random=6'], attachments: [] },
|
||||
{ feeName: '油量补充费', amount: '400.00', paidAmount: '360.00', photos: [], attachments: [{ name: '加油凭证.jpg' }] }
|
||||
]
|
||||
};
|
||||
}, []);
|
||||
|
||||
var mockViolationData = useMemo(function() {
|
||||
return {
|
||||
violationCount: 3,
|
||||
totalAmount: '650.00',
|
||||
paidTotalAmount: '600.00',
|
||||
list: [
|
||||
{ violationTime: '2025-01-10 08:30:00', plateNo: '京A12345', violationType: '违停', location: '北京市朝阳区xxx路', fineAmount: '200.00', paidFineAmount: '200.00' },
|
||||
{ violationTime: '2025-01-18 14:20:00', plateNo: '京B67890', violationType: '超速', location: '北京市海淀区xxx大道', fineAmount: '200.00', paidFineAmount: '180.00' },
|
||||
{ violationTime: '2025-01-25 09:15:00', plateNo: '京A12345', violationType: '闯红灯', location: '北京市东城区xxx路口', fineAmount: '250.00', paidFineAmount: '220.00' }
|
||||
]
|
||||
};
|
||||
}, []);
|
||||
|
||||
var mockHydrogenData = useMemo(function() {
|
||||
return {
|
||||
refuelCount: 12,
|
||||
balance: '3580.00',
|
||||
list: [
|
||||
{ refuelTime: '2025-01-15 09:30:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '15.5', costPrice: '28.00', feePrice: '434.00', paidFeePrice: '420.00' },
|
||||
{ refuelTime: '2025-01-18 14:20:00', stationName: '北京海淀加氢站', plateNo: '京B67890', amount: '12.0', costPrice: '28.00', feePrice: '336.00', paidFeePrice: '336.00' },
|
||||
{ refuelTime: '2025-01-22 11:00:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '18.2', costPrice: '28.00', feePrice: '509.60', paidFeePrice: '490.00' }
|
||||
]
|
||||
};
|
||||
}, []);
|
||||
|
||||
var vehicleBillTotals = useMemo(function() {
|
||||
var list = mockVehicleList || [];
|
||||
var monthlyRentTotal = 0, paidRentTotal = 0, serviceFeeTotal = 0, paidServiceFeeTotal = 0, depositTotal = 0, paidDepositTotal = 0;
|
||||
list.forEach(function(v) {
|
||||
monthlyRentTotal += parseFloat(v.monthlyRent || 0);
|
||||
paidRentTotal += parseFloat(v.paidMonthlyRent || 0);
|
||||
serviceFeeTotal += parseFloat(v.serviceFee || 0);
|
||||
paidServiceFeeTotal += parseFloat(v.paidServiceFee || 0);
|
||||
depositTotal += parseFloat(v.deposit || 0);
|
||||
paidDepositTotal += parseFloat(v.paidDeposit || 0);
|
||||
});
|
||||
return {
|
||||
monthlyRentTotal: monthlyRentTotal.toFixed(2),
|
||||
paidRentTotal: paidRentTotal.toFixed(2),
|
||||
serviceFeeTotal: serviceFeeTotal.toFixed(2),
|
||||
paidServiceFeeTotal: paidServiceFeeTotal.toFixed(2),
|
||||
depositTotal: depositTotal.toFixed(2),
|
||||
paidDepositTotal: paidDepositTotal.toFixed(2)
|
||||
};
|
||||
}, [mockVehicleList]);
|
||||
|
||||
var billInfoTotals = useMemo(function() {
|
||||
var monthlyRentTotal = parseFloat(vehicleBillTotals.monthlyRentTotal || 0);
|
||||
var serviceFeeTotal = parseFloat(vehicleBillTotals.serviceFeeTotal || 0);
|
||||
var depositTotal = parseFloat(vehicleBillTotals.depositTotal || 0);
|
||||
var hydrogenTotal = 0;
|
||||
(mockHydrogenData.list || []).forEach(function(item) { hydrogenTotal += parseFloat(item.feePrice || 0); });
|
||||
var violationTotal = parseFloat(mockViolationData.totalAmount || 0);
|
||||
var returnFeeTotal = parseFloat(mockReturnFeeData.totalAmount || 0);
|
||||
var payableTotal = monthlyRentTotal + serviceFeeTotal + depositTotal + hydrogenTotal + violationTotal + returnFeeTotal;
|
||||
var paidTotal = 0;
|
||||
(mockVehicleList || []).forEach(function(v) {
|
||||
paidTotal += parseFloat(v.paidMonthlyRent || 0) + parseFloat(v.paidServiceFee || 0) + parseFloat(v.paidDeposit || 0);
|
||||
});
|
||||
var discountTotal = parseFloat(mockBillDetail.discountTotal || 0);
|
||||
var unpaidTotal = Math.max(0, payableTotal - paidTotal - discountTotal);
|
||||
return {
|
||||
payableTotal: payableTotal.toFixed(2),
|
||||
paidTotal: paidTotal.toFixed(2),
|
||||
unpaidTotal: unpaidTotal.toFixed(2)
|
||||
};
|
||||
}, [vehicleBillTotals, mockHydrogenData, mockViolationData, mockReturnFeeData, mockVehicleList, mockBillDetail]);
|
||||
|
||||
var vehicleTotals = billInfoTotals;
|
||||
|
||||
var filteredList = useMemo(function() {
|
||||
var list = (mockBillList || []).slice();
|
||||
var c = (appliedFilter.contract || '').trim();
|
||||
var p = (appliedFilter.project || '').trim();
|
||||
var cust = (appliedFilter.customer || '').trim();
|
||||
var st = Array.isArray(appliedFilter.status) ? appliedFilter.status : [];
|
||||
if (c) list = list.filter(function(item) { return String(item.contractCode || '').toLowerCase().indexOf(c.toLowerCase()) >= 0; });
|
||||
if (p) list = list.filter(function(item) { return String(item.projectName || '').toLowerCase().indexOf(p.toLowerCase()) >= 0; });
|
||||
if (cust) list = list.filter(function(item) { return String(item.customerName || '').toLowerCase().indexOf(cust.toLowerCase()) >= 0; });
|
||||
if (st.length > 0) {
|
||||
var statusSet = {};
|
||||
st.forEach(function(s) { statusSet[s] = true; });
|
||||
list = list.filter(function(item) { return statusSet[item.status]; });
|
||||
}
|
||||
return list;
|
||||
}, [mockBillList, appliedFilter.contract, appliedFilter.project, appliedFilter.customer, appliedFilter.status]);
|
||||
|
||||
var totalCount = filteredList.length;
|
||||
var paginatedList = useMemo(function() {
|
||||
var start = (currentPage - 1) * pageSize;
|
||||
return filteredList.slice(start, start + pageSize);
|
||||
}, [filteredList, currentPage, pageSize]);
|
||||
|
||||
var handleSearch = useCallback(function() {
|
||||
setAppliedFilter({
|
||||
contract: (contractFilter != null && contractFilter !== '') ? String(contractFilter) : '',
|
||||
project: (projectFilter != null && projectFilter !== '') ? String(projectFilter) : '',
|
||||
customer: (customerFilter != null && customerFilter !== '') ? String(customerFilter) : '',
|
||||
status: Array.isArray(statusFilter) ? statusFilter.slice() : []
|
||||
});
|
||||
setCurrentPage(1);
|
||||
}, [contractFilter, projectFilter, customerFilter, statusFilter]);
|
||||
|
||||
var handleReset = useCallback(function() {
|
||||
setContractFilter(undefined);
|
||||
setProjectFilter(undefined);
|
||||
setCustomerFilter(undefined);
|
||||
setStatusFilter([]);
|
||||
setAppliedFilter({ contract: '', project: '', customer: '', status: [] });
|
||||
setCurrentPage(1);
|
||||
}, []);
|
||||
|
||||
var handleExport = useCallback(function() {
|
||||
if (selectedRowKeys.length === 0) {
|
||||
message.warning('请先选择要导出的数据');
|
||||
return;
|
||||
}
|
||||
message.success('导出 ' + selectedRowKeys.length + ' 条数据');
|
||||
}, [selectedRowKeys.length]);
|
||||
|
||||
var handleView = useCallback(function(id, isPayment) {
|
||||
setViewingBillId(id);
|
||||
setDetailMode(isPayment ? 'payment' : 'view');
|
||||
if (isPayment) {
|
||||
var vl = mockVehicleList.map(function(v) {
|
||||
return { paidMonthlyRent: v.paidMonthlyRent || '', paidServiceFee: v.paidServiceFee || '', paidDeposit: v.paidDeposit || '' };
|
||||
});
|
||||
var hl = mockHydrogenData.list.map(function(item) { return { paidFeePrice: item.paidFeePrice || '' }; });
|
||||
var viol = mockViolationData.list.map(function(item) { return { paidFineAmount: item.paidFineAmount || '' }; });
|
||||
var rfl = mockReturnFeeData.list.map(function(item) { return { paidAmount: item.paidAmount || '' }; });
|
||||
setPaymentForm({
|
||||
discountAmount: mockBillDetail.discountTotal || '',
|
||||
discountReason: mockBillDetail.discountReason || '',
|
||||
vehicleList: vl,
|
||||
hydrogenList: hl,
|
||||
violationList: viol,
|
||||
returnFeeList: rfl
|
||||
});
|
||||
}
|
||||
setCurrentView('detail');
|
||||
}, []);
|
||||
|
||||
var handleBackToList = useCallback(function() {
|
||||
setCurrentView('list');
|
||||
setViewingBillId(null);
|
||||
setDetailMode('view');
|
||||
}, []);
|
||||
|
||||
var handlePaymentFormChange = useCallback(function(section, index, field, value) {
|
||||
setPaymentForm(function(prev) {
|
||||
var next = {
|
||||
discountAmount: prev.discountAmount,
|
||||
discountReason: prev.discountReason,
|
||||
vehicleList: prev.vehicleList.slice(),
|
||||
hydrogenList: prev.hydrogenList.slice(),
|
||||
violationList: prev.violationList.slice(),
|
||||
returnFeeList: prev.returnFeeList.slice()
|
||||
};
|
||||
if (section === 'bill') {
|
||||
if (field === 'discountAmount') next.discountAmount = value;
|
||||
if (field === 'discountReason') next.discountReason = value;
|
||||
} else if (section === 'vehicle' && index >= 0 && index < next.vehicleList.length) {
|
||||
next.vehicleList[index] = Object.assign({}, next.vehicleList[index], { [field]: value });
|
||||
} else if (section === 'hydrogen' && index >= 0 && index < next.hydrogenList.length) {
|
||||
next.hydrogenList[index] = Object.assign({}, next.hydrogenList[index], { [field]: value });
|
||||
} else if (section === 'violation' && index >= 0 && index < next.violationList.length) {
|
||||
next.violationList[index] = Object.assign({}, next.violationList[index], { [field]: value });
|
||||
} else if (section === 'returnFee' && index >= 0 && index < next.returnFeeList.length) {
|
||||
next.returnFeeList[index] = Object.assign({}, next.returnFeeList[index], { [field]: value });
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var handleSubmit = useCallback(function() {
|
||||
var errors = [];
|
||||
if (!paymentForm.discountAmount || String(paymentForm.discountAmount).trim() === '') errors.push('减免金额');
|
||||
if (!paymentForm.discountReason || String(paymentForm.discountReason).trim() === '') errors.push('减免原因');
|
||||
paymentForm.vehicleList.forEach(function(v, i) {
|
||||
if (!v.paidMonthlyRent || String(v.paidMonthlyRent).trim() === '') errors.push('车辆账单-实付月租金(第' + (i + 1) + '行)');
|
||||
if (!v.paidServiceFee || String(v.paidServiceFee).trim() === '') errors.push('车辆账单-实付服务费(第' + (i + 1) + '行)');
|
||||
if (!v.paidDeposit || String(v.paidDeposit).trim() === '') errors.push('车辆账单-实付保证金(第' + (i + 1) + '行)');
|
||||
});
|
||||
paymentForm.hydrogenList.forEach(function(h, i) {
|
||||
if (!h.paidFeePrice || String(h.paidFeePrice).trim() === '') errors.push('氢费账单-实付氢费金额(第' + (i + 1) + '行)');
|
||||
});
|
||||
paymentForm.violationList.forEach(function(v, i) {
|
||||
if (!v.paidFineAmount || String(v.paidFineAmount).trim() === '') errors.push('违章费用-实付罚款金额(第' + (i + 1) + '行)');
|
||||
});
|
||||
paymentForm.returnFeeList.forEach(function(r, i) {
|
||||
if (!r.paidAmount || String(r.paidAmount).trim() === '') errors.push('还车费用-实付金额(第' + (i + 1) + '行)');
|
||||
});
|
||||
if (errors.length > 0) {
|
||||
message.warning('请填写必填项:' + errors.join('、'));
|
||||
return;
|
||||
}
|
||||
message.success('提交成功');
|
||||
handleBackToList();
|
||||
}, [paymentForm, handleBackToList]);
|
||||
|
||||
var handleSave = useCallback(function() {
|
||||
message.success('保存成功');
|
||||
}, []);
|
||||
|
||||
function getStatusText(status) {
|
||||
if (status === 'paid') return '已付款';
|
||||
if (status === 'partial') return '部分付款';
|
||||
return '未付款';
|
||||
}
|
||||
function getStatusTagColor(status) {
|
||||
if (status === 'paid') return 'success';
|
||||
if (status === 'partial') return 'warning';
|
||||
return 'error';
|
||||
}
|
||||
|
||||
// 列表表格列(含行内编辑氢费/其他成本)
|
||||
function renderEditableCost(row, field, label) {
|
||||
var rowId = row.id;
|
||||
var val = (listEditableCosts[rowId] && listEditableCosts[rowId][field] !== undefined)
|
||||
? listEditableCosts[rowId][field]
|
||||
: (row[field] != null ? row[field] : '0.00');
|
||||
var num = (parseFloat(val) || 0).toFixed(2);
|
||||
var isEditing = editingListCell && editingListCell.rowId === rowId && editingListCell.field === field;
|
||||
if (isEditing) {
|
||||
return React.createElement(Space, { key: 'edit', size: 4 },
|
||||
React.createElement(Input, {
|
||||
value: editingInputValue,
|
||||
onChange: function(e) { setEditingInputValue(e.target.value); },
|
||||
onBlur: function() {
|
||||
var n = parseFloat(editingInputValue);
|
||||
var formatted = (isNaN(n) ? 0 : n).toFixed(2);
|
||||
setListEditableCosts(function(prev) {
|
||||
var next = Object.assign({}, prev);
|
||||
next[rowId] = Object.assign({}, next[rowId], { [field]: formatted });
|
||||
return next;
|
||||
});
|
||||
setEditingListCell(null);
|
||||
setEditingInputValue('');
|
||||
},
|
||||
style: { width: 80 },
|
||||
autoFocus: true
|
||||
}),
|
||||
React.createElement('span', null, '元')
|
||||
);
|
||||
}
|
||||
return React.createElement('span', {
|
||||
style: { cursor: 'pointer' },
|
||||
onClick: function() {
|
||||
setEditingInputValue(num);
|
||||
setEditingListCell({ rowId: rowId, field: field });
|
||||
}
|
||||
}, num);
|
||||
}
|
||||
|
||||
var listColumns = useMemo(function() {
|
||||
return [
|
||||
{ title: '付款状态', dataIndex: 'status', key: 'status', width: 100, render: function(s) { return React.createElement(Tag, { color: getStatusTagColor(s) }, getStatusText(s)); } },
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 120 },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160 },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160 },
|
||||
{ title: '当前期数', dataIndex: 'period', key: 'period', width: 90 },
|
||||
{ title: '应付金额', dataIndex: 'payableAmount', key: 'payableAmount', width: 110, render: function(_, row) {
|
||||
return React.createElement('a', {
|
||||
onClick: function() { setPopover({ type: 'payable', data: mockPayableDetail }); },
|
||||
style: { color: '#1677ff', fontWeight: 600, textDecoration: 'underline', cursor: 'pointer' }
|
||||
}, row.payableAmount);
|
||||
}},
|
||||
{ title: '实付金额', dataIndex: 'paidAmount', key: 'paidAmount', width: 110 },
|
||||
{ title: '减免金额', dataIndex: 'discountAmount', key: 'discountAmount', width: 100 },
|
||||
{ title: '未付金额', key: 'unpaid', width: 100, render: function(_, row) {
|
||||
return (parseFloat(row.payableAmount || 0) - parseFloat(row.paidAmount || 0) - parseFloat(row.discountAmount || 0)).toFixed(2);
|
||||
}},
|
||||
{ title: '备注', dataIndex: 'remark', key: 'remark', width: 120 },
|
||||
{ title: '车辆成本(元)', dataIndex: 'vehicleCost', key: 'vehicleCost', width: 120, render: function(val) { return (parseFloat(val) || 0).toFixed(2); } },
|
||||
{ title: '氢费成本(元)', key: 'hydrogenCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'hydrogenCost', '氢费'); } },
|
||||
{ title: '其他成本(元)', key: 'otherCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'otherCost', '其他'); } },
|
||||
{ title: '操作', key: 'action', width: 140, fixed: 'right', render: function(_, row) {
|
||||
return React.createElement(Space, null,
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function() { handleView(row.id, false); } }, '查看'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', style: { color: '#52c41a' }, onClick: function() { handleView(row.id, true); } }, '收费')
|
||||
);
|
||||
}}
|
||||
];
|
||||
}, [listEditableCosts, editingListCell, editingInputValue, handleView]);
|
||||
|
||||
var rowSelection = useMemo(function() {
|
||||
return {
|
||||
selectedRowKeys: selectedRowKeys,
|
||||
onChange: function(keys) { setSelectedRowKeys(keys || []); }
|
||||
};
|
||||
}, [selectedRowKeys]);
|
||||
|
||||
var tablePagination = useMemo(function() {
|
||||
return {
|
||||
current: currentPage,
|
||||
pageSize: pageSize,
|
||||
total: totalCount,
|
||||
showSizeChanger: true,
|
||||
showTotal: function(t) { return '共 ' + t + ' 条'; },
|
||||
pageSizeOptions: ['10', '20', '50'],
|
||||
onChange: function(page, size) {
|
||||
setCurrentPage(page);
|
||||
if (size !== pageSize) setPageSize(size);
|
||||
}
|
||||
};
|
||||
}, [currentPage, pageSize, totalCount]);
|
||||
|
||||
// —————— 详情视图 ——————
|
||||
if (currentView === 'detail') {
|
||||
var detailBreadcrumbItems = [
|
||||
{ title: '运维管理' },
|
||||
{ title: '业务管理' },
|
||||
{ title: '租赁账单' },
|
||||
{ title: detailMode === 'payment' ? '收费' : '查看' }
|
||||
];
|
||||
|
||||
var billInfoCols = [
|
||||
{ label: '账单开始日期', value: mockBillDetail.startDate },
|
||||
{ label: '账单结束日期', value: mockBillDetail.endDate },
|
||||
{ label: '合同编码', value: mockBillDetail.contractCode },
|
||||
{ label: '项目名称', value: mockBillDetail.projectName },
|
||||
{ label: '客户名称', value: mockBillDetail.customerName },
|
||||
{ label: '业务部门', value: mockBillDetail.department },
|
||||
{ label: '业务负责人', value: mockBillDetail.responsible },
|
||||
{ label: '付款周期', value: mockBillDetail.paymentCycle }
|
||||
];
|
||||
|
||||
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
|
||||
React.createElement(Breadcrumb, { items: detailBreadcrumbItems, style: { marginBottom: 16 } }),
|
||||
React.createElement(Card, { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' } }, '账单信息'),
|
||||
React.createElement(Row, { gutter: [24, 24] },
|
||||
billInfoCols.map(function(item, i) {
|
||||
return React.createElement(Col, { key: i, span: 8 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, item.label + ':'),
|
||||
React.createElement('span', null, item.value)
|
||||
)
|
||||
);
|
||||
}),
|
||||
React.createElement(Col, { span: 8 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '应付款总额:'),
|
||||
React.createElement('span', { style: { color: '#1890ff', fontWeight: 600 } }, vehicleTotals.payableTotal + ' 元')
|
||||
)
|
||||
),
|
||||
React.createElement(Col, { span: 8 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '实付款总额:'),
|
||||
React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, vehicleTotals.paidTotal + ' 元')
|
||||
)
|
||||
),
|
||||
React.createElement(Col, { span: 8 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免金额:'),
|
||||
detailMode === 'payment'
|
||||
? React.createElement(Input, {
|
||||
value: paymentForm.discountAmount,
|
||||
onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountAmount', e.target.value); },
|
||||
style: { width: 120 },
|
||||
placeholder: '0.00',
|
||||
addonAfter: '元'
|
||||
})
|
||||
: React.createElement('span', { style: { fontWeight: 600 } }, mockBillDetail.discountTotal + ' 元')
|
||||
)
|
||||
),
|
||||
React.createElement(Col, { span: 8 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '未付款总额:'),
|
||||
React.createElement('span', { style: { color: '#ff4d4f', fontWeight: 600 } }, vehicleTotals.unpaidTotal + ' 元')
|
||||
)
|
||||
),
|
||||
React.createElement(Col, { span: 24 },
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免原因:'),
|
||||
detailMode === 'payment'
|
||||
? React.createElement(Input.TextArea, {
|
||||
value: paymentForm.discountReason,
|
||||
onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountReason', e.target.value); },
|
||||
placeholder: '请输入减免原因',
|
||||
rows: 3,
|
||||
style: { maxWidth: 400 }
|
||||
})
|
||||
: React.createElement('span', null, mockBillDetail.discountReason || '-')
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
// 车辆账单
|
||||
React.createElement(Card, { title: '车辆账单', style: { marginBottom: 16 } },
|
||||
React.createElement(Table, {
|
||||
dataSource: mockVehicleList,
|
||||
rowKey: 'plateNo',
|
||||
pagination: false,
|
||||
scroll: { x: 1200 },
|
||||
columns: [
|
||||
{ title: '品牌', dataIndex: 'brand', width: 80 },
|
||||
{ title: '型号', dataIndex: 'model', width: 90 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
|
||||
{ title: '计划交车日期', dataIndex: 'planDelivery', width: 120 },
|
||||
{ title: '实际交车日期', dataIndex: 'actualDelivery', width: 120 },
|
||||
{ title: '账单开始日期', dataIndex: 'billStart', width: 120 },
|
||||
{ title: '计费结束日期', dataIndex: 'billEnd', width: 120 },
|
||||
{ title: '车辆月租金', dataIndex: 'monthlyRent', width: 110 },
|
||||
{ title: '实付月租金', dataIndex: 'paidMonthlyRent', width: 110, render: function(_, record, idx) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidMonthlyRent || 0)).toFixed(2);
|
||||
var pf = paymentForm.vehicleList[idx] || {};
|
||||
return React.createElement(Input, { value: pf.paidMonthlyRent, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidMonthlyRent', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}},
|
||||
{ title: '服务费', dataIndex: 'serviceFee', width: 90, render: function(val, record) {
|
||||
if (detailMode === 'payment') return val;
|
||||
return React.createElement('a', { onClick: function() { setPopover({ type: 'service', data: record.serviceItems }); } }, val);
|
||||
}},
|
||||
{ title: '实付服务费', dataIndex: 'paidServiceFee', width: 110, render: function(_, record, idx) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidServiceFee || 0)).toFixed(2);
|
||||
var pf = paymentForm.vehicleList[idx] || {};
|
||||
return React.createElement(Input, { value: pf.paidServiceFee, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidServiceFee', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}},
|
||||
{ title: '保证金', dataIndex: 'deposit', width: 90 },
|
||||
{ title: '实付保证金', dataIndex: 'paidDeposit', width: 110, render: function(_, record, idx) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidDeposit || 0)).toFixed(2);
|
||||
var pf = paymentForm.vehicleList[idx] || {};
|
||||
return React.createElement(Input, { value: pf.paidDeposit, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidDeposit', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}}
|
||||
],
|
||||
summary: function() {
|
||||
return React.createElement(Table.Summary, null,
|
||||
React.createElement(Table.Summary.Row, null,
|
||||
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 7 }, '总计'),
|
||||
React.createElement(Table.Summary.Cell, { index: 7 }, vehicleBillTotals.monthlyRentTotal),
|
||||
React.createElement(Table.Summary.Cell, { index: 8 }, vehicleBillTotals.paidRentTotal),
|
||||
React.createElement(Table.Summary.Cell, { index: 9 }, vehicleBillTotals.serviceFeeTotal),
|
||||
React.createElement(Table.Summary.Cell, { index: 10 }, vehicleBillTotals.paidServiceFeeTotal),
|
||||
React.createElement(Table.Summary.Cell, { index: 11 }, vehicleBillTotals.depositTotal),
|
||||
React.createElement(Table.Summary.Cell, { index: 12 }, vehicleBillTotals.paidDepositTotal)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
),
|
||||
// 氢费账单
|
||||
React.createElement(Card, { title: '氢费账单', style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('span', { style: { marginRight: 24 } }, '加氢次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockHydrogenData.refuelCount + ' 次')),
|
||||
React.createElement('span', null, '当前氢费余额:', React.createElement('span', { style: { fontWeight: 600, color: '#1890ff' } }, mockHydrogenData.balance + ' 元'))
|
||||
),
|
||||
React.createElement(Table, {
|
||||
dataSource: mockHydrogenData.list,
|
||||
rowKey: function(item, i) { return i; },
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '加氢时间', dataIndex: 'refuelTime', width: 180 },
|
||||
{ title: '加氢站名称', dataIndex: 'stationName', width: 140 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
|
||||
{ title: '加氢量', dataIndex: 'amount', width: 90 },
|
||||
{ title: '成本单价(元/KG)', dataIndex: 'costPrice', width: 120 },
|
||||
{ title: '氢费价格(元)', dataIndex: 'feePrice', width: 120 },
|
||||
{ title: '实付氢费金额(元)', dataIndex: 'paidFeePrice', width: 140, render: function(_, record, i) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidFeePrice || 0)).toFixed(2);
|
||||
var pf = paymentForm.hydrogenList[i] || {};
|
||||
return React.createElement(Input, { value: pf.paidFeePrice, onChange: function(e) { handlePaymentFormChange('hydrogen', i, 'paidFeePrice', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}}
|
||||
],
|
||||
summary: function() {
|
||||
var totalFee = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.feePrice || 0); }, 0).toFixed(2);
|
||||
var totalPaid = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.paidFeePrice || 0); }, 0).toFixed(2);
|
||||
return React.createElement(Table.Summary, null,
|
||||
React.createElement(Table.Summary.Row, null,
|
||||
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 5 }, '总计'),
|
||||
React.createElement(Table.Summary.Cell, { index: 5 }, totalFee),
|
||||
React.createElement(Table.Summary.Cell, { index: 6 }, totalPaid)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
),
|
||||
// 违章费用
|
||||
React.createElement(Card, { title: '违章费用', style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('span', null, '违章次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockViolationData.violationCount + ' 次'))
|
||||
),
|
||||
React.createElement(Table, {
|
||||
dataSource: mockViolationData.list,
|
||||
rowKey: function(item, i) { return i; },
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '违章时间', dataIndex: 'violationTime', width: 180 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
|
||||
{ title: '违章类型', dataIndex: 'violationType', width: 100 },
|
||||
{ title: '违章地点', dataIndex: 'location', width: 200 },
|
||||
{ title: '罚款金额', dataIndex: 'fineAmount', width: 100 },
|
||||
{ title: '实付罚款金额(元)', dataIndex: 'paidFineAmount', width: 140, render: function(_, record, i) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidFineAmount || 0)).toFixed(2);
|
||||
var pf = paymentForm.violationList[i] || {};
|
||||
return React.createElement(Input, { value: pf.paidFineAmount, onChange: function(e) { handlePaymentFormChange('violation', i, 'paidFineAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}}
|
||||
],
|
||||
summary: function() {
|
||||
var totalPaid = mockViolationData.list.reduce(function(s, item) { return s + parseFloat(item.paidFineAmount || 0); }, 0).toFixed(2);
|
||||
return React.createElement(Table.Summary, null,
|
||||
React.createElement(Table.Summary.Row, null,
|
||||
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 4 }, '总计'),
|
||||
React.createElement(Table.Summary.Cell, { index: 4 }, mockViolationData.totalAmount),
|
||||
React.createElement(Table.Summary.Cell, { index: 5 }, totalPaid)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
),
|
||||
// 还车费用
|
||||
React.createElement(Card, { title: '还车费用', style: { marginBottom: 16 } },
|
||||
React.createElement(Table, {
|
||||
dataSource: mockReturnFeeData.list,
|
||||
rowKey: function(item, i) { return i; },
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '费用名称', dataIndex: 'feeName', width: 140 },
|
||||
{ title: '金额', dataIndex: 'amount', width: 100 },
|
||||
{ title: '实付金额(元)', dataIndex: 'paidAmount', width: 120, render: function(_, record, i) {
|
||||
if (detailMode !== 'payment') return (parseFloat(record.paidAmount || 0)).toFixed(2);
|
||||
var pf = paymentForm.returnFeeList[i] || {};
|
||||
return React.createElement(Input, { value: pf.paidAmount, onChange: function(e) { handlePaymentFormChange('returnFee', i, 'paidAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
|
||||
}},
|
||||
{ title: '照片', dataIndex: 'photos', width: 200, render: function(photos) {
|
||||
if (!photos || photos.length === 0) return '-';
|
||||
return React.createElement(Space, null, photos.slice(0, 5).map(function(url, idx) {
|
||||
return React.createElement('img', {
|
||||
key: idx,
|
||||
src: url,
|
||||
alt: '',
|
||||
style: { width: 40, height: 40, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
|
||||
onClick: function() { setPhotoViewer({ visible: true, photos: photos, currentIndex: idx }); }
|
||||
});
|
||||
}));
|
||||
}},
|
||||
{ title: '附件', dataIndex: 'attachments', render: function(attachments) {
|
||||
if (!attachments || attachments.length === 0) return '-';
|
||||
return React.createElement(Space, { wrap: true }, attachments.map(function(att, idx) {
|
||||
return React.createElement('a', { key: idx, onClick: function() { message.info('下载附件:' + att.name); } }, att.name);
|
||||
}));
|
||||
}}
|
||||
],
|
||||
summary: function() {
|
||||
var totalPaid = mockReturnFeeData.list.reduce(function(s, item) { return s + parseFloat(item.paidAmount || 0); }, 0).toFixed(2);
|
||||
return React.createElement(Table.Summary, null,
|
||||
React.createElement(Table.Summary.Row, null,
|
||||
React.createElement(Table.Summary.Cell, { index: 0 }, '总计'),
|
||||
React.createElement(Table.Summary.Cell, { index: 1 }, mockReturnFeeData.totalAmount),
|
||||
React.createElement(Table.Summary.Cell, { index: 2 }, totalPaid),
|
||||
React.createElement(Table.Summary.Cell, { index: 3 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 4 }, '')
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginTop: 24, textAlign: 'center' } },
|
||||
React.createElement(Space, null,
|
||||
detailMode === 'payment' && React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交审核'),
|
||||
detailMode === 'payment' && React.createElement(Button, { onClick: handleSave }, '保存'),
|
||||
detailMode === 'payment' && React.createElement(Button, { onClick: handleBackToList }, '取消'),
|
||||
detailMode === 'view' && React.createElement(Button, { onClick: handleBackToList }, '返回')
|
||||
)
|
||||
),
|
||||
// 照片查看器 Modal
|
||||
React.createElement(Modal, {
|
||||
title: '照片查看',
|
||||
open: photoViewer.visible && photoViewer.photos.length > 0,
|
||||
onCancel: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); },
|
||||
footer: null,
|
||||
width: '90vw',
|
||||
centered: true
|
||||
}, photoViewer.photos.length > 0 ? React.createElement('div', { style: { textAlign: 'center' } },
|
||||
React.createElement('img', {
|
||||
src: photoViewer.photos[photoViewer.currentIndex].replace('80/80', '600/600'),
|
||||
alt: '',
|
||||
style: { maxWidth: '100%', maxHeight: '70vh', objectFit: 'contain' }
|
||||
}),
|
||||
React.createElement('div', { style: { marginTop: 16 } },
|
||||
React.createElement(Button, {
|
||||
disabled: photoViewer.currentIndex <= 0,
|
||||
onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex - 1 }); }
|
||||
}, '上一张'),
|
||||
React.createElement('span', { style: { margin: '0 16px' } }, (photoViewer.currentIndex + 1) + ' / ' + photoViewer.photos.length),
|
||||
React.createElement(Button, {
|
||||
disabled: photoViewer.currentIndex >= photoViewer.photos.length - 1,
|
||||
onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex + 1 }); }
|
||||
}, '下一张')
|
||||
)
|
||||
) : null),
|
||||
// 服务费明细 Modal
|
||||
popover.type === 'service' && React.createElement(Modal, {
|
||||
title: '服务费明细',
|
||||
open: true,
|
||||
onCancel: function() { setPopover({ type: null, data: null }); },
|
||||
footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭')
|
||||
}, React.createElement(Table, {
|
||||
dataSource: popover.data,
|
||||
rowKey: function(item, i) { return i; },
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '服务项', dataIndex: 'name' },
|
||||
{ title: '价格', dataIndex: 'price' },
|
||||
{ title: '服务生效日期', dataIndex: 'effectiveDate' }
|
||||
]
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
// —————— 列表视图 ——————
|
||||
var listBreadcrumbItems = [
|
||||
{ title: '运维管理' },
|
||||
{ title: '业务管理' },
|
||||
{ title: '租赁账单' }
|
||||
];
|
||||
|
||||
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
|
||||
React.createElement(Breadcrumb, { items: listBreadcrumbItems, style: { marginBottom: 16 } }),
|
||||
React.createElement(Card, null,
|
||||
React.createElement(Row, { gutter: [16, 16], style: { marginBottom: 16 }, align: 'middle' },
|
||||
React.createElement(Col, null,
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '合同编码:'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择合同编码',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: contractFilter,
|
||||
onChange: setContractFilter,
|
||||
style: { width: 200 },
|
||||
options: contractOptions
|
||||
})
|
||||
),
|
||||
React.createElement(Col, null,
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '项目名称:'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择项目名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: projectFilter,
|
||||
onChange: setProjectFilter,
|
||||
style: { width: 200 },
|
||||
options: projectOptions
|
||||
})
|
||||
),
|
||||
React.createElement(Col, null,
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '客户名称:'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择客户名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: customerFilter,
|
||||
onChange: setCustomerFilter,
|
||||
style: { width: 200 },
|
||||
options: customerOptions
|
||||
})
|
||||
),
|
||||
React.createElement(Col, null,
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '付款状态:'),
|
||||
React.createElement(Select, {
|
||||
mode: 'multiple',
|
||||
placeholder: '全部',
|
||||
allowClear: true,
|
||||
value: statusFilter,
|
||||
onChange: setStatusFilter,
|
||||
style: { width: 200 },
|
||||
options: statusOptions,
|
||||
maxTagCount: 'responsive'
|
||||
})
|
||||
),
|
||||
React.createElement(Col, null,
|
||||
React.createElement(Space, null,
|
||||
React.createElement(Button, { type: 'primary', onClick: handleSearch }, '查询'),
|
||||
React.createElement(Button, { onClick: handleReset }, '重置')
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' } },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleExport }, '导出')
|
||||
),
|
||||
React.createElement(Table, {
|
||||
rowSelection: rowSelection,
|
||||
columns: listColumns,
|
||||
dataSource: paginatedList,
|
||||
rowKey: 'id',
|
||||
pagination: tablePagination,
|
||||
scroll: { x: 1600 },
|
||||
size: 'middle'
|
||||
})
|
||||
),
|
||||
// 应付金额明细 Modal(列表按内容一行显示、宽度随内容调整)
|
||||
React.createElement(Modal, {
|
||||
title: '应付金额明细',
|
||||
open: popover.type === 'payable',
|
||||
onCancel: function() { setPopover({ type: null, data: null }); },
|
||||
footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭'),
|
||||
width: 'fit-content',
|
||||
style: { maxWidth: '90vw' },
|
||||
styles: { body: { paddingBottom: 24 } }
|
||||
}, popover.type === 'payable' && popover.data ? React.createElement(Table, {
|
||||
dataSource: popover.data,
|
||||
rowKey: function(item, i) { return i; },
|
||||
pagination: false,
|
||||
tableLayout: 'auto',
|
||||
style: { minWidth: 0 },
|
||||
columns: [
|
||||
{ title: '账单开始日期', dataIndex: 'startDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
|
||||
{ title: '账单结束日期', dataIndex: 'endDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
|
||||
{ title: '车辆租金', dataIndex: 'rent', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
|
||||
{ title: '服务费', dataIndex: 'serviceFee', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
|
||||
{ title: '保证金', dataIndex: 'deposit', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }
|
||||
]
|
||||
}) : null)
|
||||
);
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
283
web端/业务管理/编辑交车任务.jsx
Normal file
283
web端/业务管理/编辑交车任务.jsx
Normal file
@@ -0,0 +1,283 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型
|
||||
// 数字化资产ONEOS运管平台 - 编辑交车任务模块(布局同新增交车任务,项目名称禁用不可改)
|
||||
|
||||
const Component = function() {
|
||||
var antd = window.antd;
|
||||
var Input = antd.Input;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
var Checkbox = antd.Checkbox;
|
||||
var Tooltip = antd.Tooltip;
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
|
||||
var projectId = React.useState('p1');
|
||||
var selectedProjectId = projectId[0];
|
||||
var setSelectedProjectId = projectId[1];
|
||||
|
||||
var expectedDelivery = React.useState(null);
|
||||
var expectedDeliveryValue = expectedDelivery[0];
|
||||
var setExpectedDelivery = expectedDelivery[1];
|
||||
|
||||
var billingDate = React.useState(null);
|
||||
var billingDateValue = billingDate[0];
|
||||
var setBillingDate = billingDate[1];
|
||||
|
||||
var selectedRowKeys = React.useState(['v3', 'v4']);
|
||||
var checkedRowKeys = selectedRowKeys[0];
|
||||
var setCheckedRowKeys = selectedRowKeys[1];
|
||||
|
||||
var formErrors = React.useState({});
|
||||
var errors = formErrors[0];
|
||||
var setErrors = formErrors[1];
|
||||
|
||||
var reqSpecState = React.useState(false);
|
||||
var reqSpecOpen = reqSpecState[0];
|
||||
var setReqSpecOpen = reqSpecState[1];
|
||||
|
||||
// Mock:项目列表及车辆样例。deliveryStatus: 'none' 可选,'submitted' 已提交交车任务不可选,'completed' 已完成交车不显示
|
||||
var projectList = [
|
||||
{ id: 'p1', name: '嘉兴某某物流氢能运输项目', contractCode: 'JXZL20260216YW101235A', customerName: '嘉兴某某物流有限公司', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '浙江省嘉兴市南湖区科技大道1号', vehicles: [
|
||||
{ key: 'v1', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10001', vin: 'LFV2BJCH8K3123456', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '首车', deliveryStatus: 'submitted' },
|
||||
{ key: 'v2', brand: '东风', model: '氢燃料电池重卡 H31', plateNo: '浙A10002', vin: 'LFV2BJCH8K3123457', monthRent: '12800', serviceFee: '800', deposit: '30000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v3', brand: '福田', model: '智蓝氢能轻卡', plateNo: '', vin: 'LZYTBACR2M1234567', monthRent: '8500', serviceFee: '500', deposit: '20000', remark: '待上牌', deliveryStatus: 'none' },
|
||||
{ key: 'v4', brand: '重汽', model: '豪沃氢能牵引车', plateNo: '浙F20001', vin: 'ZZ4257N386FZ12345', monthRent: '15000', serviceFee: '1000', deposit: '35000', remark: '', deliveryStatus: 'none' },
|
||||
{ key: 'v5', brand: '陕汽', model: '德龙氢能自卸', plateNo: '浙F20002', vin: 'SX1313GR456123456', monthRent: '13200', serviceFee: '880', deposit: '32000', remark: '固定线路', deliveryStatus: 'none' }
|
||||
]},
|
||||
{ id: 'p2', name: '上海某某运输氢能租赁项目', contractCode: 'SHZL20260201YW200123A', customerName: '上海某某运输公司', deliveryRegion: '上海市 / 上海市', deliveryLocation: '上海市浦东新区张江高科技园区', vehicles: [
|
||||
{ key: 'v6', brand: '上汽红岩', model: '杰狮氢能牵引', plateNo: '沪A30003', vin: 'SH1313HY789012345', monthRent: '14500', serviceFee: '950', deposit: '34000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v7', brand: '宇通', model: '氢能公交 ZK6126', plateNo: '沪B40001', vin: 'LZYTAGCF8K4567890', monthRent: '22000', serviceFee: '1200', deposit: '50000', remark: '示范线路', deliveryStatus: 'none' },
|
||||
{ key: 'v8', brand: '福田', model: '欧辉氢能大巴', plateNo: '', vin: 'LZYTBACR2M2345678', monthRent: '19800', serviceFee: '1100', deposit: '45000', remark: '', deliveryStatus: 'none' }
|
||||
]},
|
||||
{ id: 'p3', name: '杭州某某租赁氢能项目', contractCode: 'HZZL20260115YW100089A', customerName: '杭州某某租赁有限公司', deliveryRegion: '浙江省 / 杭州市', deliveryLocation: '浙江省杭州市余杭区未来科技城', vehicles: [
|
||||
{ key: 'v9', brand: '品牌C', model: '型号C1', plateNo: '浙A40004', vin: 'L4234567890ABCDEF', monthRent: '8200', serviceFee: '450', deposit: '20000', remark: '重点客户', deliveryStatus: 'completed' },
|
||||
{ key: 'v10', brand: '品牌C', model: '型号C2', plateNo: '', vin: 'L5234567890ABCDEF', monthRent: '7800', serviceFee: '420', deposit: '19000', remark: '', deliveryStatus: 'submitted' },
|
||||
{ key: 'v11', brand: '东风', model: '氢燃料电池厢货', plateNo: '浙A50001', vin: 'LFV2BJCH8K5678901', monthRent: '9200', serviceFee: '520', deposit: '22000', remark: '城配', deliveryStatus: 'none' },
|
||||
{ key: 'v12', brand: '开沃', model: '创源氢能轻卡', plateNo: '浙A50002', vin: 'LJXTBACR9N6789012', monthRent: '8800', serviceFee: '480', deposit: '21000', remark: '', deliveryStatus: 'none' }
|
||||
]}
|
||||
];
|
||||
|
||||
var selectedProject = projectList.find(function(p) { return p.id === selectedProjectId; });
|
||||
var vehicleListRaw = selectedProject ? selectedProject.vehicles : [];
|
||||
var vehicleList = vehicleListRaw.filter(function(v) { return v.deliveryStatus !== 'completed'; });
|
||||
var selectableVehicles = vehicleList.filter(function(v) { return v.deliveryStatus !== 'submitted'; });
|
||||
|
||||
var handleProjectChange = function(id) {
|
||||
setSelectedProjectId(id || '');
|
||||
setCheckedRowKeys([]);
|
||||
};
|
||||
|
||||
var todayStr = (function() {
|
||||
var d = new Date();
|
||||
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
|
||||
})();
|
||||
|
||||
var validateExpectedDelivery = function() {
|
||||
if (!expectedDeliveryValue || !expectedDeliveryValue.length) return '请选择预计交车日期';
|
||||
var start = expectedDeliveryValue[0] && expectedDeliveryValue[0].format ? expectedDeliveryValue[0].format('YYYY-MM-DD') : null;
|
||||
var end = expectedDeliveryValue[1] && expectedDeliveryValue[1].format ? expectedDeliveryValue[1].format('YYYY-MM-DD') : null;
|
||||
if (!start) return '请选择预计交车日期';
|
||||
if (end && end < start) return '结束日期不能早于开始日期';
|
||||
if (end && end < todayStr) return '结束日期不能早于当前日期';
|
||||
return null;
|
||||
};
|
||||
|
||||
var expectedDeliveryError = validateExpectedDelivery();
|
||||
|
||||
var billingDateError = !billingDateValue ? '请选择开始计费日期' : null;
|
||||
|
||||
var handleSubmit = function() {
|
||||
var err = {};
|
||||
if (!selectedProjectId) err.projectName = '请选择项目名称';
|
||||
if (expectedDeliveryError) err.expectedDelivery = expectedDeliveryError;
|
||||
if (billingDateError) err.billingDate = billingDateError;
|
||||
if (checkedRowKeys.length === 0 && selectableVehicles.length > 0) err.vehicles = '请至少选择一辆车';
|
||||
setErrors(err);
|
||||
if (Object.keys(err).length) return;
|
||||
message.success('交车任务已保存。');
|
||||
};
|
||||
|
||||
var handleCancel = function() {
|
||||
message.info('取消');
|
||||
};
|
||||
|
||||
var onSelectAll = function(checked) {
|
||||
if (checked) setCheckedRowKeys(selectableVehicles.map(function(v) { return v.key; }));
|
||||
else setCheckedRowKeys([]);
|
||||
};
|
||||
|
||||
var onSelectRow = function(record, checked) {
|
||||
if (checked) setCheckedRowKeys(function(prev) { return prev.indexOf(record.key) === -1 ? prev.concat(record.key) : prev; });
|
||||
else setCheckedRowKeys(function(prev) { return prev.filter(function(k) { return k !== record.key; }); });
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
labelRequired: { color: '#ff4d4f', marginRight: 4 },
|
||||
errMsg: { color: '#ff4d4f', fontSize: 12, marginTop: 4 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 },
|
||||
tableWrap: { marginTop: 16, overflowX: 'auto' },
|
||||
table: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
||||
th: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 },
|
||||
td: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' },
|
||||
inputDisabled: { width: '100%', padding: '6px 10px', border: '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#f5f5f5', color: '#666', fontSize: 13 },
|
||||
modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' },
|
||||
modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 },
|
||||
modalBody: { padding: 20, overflow: 'auto', flex: 1 }
|
||||
};
|
||||
|
||||
var reqSpecBlock = { marginBottom: 16 };
|
||||
var reqSpecH2 = { fontSize: 14, fontWeight: 600, color: '#333', marginBottom: 6 };
|
||||
var reqSpecLi = { fontSize: 13, color: '#666', marginBottom: 2, paddingLeft: 8 };
|
||||
|
||||
var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } },
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '交车任务')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '1.面包屑:'), React.createElement('div', { style: reqSpecLi }, '1.1.业务管理-交车任务-编辑交车任务')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.表单:'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.1.选择项目名称:已选择禁用,不可修改;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.2.合同编码、客户名称、交车区域、交车地点、预计交车日期、开始计费日期、车辆列表等可修改;'),
|
||||
React.createElement('div', { style: reqSpecLi }, '预计交车日期:不能早于当前日期'),
|
||||
React.createElement('div', { style: reqSpecLi }, '2.9.页面底部为提交、取消;')
|
||||
));
|
||||
|
||||
var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement(Button, { onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null;
|
||||
|
||||
var FormItem = function(props) {
|
||||
return React.createElement('div', { style: styles.formCol },
|
||||
React.createElement('label', { style: styles.label }, props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, props.label),
|
||||
props.children,
|
||||
props.error ? React.createElement('div', { style: styles.errMsg }, props.error) : null
|
||||
);
|
||||
};
|
||||
|
||||
var projectOptions = projectList.map(function(p) { return React.createElement(Option, { key: p.id, value: p.id }, p.name); });
|
||||
|
||||
var formRow1 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '选择项目名称', required: true, error: errors.projectName },
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入项目名称',
|
||||
style: { width: '100%' },
|
||||
value: selectedProjectId || undefined,
|
||||
disabled: true,
|
||||
showSearch: true,
|
||||
filterOption: function(input, opt) { return opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; },
|
||||
status: errors.projectName ? 'error' : undefined
|
||||
}, projectOptions)),
|
||||
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: selectedProject ? selectedProject.contractCode : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: selectedProject ? selectedProject.customerName : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '交车区域' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryRegion : '', disabled: true, style: { width: '100%' } })),
|
||||
React.createElement(FormItem, { label: '交车地点' }, React.createElement(Input, { value: selectedProject ? selectedProject.deliveryLocation : '', disabled: true, style: { width: '100%' } }))
|
||||
);
|
||||
|
||||
var formRow2 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '预计交车日期', required: true, error: errors.expectedDelivery },
|
||||
React.createElement(RangePicker, {
|
||||
style: { width: '100%' },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: ['请选择开始日期', '请选择结束日期(单日请选同一天)'],
|
||||
value: expectedDeliveryValue,
|
||||
onChange: function(dates) { setExpectedDelivery(dates && dates.length === 2 ? dates : null); },
|
||||
status: errors.expectedDelivery ? 'error' : undefined
|
||||
})),
|
||||
React.createElement(FormItem, { label: '开始计费日期', required: true, error: errors.billingDate },
|
||||
React.createElement(DatePicker, {
|
||||
style: { width: '100%' },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择日期',
|
||||
value: billingDateValue,
|
||||
onChange: function(d, dateStr) { setBillingDate(d); },
|
||||
status: errors.billingDate ? 'error' : undefined
|
||||
}))
|
||||
);
|
||||
|
||||
var allSelectableChecked = selectableVehicles.length > 0 && selectableVehicles.every(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; });
|
||||
var someSelectableChecked = selectableVehicles.some(function(v) { return checkedRowKeys.indexOf(v.key) !== -1; });
|
||||
var tableHeader = React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: Object.assign({}, styles.th, { width: 48 }) },
|
||||
React.createElement(Checkbox, {
|
||||
checked: allSelectableChecked,
|
||||
indeterminate: someSelectableChecked && !allSelectableChecked,
|
||||
onChange: function(e) { onSelectAll(e.target.checked); }
|
||||
})),
|
||||
React.createElement('th', { style: styles.th }, '品牌'),
|
||||
React.createElement('th', { style: styles.th }, '型号'),
|
||||
React.createElement('th', { style: styles.th }, '车牌号'),
|
||||
React.createElement('th', { style: styles.th }, '车辆识别代码'),
|
||||
React.createElement('th', { style: styles.th }, '车辆月租金'),
|
||||
React.createElement('th', { style: styles.th }, '服务费'),
|
||||
React.createElement('th', { style: styles.th }, '保证金'),
|
||||
React.createElement('th', { style: styles.th }, '备注')
|
||||
)
|
||||
);
|
||||
|
||||
var tableBody = React.createElement('tbody', null,
|
||||
vehicleList.length === 0
|
||||
? React.createElement('tr', null, React.createElement('td', { colSpan: 9, style: Object.assign({}, styles.td, { textAlign: 'center', color: '#999' }) }, '该合同下暂无车辆信息'))
|
||||
: vehicleList.map(function(row) {
|
||||
var isSubmitted = row.deliveryStatus === 'submitted';
|
||||
return React.createElement('tr', { key: row.key },
|
||||
React.createElement('td', { style: styles.td },
|
||||
isSubmitted
|
||||
? React.createElement(Tooltip, { title: '该车辆已有交车任务' },
|
||||
React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, cursor: 'not-allowed' } },
|
||||
React.createElement(Checkbox, { disabled: true, checked: false }),
|
||||
React.createElement('span', { style: { color: '#999', fontSize: 12 } }, '已提交')))
|
||||
: React.createElement(Checkbox, { checked: checkedRowKeys.indexOf(row.key) !== -1, onChange: function(e) { onSelectRow(row, e.target.checked); } })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.brand, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.model, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.plateNo || '-', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.vin, disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.monthRent + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.serviceFee + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.deposit + '元', disabled: true, style: styles.inputDisabled })),
|
||||
React.createElement('td', { style: styles.td }, React.createElement(Input, { value: row.remark || '-', disabled: true, style: styles.inputDisabled }))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
var tableEl = React.createElement('div', { style: styles.tableWrap },
|
||||
React.createElement('table', { style: styles.table }, tableHeader, tableBody)
|
||||
);
|
||||
|
||||
if (errors.vehicles) {
|
||||
tableEl = React.createElement('div', null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.errMsg, { marginBottom: 8 }) }, errors.vehicles),
|
||||
tableEl
|
||||
);
|
||||
}
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb },
|
||||
React.createElement('span', null, '业务管理'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', null, '交车任务'),
|
||||
React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { style: { color: '#1890ff' } }, '编辑交车任务')),
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 14 }, onClick: function() { setReqSpecOpen(true); } }, '查看需求说明')),
|
||||
React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '交车任务')),
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
formRow1,
|
||||
formRow2,
|
||||
tableEl)),
|
||||
React.createElement('div', { style: { height: 60 } }),
|
||||
reqSpecModalContent,
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'),
|
||||
React.createElement(Button, { onClick: handleCancel }, '取消'))
|
||||
);
|
||||
};
|
||||
446
web端/登录.jsx
Normal file
446
web端/登录.jsx
Normal file
@@ -0,0 +1,446 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 参考 Arco Design Pro 登录页布局:左侧品牌区 + 右侧登录表单
|
||||
const Component = function () {
|
||||
var antd = window.antd;
|
||||
var useState = React.useState;
|
||||
var useRef = React.useRef;
|
||||
var useEffect = React.useEffect;
|
||||
|
||||
var _state = useState({ username: '', password: '', remember: true, agreeTerms: false, phone: '', smsCode: '' });
|
||||
var formData = _state[0];
|
||||
var setFormData = _state[1];
|
||||
|
||||
var _mode = useState('account');
|
||||
var loginMode = _mode[0];
|
||||
var setLoginMode = _mode[1];
|
||||
|
||||
var _countdown = useState(0);
|
||||
var countdown = _countdown[0];
|
||||
var setCountdown = _countdown[1];
|
||||
|
||||
var _hasRequestedCode = useState(false);
|
||||
var hasRequestedCode = _hasRequestedCode[0];
|
||||
var setHasRequestedCode = _hasRequestedCode[1];
|
||||
|
||||
var timerRef = useRef(null);
|
||||
|
||||
useEffect(function () {
|
||||
return function () {
|
||||
if (timerRef.current) {
|
||||
clearInterval(timerRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
var handleChange = function (field, value) {
|
||||
var next = {};
|
||||
next[field] = value;
|
||||
setFormData(function (prev) {
|
||||
return Object.assign({}, prev, next);
|
||||
});
|
||||
};
|
||||
|
||||
var handleGetCode = function () {
|
||||
if (countdown > 0) return;
|
||||
setHasRequestedCode(true);
|
||||
setCountdown(60);
|
||||
timerRef.current = setInterval(function () {
|
||||
setCountdown(function (prev) {
|
||||
if (prev <= 1) {
|
||||
clearInterval(timerRef.current);
|
||||
timerRef.current = null;
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
var handleSubmit = function () {
|
||||
if (!formData.agreeTerms) {
|
||||
antd.message.warning('请阅读并同意《用户服务协议》和《隐私政策》');
|
||||
return;
|
||||
}
|
||||
if (loginMode === 'phone') {
|
||||
if (!formData.phone) {
|
||||
antd.message.warning('请输入手机号');
|
||||
return;
|
||||
}
|
||||
if (!formData.smsCode) {
|
||||
antd.message.warning('请输入验证码');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!formData.username) {
|
||||
antd.message.warning('请输入用户名');
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
antd.message.warning('请输入密码');
|
||||
return;
|
||||
}
|
||||
}
|
||||
antd.message.success('登录成功(原型演示)');
|
||||
if (window.$axure && typeof window.$axure.navigateToUrl === 'function') {
|
||||
window.$axure.navigateToUrl('工作台');
|
||||
} else {
|
||||
window.location.hash = '工作台';
|
||||
}
|
||||
};
|
||||
|
||||
var switchToPhone = function (e) {
|
||||
if (e && e.preventDefault) e.preventDefault();
|
||||
setLoginMode('phone');
|
||||
};
|
||||
var switchToAccount = function (e) {
|
||||
if (e && e.preventDefault) e.preventDefault();
|
||||
setLoginMode('account');
|
||||
};
|
||||
|
||||
var styles = {
|
||||
wrap: {
|
||||
display: 'flex',
|
||||
minHeight: '100vh',
|
||||
width: '100%',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
backgroundColor: '#f5f5f5'
|
||||
},
|
||||
left: {
|
||||
flex: '1',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '48px',
|
||||
background: 'linear-gradient(135deg, #1d2129 0%, #2d3748 50%, #1d2129 100%)',
|
||||
color: '#fff'
|
||||
},
|
||||
leftContent: {
|
||||
maxWidth: '400px',
|
||||
textAlign: 'center'
|
||||
},
|
||||
title: {
|
||||
fontSize: '28px',
|
||||
fontWeight: '600',
|
||||
marginBottom: '16px',
|
||||
letterSpacing: '0.5px'
|
||||
},
|
||||
desc: {
|
||||
fontSize: '14px',
|
||||
opacity: 0.85,
|
||||
lineHeight: 1.6
|
||||
},
|
||||
right: {
|
||||
width: '480px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '48px',
|
||||
backgroundColor: '#fff',
|
||||
boxShadow: '-4px 0 24px rgba(0,0,0,0.06)'
|
||||
},
|
||||
formCard: {
|
||||
width: '100%',
|
||||
maxWidth: '360px'
|
||||
},
|
||||
formTitle: {
|
||||
fontSize: '24px',
|
||||
fontWeight: '600',
|
||||
color: '#1d2129',
|
||||
marginBottom: '8px',
|
||||
textAlign: 'center'
|
||||
},
|
||||
formSub: {
|
||||
fontSize: '14px',
|
||||
color: '#86909c',
|
||||
marginBottom: '32px',
|
||||
textAlign: 'center'
|
||||
},
|
||||
formItem: {
|
||||
marginBottom: '20px'
|
||||
},
|
||||
label: {
|
||||
display: 'block',
|
||||
fontSize: '14px',
|
||||
color: '#4e5969',
|
||||
marginBottom: '8px'
|
||||
},
|
||||
input: {
|
||||
width: '100%',
|
||||
height: '40px',
|
||||
padding: '8px 12px',
|
||||
fontSize: '14px',
|
||||
border: '1px solid #e5e6eb',
|
||||
borderRadius: '6px',
|
||||
boxSizing: 'border-box',
|
||||
outline: 'none'
|
||||
},
|
||||
codeRow: {
|
||||
display: 'flex',
|
||||
gap: '8px',
|
||||
alignItems: 'center'
|
||||
},
|
||||
codeInput: {
|
||||
flex: '1',
|
||||
height: '40px',
|
||||
padding: '8px 12px',
|
||||
fontSize: '14px',
|
||||
border: '1px solid #e5e6eb',
|
||||
borderRadius: '6px',
|
||||
boxSizing: 'border-box',
|
||||
outline: 'none'
|
||||
},
|
||||
getCodeBtn: {
|
||||
flexShrink: 0,
|
||||
height: '40px',
|
||||
padding: '0 16px',
|
||||
fontSize: '14px',
|
||||
color: '#165dff',
|
||||
backgroundColor: '#fff',
|
||||
border: '1px solid #165dff',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
getCodeBtnDisabled: {
|
||||
color: '#86909c',
|
||||
borderColor: '#e5e6eb',
|
||||
cursor: 'not-allowed'
|
||||
},
|
||||
row: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '24px'
|
||||
},
|
||||
submitBtn: {
|
||||
width: '100%',
|
||||
height: '44px',
|
||||
fontSize: '16px',
|
||||
fontWeight: '500',
|
||||
borderRadius: '6px'
|
||||
},
|
||||
tabBar: {
|
||||
display: 'flex',
|
||||
marginBottom: '24px',
|
||||
borderBottom: '1px solid #e5e6eb'
|
||||
},
|
||||
tab: {
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
padding: '12px 0',
|
||||
fontSize: '15px',
|
||||
color: '#86909c',
|
||||
cursor: 'pointer',
|
||||
borderBottom: '2px solid transparent',
|
||||
marginBottom: '-1px'
|
||||
},
|
||||
tabActive: {
|
||||
color: '#165dff',
|
||||
fontWeight: '500',
|
||||
borderBottomColor: '#165dff'
|
||||
}
|
||||
};
|
||||
|
||||
return React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement('style', {
|
||||
dangerouslySetInnerHTML: {
|
||||
__html: '@media (max-width: 768px) { .login-right { width: 100% !important; min-width: 100% !important; } .login-left { min-height: 200px !important; } .login-wrap { flex-direction: column !important; } }'
|
||||
}
|
||||
}),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.wrap, className: 'login-wrap' },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.left, className: 'login-left' },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.leftContent },
|
||||
React.createElement('h1', { style: styles.title }, '羚牛氢能'),
|
||||
React.createElement('p', { style: styles.desc }, '数字化资产ONE-OS运管平台')
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.right, className: 'login-right' },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.formCard },
|
||||
React.createElement('h2', { style: styles.formTitle }, '欢迎登录'),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.tabBar },
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
style: loginMode === 'account' ? Object.assign({}, styles.tab, styles.tabActive) : styles.tab,
|
||||
onClick: switchToAccount,
|
||||
role: 'button',
|
||||
tabIndex: 0
|
||||
},
|
||||
'账号密码登录'
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
style: loginMode === 'phone' ? Object.assign({}, styles.tab, styles.tabActive) : styles.tab,
|
||||
onClick: switchToPhone,
|
||||
role: 'button',
|
||||
tabIndex: 0
|
||||
},
|
||||
'手机号登录'
|
||||
)
|
||||
),
|
||||
React.createElement('p', { style: styles.formSub }, loginMode === 'phone' ? '请输入手机号与验证码' : '请输入您的账号与密码'),
|
||||
loginMode === 'phone'
|
||||
? React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.formItem },
|
||||
React.createElement('label', { style: styles.label }, '手机号'),
|
||||
React.createElement('input', {
|
||||
style: styles.input,
|
||||
placeholder: '请输入手机号',
|
||||
value: formData.phone,
|
||||
onChange: function (e) {
|
||||
handleChange('phone', e.target.value);
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.formItem },
|
||||
React.createElement('label', { style: styles.label }, '验证码'),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.codeRow },
|
||||
React.createElement('input', {
|
||||
style: styles.codeInput,
|
||||
placeholder: '请输入验证码',
|
||||
value: formData.smsCode,
|
||||
onChange: function (e) {
|
||||
handleChange('smsCode', e.target.value);
|
||||
}
|
||||
}),
|
||||
React.createElement(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
style: countdown > 0 ? Object.assign({}, styles.getCodeBtn, styles.getCodeBtnDisabled) : styles.getCodeBtn,
|
||||
disabled: countdown > 0,
|
||||
onClick: handleGetCode
|
||||
},
|
||||
countdown > 0 ? countdown + '秒后重新获取' : (hasRequestedCode ? '再次获取' : '获取验证码')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
: React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.formItem },
|
||||
React.createElement('label', { style: styles.label }, '用户名'),
|
||||
React.createElement('input', {
|
||||
style: styles.input,
|
||||
placeholder: '请输入用户名',
|
||||
value: formData.username,
|
||||
onChange: function (e) {
|
||||
handleChange('username', e.target.value);
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.formItem },
|
||||
React.createElement('label', { style: styles.label }, '密码'),
|
||||
React.createElement('input', {
|
||||
style: styles.input,
|
||||
type: 'password',
|
||||
placeholder: '请输入密码',
|
||||
value: formData.password,
|
||||
onChange: function (e) {
|
||||
handleChange('password', e.target.value);
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.row },
|
||||
React.createElement(
|
||||
'label',
|
||||
{ style: { display: 'flex', alignItems: 'center', cursor: 'pointer', fontSize: '14px', color: '#4e5969' } },
|
||||
React.createElement('input', {
|
||||
type: 'checkbox',
|
||||
checked: formData.remember,
|
||||
onChange: function (e) {
|
||||
handleChange('remember', e.target.checked);
|
||||
},
|
||||
style: { marginRight: '8px' }
|
||||
}),
|
||||
'记住我'
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'label',
|
||||
{
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
color: '#4e5969',
|
||||
marginBottom: '20px',
|
||||
lineHeight: 1.5
|
||||
}
|
||||
},
|
||||
React.createElement('input', {
|
||||
type: 'checkbox',
|
||||
checked: formData.agreeTerms,
|
||||
onChange: function (e) {
|
||||
handleChange('agreeTerms', e.target.checked);
|
||||
},
|
||||
style: { marginRight: '8px', marginTop: '3px', flexShrink: 0 }
|
||||
}),
|
||||
React.createElement(
|
||||
'span',
|
||||
null,
|
||||
'阅读并同意',
|
||||
React.createElement('a', { href: '#', style: { color: '#165dff', marginLeft: '2px' }, onClick: function (e) { e.preventDefault(); } }, '《用户服务协议》'),
|
||||
'和',
|
||||
React.createElement('a', { href: '#', style: { color: '#165dff' }, onClick: function (e) { e.preventDefault(); } }, '《隐私政策》')
|
||||
)
|
||||
),
|
||||
antd && antd.Button
|
||||
? React.createElement(antd.Button, {
|
||||
type: 'primary',
|
||||
block: true,
|
||||
size: 'large',
|
||||
style: styles.submitBtn,
|
||||
onClick: handleSubmit
|
||||
}, '登 录')
|
||||
: React.createElement(
|
||||
'button',
|
||||
{
|
||||
style: Object.assign({}, styles.submitBtn, {
|
||||
backgroundColor: '#165dff',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
cursor: 'pointer'
|
||||
}),
|
||||
onClick: handleSubmit
|
||||
},
|
||||
'登 录'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
1048
web端/财务管理/提车首付款.jsx
Normal file
1048
web端/财务管理/提车首付款.jsx
Normal file
File diff suppressed because it is too large
Load Diff
BIN
web端/车辆租赁合同/.DS_Store
vendored
Normal file
BIN
web端/车辆租赁合同/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
web端/车辆租赁合同/2026-02-27租赁合同修改点.zip
Normal file
BIN
web端/车辆租赁合同/2026-02-27租赁合同修改点.zip
Normal file
Binary file not shown.
690
web端/车辆租赁合同/合同续签.jsx
Normal file
690
web端/车辆租赁合同/合同续签.jsx
Normal file
File diff suppressed because one or more lines are too long
655
web端/车辆租赁合同/新增租赁合同.jsx
Normal file
655
web端/车辆租赁合同/新增租赁合同.jsx
Normal file
File diff suppressed because one or more lines are too long
448
web端/车辆租赁合同/新增车辆.jsx
Normal file
448
web端/车辆租赁合同/新增车辆.jsx
Normal file
@@ -0,0 +1,448 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 业务管理 - 车辆租赁合同 - 新增车辆(与查看合同页布局一致,仅新增车辆信息卡片可编辑)
|
||||
|
||||
const Component = function() {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var antd = window.antd;
|
||||
var Select = antd.Select;
|
||||
var Input = antd.Input;
|
||||
var Button = antd.Button;
|
||||
var Table = antd.Table;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
|
||||
var cc1State = React.useState(false);
|
||||
var cc1 = cc1State[0];
|
||||
var setCc1 = cc1State[1];
|
||||
var cc2State = React.useState(false);
|
||||
var cc2 = cc2State[0];
|
||||
var setCc2 = cc2State[1];
|
||||
var cc3State = React.useState(false);
|
||||
var cc3 = cc3State[0];
|
||||
var setCc3 = cc3State[1];
|
||||
var cc4State = React.useState(false);
|
||||
var cc4 = cc4State[0];
|
||||
var setCc4 = cc4State[1];
|
||||
var cc5State = React.useState(false);
|
||||
var cc5 = cc5State[0];
|
||||
var setCc5 = cc5State[1];
|
||||
var cc6State = React.useState(false);
|
||||
var cc6 = cc6State[0];
|
||||
var setCc6 = cc6State[1];
|
||||
var cc7State = React.useState(false);
|
||||
var cc7 = cc7State[0];
|
||||
var setCc7 = cc7State[1];
|
||||
|
||||
var emptyNewRow = { brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' };
|
||||
var _newVehicleRows = useState([Object.assign({}, emptyNewRow)]);
|
||||
var newVehicleRows = _newVehicleRows[0];
|
||||
var setNewVehicleRows = _newVehicleRows[1];
|
||||
var _serviceModalRowIndex = useState(null);
|
||||
var serviceModalRowIndex = _serviceModalRowIndex[0];
|
||||
var setServiceModalRowIndex = _serviceModalRowIndex[1];
|
||||
|
||||
var brandList = ['品牌A', '品牌B', '品牌C', '品牌D'];
|
||||
var serviceItemOptions = ['代处理费用', '罚款', '违章处理违约金', '未参加安全培训', '车辆出险', '年检年审违约', '停车费', '设备损坏金(包含易损件)', '清洗费', '上门收车人工费', '上门收车送车行驶费', '上门收车基础服务费', '保险上浮', '保养费用', '补办驾驶证', '补办牌照', '补办营运证', '补办加氢证', '借用备用钥匙', '补配钥匙', '租金', '氢气费-客', '退还车氢量差', '能源费补缴', '能源费退款', '送车上门人工费', '送车上门送车行驶费', '送车上门基础服务费', '保证金', '氢气预付费', '维修费用', 'ETC-客', 'ETC卡缺损费', 'ETC设备缺损费', '电费-客', '未结算保养费', '未结算维修费', '车损费', '工具损坏或丢失费', '证件费', '广告损坏费', '送车服务费', '接车服务费', '补办行驶证', '超赔险', '轮胎磨损费', '无忧包', '轮胎保', '养护保', '尾板'];
|
||||
var modelByBrand = { '品牌A': ['型号A1', '型号A2', '型号A3'], '品牌B': ['型号B1', '型号B2'], '品牌C': ['型号C1', '型号C2'], '品牌D': ['型号D1'] };
|
||||
var plateNoOptions = ['浙A10001', '浙A10002', '浙B20001', '浙B20002', '浙C30001', '浙C30002', '沪D40001', '沪D40002', '苏E50001', '苏E50002', '京F60001', '京F60002'].map(function(p) { return { value: p, label: p }; });
|
||||
|
||||
var addNewVehicleRow = useCallback(function() {
|
||||
setNewVehicleRows(function(prev) { return prev.concat([{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }]); });
|
||||
}, []);
|
||||
var removeNewVehicleRow = useCallback(function(index) {
|
||||
setNewVehicleRows(function(prev) {
|
||||
var next = prev.slice();
|
||||
next.splice(index, 1);
|
||||
if (next.length === 0) next = [{ brand: '', model: '', plateNo: '', vin: '', monthRent: '', serviceItems: [{ project: '', fee: '', effectiveDate: '' }], deposit: '', remark: '' }];
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
var updateNewVehicleRow = useCallback(function(index, field, value) {
|
||||
setNewVehicleRows(function(prev) {
|
||||
var next = prev.slice();
|
||||
var row = next[index] || emptyNewRow;
|
||||
var o = {};
|
||||
o[field] = value;
|
||||
if (field === 'brand') o.model = '';
|
||||
if (field === 'plateNo') o.vin = value;
|
||||
next[index] = Object.assign({}, row, o);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var calcRowServiceFee = function(row) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < (row.serviceItems || []).length; i++) {
|
||||
var fee = parseFloat(row.serviceItems[i].fee);
|
||||
if (!isNaN(fee)) sum += fee;
|
||||
}
|
||||
return sum.toFixed(2);
|
||||
};
|
||||
var openServiceModal = function(index) { setServiceModalRowIndex(index); };
|
||||
var closeServiceModal = function() { setServiceModalRowIndex(null); };
|
||||
var addServiceItem = function() {
|
||||
if (serviceModalRowIndex === null) return;
|
||||
var next = newVehicleRows.slice();
|
||||
var row = next[serviceModalRowIndex];
|
||||
var items = (row.serviceItems || []).concat([{ project: '', fee: '', effectiveDate: '' }]);
|
||||
var newRow = {};
|
||||
for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; }
|
||||
newRow.serviceItems = items;
|
||||
next[serviceModalRowIndex] = newRow;
|
||||
setNewVehicleRows(next);
|
||||
};
|
||||
var removeServiceItem = function(siIndex) {
|
||||
if (serviceModalRowIndex === null) return;
|
||||
var next = newVehicleRows.slice();
|
||||
var row = next[serviceModalRowIndex];
|
||||
var items = (row.serviceItems || []).slice();
|
||||
items.splice(siIndex, 1);
|
||||
if (items.length === 0) items = [{ project: '', fee: '', effectiveDate: '' }];
|
||||
var newRow = {};
|
||||
for (var k in row) { if (row.hasOwnProperty(k) && k !== 'serviceItems') newRow[k] = row[k]; }
|
||||
newRow.serviceItems = items;
|
||||
next[serviceModalRowIndex] = newRow;
|
||||
setNewVehicleRows(next);
|
||||
};
|
||||
var updateServiceItem = function(siIndex, field, value) {
|
||||
if (serviceModalRowIndex === null) return;
|
||||
var next = newVehicleRows.slice();
|
||||
var row = next[serviceModalRowIndex];
|
||||
var items = (row.serviceItems || []).slice();
|
||||
var item = items[siIndex] || {};
|
||||
var newItem = {};
|
||||
for (var k in item) { if (item.hasOwnProperty(k)) newItem[k] = item[k]; }
|
||||
newItem[field] = value;
|
||||
items[siIndex] = newItem;
|
||||
var newRow = {};
|
||||
for (var rk in row) { if (row.hasOwnProperty(rk) && rk !== 'serviceItems') newRow[rk] = row[rk]; }
|
||||
newRow.serviceItems = items;
|
||||
next[serviceModalRowIndex] = newRow;
|
||||
setNewVehicleRows(next);
|
||||
};
|
||||
var newOrderSummary = (function() {
|
||||
var count = newVehicleRows.length;
|
||||
var rentService = 0;
|
||||
var depositTotal = 0;
|
||||
for (var i = 0; i < newVehicleRows.length; i++) {
|
||||
rentService += parseFloat(newVehicleRows[i].monthRent) || 0;
|
||||
rentService += parseFloat(calcRowServiceFee(newVehicleRows[i])) || 0;
|
||||
depositTotal += parseFloat(newVehicleRows[i].deposit) || 0;
|
||||
}
|
||||
return { vehicleCount: count, totalRentService: rentService.toFixed(2), totalDeposit: depositTotal.toFixed(2) };
|
||||
})();
|
||||
|
||||
var scrollToCard = function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 },
|
||||
anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
|
||||
cardToggle: { color: '#999', fontSize: 14 },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
|
||||
formColFull: { flex: '0 0 100%', marginBottom: 8 },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 },
|
||||
inputDisabled: { backgroundColor: '#f5f5f5', color: '#666', cursor: 'default', borderColor: '#e8e8e8' },
|
||||
summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 },
|
||||
summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' },
|
||||
summaryListLabel: { flex: '0 0 140px', color: '#666' },
|
||||
summaryListValue: { flex: 1, fontWeight: 600, color: '#333' },
|
||||
rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
||||
rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 },
|
||||
rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' },
|
||||
rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 },
|
||||
btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 },
|
||||
btnDefault: { backgroundColor: '#fff', color: '#333' },
|
||||
btnPrimary: { backgroundColor: '#1890ff', color: '#fff', borderColor: '#1890ff' },
|
||||
feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 },
|
||||
feeSectionTitleFirst: { marginTop: 0 },
|
||||
sectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginBottom: 12 },
|
||||
modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 720, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' },
|
||||
modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 },
|
||||
modalBody: { padding: 20, overflow: 'auto', flex: 1 },
|
||||
rentalTdCenter: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' }
|
||||
};
|
||||
|
||||
var CardBlock = function(props) {
|
||||
return React.createElement('div', { id: props.id, style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } },
|
||||
React.createElement('span', { style: styles.cardTitle }, props.title),
|
||||
React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起')
|
||||
),
|
||||
!props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null
|
||||
);
|
||||
};
|
||||
|
||||
var FormItemReadOnly = function(props) {
|
||||
var colStyle = props.fullWidth ? styles.formColFull : styles.formCol;
|
||||
return React.createElement('div', { style: colStyle },
|
||||
React.createElement('label', { style: styles.label }, props.label),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled) }, props.value || '—')
|
||||
);
|
||||
};
|
||||
|
||||
var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' };
|
||||
var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' };
|
||||
var mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', remarks: '' };
|
||||
var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }];
|
||||
var mockRentalOrders = [{ brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }, { brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '' }];
|
||||
var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' };
|
||||
var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' };
|
||||
var mockFeeTemplate = '标准费用模板A';
|
||||
var mockBillingMethod = '按自然月结算';
|
||||
var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }];
|
||||
var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }];
|
||||
var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }];
|
||||
var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }];
|
||||
var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费'));
|
||||
var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细'));
|
||||
var makeFeeRow3 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.project), React.createElement('td', { style: styles.rentalTd }, r.standard), React.createElement('td', { style: styles.rentalTd }, r.serviceFee)); };
|
||||
var makeFeeRow5 = function(r, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.rentalTd }, r.category), React.createElement('td', { style: styles.rentalTd }, r.part), React.createElement('td', { style: styles.rentalTd }, r.partName), React.createElement('td', { style: styles.rentalTd }, r.qty), React.createElement('td', { style: styles.rentalTd }, r.feeDetail)); };
|
||||
var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateCertFees.map(makeFeeRow3)));
|
||||
var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplatePenaltyFees.map(makeFeeRow3)));
|
||||
var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeTemplateConsumables.map(makeFeeRow5)));
|
||||
var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeTemplateOtherFees.map(makeFeeRow3)));
|
||||
var feeTemplateBody = React.createElement('div', null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'), feeCertTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'), feePenaltyTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'), feeConsumablesTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'), feeOtherTable
|
||||
);
|
||||
|
||||
var customerFields = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '客户名称', value: mockCustomer.name }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockCustomer.creditCode }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户地址', value: mockCustomer.address }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockCustomer.contact }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户电话', value: mockCustomer.phone }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockCustomer.email }),
|
||||
React.createElement(FormItemReadOnly, { label: '企业名称', value: mockCustomer.companyName }),
|
||||
React.createElement(FormItemReadOnly, { label: '企业电话', value: mockCustomer.companyPhone }),
|
||||
React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockCustomer.mailingAddress }),
|
||||
React.createElement(FormItemReadOnly, { label: '开户银行', value: mockCustomer.bank }),
|
||||
React.createElement(FormItemReadOnly, { label: '银行账号', value: mockCustomer.bankAccount }),
|
||||
React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockCustomer.taxId }),
|
||||
React.createElement(FormItemReadOnly, { label: '业务部门', value: '业务1部' }),
|
||||
React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' })
|
||||
);
|
||||
|
||||
var contractFormRow1 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }),
|
||||
React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }),
|
||||
React.createElement(FormItemReadOnly, { label: '合同类型', value: mockContract.contractType }),
|
||||
React.createElement(FormItemReadOnly, { label: '生效日期', value: mockContract.effectiveDate }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款方式', value: mockContract.paymentMethod }),
|
||||
React.createElement(FormItemReadOnly, { label: '主要车型', value: mockContract.mainVehicleModels }),
|
||||
React.createElement(FormItemReadOnly, { label: '结束日期', value: mockContract.endDate }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款周期', value: mockContract.paymentPeriod }),
|
||||
React.createElement(FormItemReadOnly, { label: '签约公司', value: mockContract.signingCompany }),
|
||||
React.createElement('div', { style: styles.formCol },
|
||||
React.createElement(FormItemReadOnly, { label: '交车区域', value: mockContract.deliveryRegion }),
|
||||
React.createElement('div', null,
|
||||
React.createElement('label', { style: styles.label }, '合同原件'),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) },
|
||||
contractOriginal
|
||||
? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
|
||||
React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name),
|
||||
(contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null
|
||||
)
|
||||
: '—'
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation })
|
||||
);
|
||||
var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' }));
|
||||
|
||||
var authorizedContent = React.createElement('div', null,
|
||||
React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } },
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人姓名'),
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人联系电话'),
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人身份证')
|
||||
),
|
||||
mockAuthorized.map(function(item, index) {
|
||||
return React.createElement('div', { key: index, style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } },
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.name),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.phone),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.idCard)
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
var rentalSummaryEl = React.createElement('div', { style: styles.summaryList },
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.vehicleCount + ' 辆')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalRentService + ' 元')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalDeposit + ' 元')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.hydrogenPrepay + ' 元'))
|
||||
);
|
||||
var rentalTableBody = mockRentalOrders.map(function(row, idx) {
|
||||
return React.createElement('tr', { key: idx },
|
||||
React.createElement('td', { style: styles.rentalTd }, idx + 1),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.brand)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.model)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.plateNo)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vin)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—'))
|
||||
);
|
||||
});
|
||||
var rentalTableEl = React.createElement('table', { style: styles.rentalTable },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '品牌'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '型号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 120 }, '车牌号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆识别代码'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 120 }, '车辆月租金'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null, rentalTableBody)
|
||||
);
|
||||
var hydrogenReadOnly = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '氢费承担方', value: mockHydrogen.bearer }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款方式', value: mockHydrogen.paymentMethod }),
|
||||
React.createElement(FormItemReadOnly, { label: '氢气预付款', value: mockHydrogen.hydrogenPrepay + ' 元' }),
|
||||
React.createElement(FormItemReadOnly, { label: '退还车氢气单价', value: mockHydrogen.returnPrice + ' 元' })
|
||||
);
|
||||
var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly);
|
||||
|
||||
var newVehicleColumns = [
|
||||
{ title: '序号', key: 'no', width: 60, render: function(_, __, i) { return i + 1; } },
|
||||
{ title: '品牌', key: 'brand', width: 90, render: function(_, row, i) { return React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: row.brand || undefined, onChange: function(v) { updateNewVehicleRow(i, 'brand', v || ''); }, options: brandList.map(function(b) { return { value: b, label: b }; }), allowClear: true }); } },
|
||||
{ title: '型号', key: 'model', width: 100, render: function(_, row, i) { var opts = (row.brand && modelByBrand[row.brand]) ? modelByBrand[row.brand].map(function(m) { return { value: m, label: m }; }) : []; return React.createElement(Select, { placeholder: '请选择', style: { width: '100%' }, value: row.model || undefined, onChange: function(v) { updateNewVehicleRow(i, 'model', v || ''); }, options: opts, allowClear: true, disabled: !row.brand }); } },
|
||||
{ title: '车牌号', key: 'plateNo', width: 120, render: function(_, row, i) { return React.createElement(Select, { placeholder: '请选择或输入搜索', style: { width: '100%' }, value: row.plateNo || undefined, onChange: function(v) { updateNewVehicleRow(i, 'plateNo', v || ''); }, options: plateNoOptions, allowClear: true, showSearch: true, filterOption: function(input, option) { var label = (option && option.label) ? option.label : ''; return label.toLowerCase().indexOf((input || '').toLowerCase()) >= 0; } }); } },
|
||||
{ title: '车辆识别代码', key: 'vin', width: 160, render: function(_, row, i) { return React.createElement(Input, { placeholder: '由车牌号反写', value: row.plateNo || row.vin || '', disabled: true }); } },
|
||||
{ title: '车辆月租金', key: 'monthRent', width: 120, render: function(_, row, i) { return React.createElement(Input, { placeholder: '0.00', value: row.monthRent || '', onChange: function(e) { updateNewVehicleRow(i, 'monthRent', e.target.value); }, addonAfter: '元' }); } },
|
||||
{ title: '服务费项目', key: 'serviceItems', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { openServiceModal(i); } }, '管理'); } },
|
||||
{ title: '服务费', key: 'serviceFee', width: 90, render: function(_, row, i) { return calcRowServiceFee(row) + ' 元'; } },
|
||||
{ title: '保证金', key: 'deposit', width: 100, render: function(_, row, i) { return React.createElement(Input, { placeholder: '0.00', value: row.deposit || '', onChange: function(e) { updateNewVehicleRow(i, 'deposit', e.target.value); }, addonAfter: '元' }); } },
|
||||
{ title: '备注', key: 'remark', width: 80, render: function(_, row, i) { return React.createElement(Input, { placeholder: '请输入', value: row.remark || '', onChange: function(e) { updateNewVehicleRow(i, 'remark', e.target.value); } }); } },
|
||||
{ title: '操作', key: 'action', width: 80, render: function(_, row, i) { return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeNewVehicleRow(i); } }, '删除'); } }
|
||||
];
|
||||
|
||||
var newOrderSummaryEl = React.createElement('div', { style: styles.summaryList },
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.vehicleCount + ' 辆')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.totalRentService + ' 元')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, newOrderSummary.totalDeposit + ' 元'))
|
||||
);
|
||||
|
||||
var serviceModalRows = serviceModalRowIndex !== null && newVehicleRows[serviceModalRowIndex] ? newVehicleRows[serviceModalRowIndex].serviceItems : [];
|
||||
var serviceModalContent = serviceModalRowIndex !== null ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) closeServiceModal(); } },
|
||||
React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } },
|
||||
React.createElement('div', { style: styles.modalHeader }, '服务项目'),
|
||||
React.createElement('div', { style: styles.modalBody },
|
||||
React.createElement('table', { style: styles.rentalTable },
|
||||
React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '服务项目'), React.createElement('th', { style: styles.rentalTh }, '费用'), React.createElement('th', { style: styles.rentalTh }, '生效时间'), React.createElement('th', { style: Object.assign({}, styles.rentalTh, { width: 80 }) }, '操作'))),
|
||||
React.createElement('tbody', null, serviceModalRows.map(function(si, siIdx) {
|
||||
return React.createElement('tr', { key: siIdx },
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择服务项目', value: si.project || undefined, onChange: function(v) { updateServiceItem(siIdx, 'project', v || ''); }, showSearch: true, filterOption: function(input, opt) { return opt && opt.children && String(opt.children).toLowerCase().indexOf((input || '').toLowerCase()) >= 0; }, allowClear: true }, serviceItemOptions.map(function(opt, oi) { return React.createElement(Option, { key: oi, value: opt }, opt); }))),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement(Input, { placeholder: '0.00', value: si.fee || '', onChange: function(e) { updateServiceItem(siIdx, 'fee', e.target.value); }, addonAfter: '元', style: { width: '100%' } })),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD', placeholder: '请选择生效时间', value: si.effectiveDate && window.moment ? window.moment(si.effectiveDate, 'YYYY-MM-DD') : null, onChange: function(d, dateStr) { updateServiceItem(siIdx, 'effectiveDate', dateStr || ''); } })),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function() { removeServiceItem(siIdx); } }, '删除'))
|
||||
);
|
||||
}))
|
||||
),
|
||||
React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addServiceItem }, '添加一行')
|
||||
),
|
||||
React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right', display: 'flex', gap: 12, justifyContent: 'flex-end' } },
|
||||
React.createElement(Button, { type: 'primary', onClick: function() { closeServiceModal(); } }, '保存'),
|
||||
React.createElement(Button, { onClick: closeServiceModal }, '关闭')
|
||||
)
|
||||
)
|
||||
) : null;
|
||||
|
||||
var newVehicleCardContent = React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.sectionTitle }, '新增订单'),
|
||||
newOrderSummaryEl,
|
||||
React.createElement(Table, { rowKey: function(_, i) { return String(i); }, size: 'small', columns: newVehicleColumns, dataSource: newVehicleRows, pagination: false, scroll: { x: 1100 } }),
|
||||
React.createElement(Button, { type: 'dashed', style: { marginTop: 12, width: '100%' }, onClick: addNewVehicleRow }, '添加一行')
|
||||
);
|
||||
|
||||
var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody);
|
||||
var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod));
|
||||
var attachmentContent = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '盖章合同附件')),
|
||||
React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { color: '#666', fontSize: 14 } }, '租赁合同-盖章版.pdf · 1.2 MB · 2026-02-16 14:30'))
|
||||
);
|
||||
|
||||
var changeHistorySorted = [{ changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' }];
|
||||
var historyTableRows = changeHistorySorted.map(function(row, index) {
|
||||
return React.createElement('tr', { key: index },
|
||||
React.createElement('td', { style: styles.rentalTd }, index + 1),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.changeTime),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.opType),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.operator),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.remark)
|
||||
);
|
||||
});
|
||||
var historyTable = React.createElement('table', { style: styles.rentalTable },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.rentalTh, width: 60 }, '序号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 140 }, '变更时间'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '操作类型'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 90 }, '操作人'),
|
||||
React.createElement('th', { style: styles.rentalTh }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null, historyTableRows)
|
||||
);
|
||||
var changeHistoryContent = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader }, React.createElement('span', { style: styles.cardTitle }, '合同变更历史记录')),
|
||||
React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: { overflowX: 'auto' } }, historyTable))
|
||||
);
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '新增车辆'))
|
||||
),
|
||||
React.createElement('div', { style: styles.anchorWrap },
|
||||
React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-new-vehicle'); } }, '新增车辆信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'),
|
||||
React.createElement('button', { type: 'button', style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录')
|
||||
),
|
||||
React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)),
|
||||
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))),
|
||||
React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)),
|
||||
React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)),
|
||||
React.createElement('div', { id: 'card-new-vehicle', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '新增车辆信息', collapsed: cc7, setCollapsed: setCc7 }, newVehicleCardContent)),
|
||||
React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)),
|
||||
React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)),
|
||||
React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, attachmentContent),
|
||||
React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, changeHistoryContent),
|
||||
React.createElement('div', { style: { height: 60 } }),
|
||||
serviceModalContent,
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { message.info('取消(原型)'); } }, '取消'),
|
||||
React.createElement('button', { type: 'button', style: Object.assign({}, styles.btn, styles.btnPrimary), onClick: function() { message.success('已提交审核,审核通过后生效(原型)'); } }, '提交审核')
|
||||
)
|
||||
);
|
||||
};
|
||||
448
web端/车辆租赁合同/查看租赁合同.jsx
Normal file
448
web端/车辆租赁合同/查看租赁合同.jsx
Normal file
@@ -0,0 +1,448 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名 - Axhub 产品原型
|
||||
// 车辆资产管理系统 - 查看租赁合同模块(只读 + 审批状态 + 盖章附件,IE11+ 兼容)
|
||||
|
||||
const Component = function() {
|
||||
var cc1State = React.useState(false);
|
||||
var cc1 = cc1State[0];
|
||||
var setCc1 = cc1State[1];
|
||||
var cc2State = React.useState(false);
|
||||
var cc2 = cc2State[0];
|
||||
var setCc2 = cc2State[1];
|
||||
var cc3State = React.useState(false);
|
||||
var cc3 = cc3State[0];
|
||||
var setCc3 = cc3State[1];
|
||||
var cc4State = React.useState(false);
|
||||
var cc4 = cc4State[0];
|
||||
var setCc4 = cc4State[1];
|
||||
var cc5State = React.useState(false);
|
||||
var cc5 = cc5State[0];
|
||||
var setCc5 = cc5State[1];
|
||||
var cc6State = React.useState(false);
|
||||
var cc6 = cc6State[0];
|
||||
var setCc6 = cc6State[1];
|
||||
var attachmentHoverState = React.useState(false);
|
||||
var attachmentHover = attachmentHoverState[0];
|
||||
var setAttachmentHover = attachmentHoverState[1];
|
||||
var reqSpecState = React.useState(false);
|
||||
var reqSpecOpen = reqSpecState[0];
|
||||
var setReqSpecOpen = reqSpecState[1];
|
||||
var servicePopoverRowState = React.useState(null);
|
||||
var servicePopoverRow = servicePopoverRowState[0];
|
||||
var setServicePopoverRow = servicePopoverRowState[1];
|
||||
|
||||
// 模拟已上传的盖章合同附件
|
||||
var uploadedFile = { name: '租赁合同-盖章版.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' };
|
||||
|
||||
// 合同变更历史记录(变更时间倒序,序号 1.2.3...)
|
||||
var changeHistoryRaw = [
|
||||
{ changeTime: '2026-02-16 14:00', opType: '变更内容', operator: '张三', remark: '"结束日期"由"2027-01-16"修改为"2027-02-16"' },
|
||||
{ changeTime: '2026-02-16 10:30', opType: '附加费用', operator: '李四', remark: '添加服务项目"保养费用",费用为"200",生效时间为"2026-03-01"\n添加服务项目"清洗费",费用为"80",生效时间为"2026-03-01"' },
|
||||
{ changeTime: '2026-02-15 16:20', opType: '添加授权人', operator: '王五', remark: '添加授权人"李四"' },
|
||||
{ changeTime: '2026-02-14 09:15', opType: '合同续签', operator: '赵六', remark: '原合同编码"JXZL20250210YW101100A"' },
|
||||
{ changeTime: '2026-02-13 10:00', opType: '终止合同', operator: '周九', remark: '终止原因"客户提前解约"' },
|
||||
{ changeTime: '2026-02-12 11:00', opType: '撤回合同', operator: '钱七', remark: '-' },
|
||||
{ changeTime: '2026-02-10 14:30', opType: '转正式合同', operator: '孙八', remark: '原合同编码"JXZL20260210YW101230B"' }
|
||||
];
|
||||
var changeHistorySorted = changeHistoryRaw.slice().sort(function(a, b) { return b.changeTime.localeCompare(a.changeTime); });
|
||||
|
||||
var scrollToCard = function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
anchorWrap: { position: 'fixed', top: 80, right: 24, zIndex: 100, backgroundColor: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.12)', padding: '12px 16px', minWidth: 160 },
|
||||
anchorItem: { display: 'block', padding: '6px 0', color: '#1890ff', cursor: 'pointer', border: 'none', background: 'none', width: '100%', textAlign: 'left', fontSize: 13 },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
|
||||
cardToggle: { color: '#999', fontSize: 14 },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
|
||||
formColFull: { flex: '0 0 100%', marginBottom: 8 },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
input: { width: '100%', padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14 },
|
||||
inputDisabled: { backgroundColor: '#f5f5f5', color: '#666', cursor: 'default', borderColor: '#e8e8e8' },
|
||||
summaryList: { marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 16 },
|
||||
summaryListItem: { flex: '0 0 calc(50% - 8px)', display: 'flex', alignItems: 'center', padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, boxSizing: 'border-box' },
|
||||
summaryListLabel: { flex: '0 0 140px', color: '#666' },
|
||||
summaryListValue: { flex: 1, fontWeight: 600, color: '#333' },
|
||||
rentalTable: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
||||
rentalTh: { padding: '10px 8px', textAlign: 'left', borderBottom: '1px solid #e8e8e8', backgroundColor: '#fafafa', fontWeight: 600 },
|
||||
rentalTd: { padding: '8px', borderBottom: '1px solid #f0f0f0', verticalAlign: 'middle' },
|
||||
rentalInputDisabled: { backgroundColor: '#f5f5f5', color: '#666', border: '1px solid #e8e8e8', padding: '6px 10px', borderRadius: 4, width: '100%', fontSize: 13 },
|
||||
tag: { display: 'inline-block', padding: '2px 8px', marginRight: 8, marginBottom: 4, backgroundColor: '#e6f7ff', color: '#1890ff', borderRadius: 4, fontSize: 12 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, justifyContent: 'flex-start', zIndex: 99 },
|
||||
btn: { padding: '8px 24px', borderRadius: 4, border: '1px solid #d9d9d9', cursor: 'pointer', fontSize: 14 },
|
||||
btnDefault: { backgroundColor: '#fff', color: '#333' },
|
||||
approvalCard: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
approvalCardHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600, color: '#333', textAlign: 'center' },
|
||||
approvalCardBody: { padding: '24px 20px', display: 'flex', justifyContent: 'center' },
|
||||
stepWrap: { display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap', justifyContent: 'center' },
|
||||
stepItem: { flex: '1 1 0', minWidth: 140, maxWidth: 220, textAlign: 'center', position: 'relative' },
|
||||
stepIcon: { width: 32, height: 32, borderRadius: '50%', margin: '0 auto 8px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, fontWeight: 600 },
|
||||
stepIconDone: { backgroundColor: '#52c41a', color: '#fff' },
|
||||
stepLine: { position: 'absolute', top: 16, left: '50%', right: '-50%', height: 2, backgroundColor: '#e8e8e8', zIndex: 0 },
|
||||
stepLineDone: { backgroundColor: '#52c41a' },
|
||||
stepTitle: { fontSize: 13, color: '#333', fontWeight: 500, marginBottom: 4 },
|
||||
stepDesc: { fontSize: 12, color: '#666' },
|
||||
stepStatus: { fontSize: 12, color: '#52c41a', marginTop: 4 },
|
||||
stepTime: { fontSize: 12, color: '#999', marginTop: 2 },
|
||||
attachmentRow: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 12 },
|
||||
attachmentFile: { display: 'inline-flex', alignItems: 'center', padding: '8px 12px', backgroundColor: '#f5f5f5', borderRadius: 4, border: '1px solid #d9d9d9', fontSize: 14, color: '#1890ff', cursor: 'pointer', textDecoration: 'none', fontFamily: 'inherit', margin: 0, outline: 'none' },
|
||||
attachmentFileHover: { color: '#40a9ff', borderColor: '#1890ff', backgroundColor: '#e6f7ff' },
|
||||
attachmentMeta: { fontSize: 12, color: '#999', marginLeft: 8 },
|
||||
feeSectionTitle: { fontSize: 15, fontWeight: 600, color: '#333', marginTop: 20, marginBottom: 10 },
|
||||
feeSectionTitleFirst: { marginTop: 0 },
|
||||
historyLink: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 },
|
||||
modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
modalBox: { backgroundColor: '#fff', borderRadius: 8, width: '90%', maxWidth: 640, maxHeight: '85vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', boxShadow: '0 4px 20px rgba(0,0,0,0.15)' },
|
||||
modalHeader: { padding: '16px 20px', borderBottom: '1px solid #f0f0f0', fontSize: 16, fontWeight: 600 },
|
||||
modalBody: { padding: 20, overflow: 'auto', flex: 1 }
|
||||
};
|
||||
|
||||
var CardBlock = function(props) {
|
||||
return React.createElement('div', { id: props.id, style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader, onClick: function() { props.setCollapsed(!props.collapsed); } },
|
||||
React.createElement('span', { style: styles.cardTitle }, props.title),
|
||||
React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起')
|
||||
),
|
||||
!props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null
|
||||
);
|
||||
};
|
||||
|
||||
var FormItemReadOnly = function(props) {
|
||||
var colStyle = props.fullWidth ? styles.formColFull : styles.formCol;
|
||||
return React.createElement('div', { style: colStyle },
|
||||
React.createElement('label', { style: styles.label }, props.label),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled) }, props.value || '—')
|
||||
);
|
||||
};
|
||||
|
||||
// 审批流程步骤数据(全部已通过,含审核时间 YYYY-MM-DD HH:MM)
|
||||
var approvalSteps = [
|
||||
{ title: '业务部主管', person: '姚守涛', status: '已通过', approveTime: '2026-02-16 09:30' },
|
||||
{ title: '事业部主管', person: '尚建华', status: '已通过', approveTime: '2026-02-16 10:15' },
|
||||
{ title: '财务部', person: '宋欣怡 / 吕红', status: '已通过', approveTime: '2026-02-16 11:20' },
|
||||
{ title: '法务部', person: '高洁 / 彭青松', status: '已通过', approveTime: '2026-02-16 14:00' }
|
||||
];
|
||||
|
||||
var approvalStepEls = approvalSteps.map(function(step, index) {
|
||||
var isLast = index === approvalSteps.length - 1;
|
||||
var lineStyle = Object.assign({}, styles.stepLine, step.status === '已通过' ? styles.stepLineDone : {});
|
||||
if (isLast) lineStyle.display = 'none';
|
||||
return React.createElement('div', { key: index, style: Object.assign({}, styles.stepItem, { zIndex: approvalSteps.length - index }) },
|
||||
!isLast ? React.createElement('div', { style: lineStyle }) : null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.stepIcon, styles.stepIconDone) }, '✓'),
|
||||
React.createElement('div', { style: styles.stepTitle }, step.title),
|
||||
React.createElement('div', { style: styles.stepDesc }, step.person),
|
||||
React.createElement('div', { style: styles.stepStatus }, step.status),
|
||||
step.approveTime ? React.createElement('div', { style: styles.stepTime }, step.approveTime) : null
|
||||
);
|
||||
});
|
||||
|
||||
var approvalCardEl = React.createElement('div', { style: styles.approvalCard },
|
||||
React.createElement('div', { style: styles.approvalCardHeader }, '审批状态'),
|
||||
React.createElement('div', { style: styles.approvalCardBody },
|
||||
React.createElement('div', { style: styles.stepWrap }, approvalStepEls)
|
||||
)
|
||||
);
|
||||
|
||||
// 模拟只读数据(与新增合同同结构)
|
||||
var mockCustomer = { name: '嘉兴某某物流有限公司', creditCode: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', contact: '张三', phone: '13800138001', email: 'zhangsan@example.com', companyName: '嘉兴某某物流有限公司', companyPhone: '0571-88888888', mailingAddress: '浙江省嘉兴市南湖区科技大道1号', bank: '中国工商银行嘉兴分行', bankAccount: '6222021234567890123', taxId: '91330400MA2XXXXX1' };
|
||||
var contractOriginal = { name: '租赁合同-原件.pdf', size: '1.2 MB', uploadTime: '2026-02-16 14:30' };
|
||||
var mockContract = { projectName: '嘉兴氢能运输项目', contractCode: 'JXZL20260216YW101235A', contractType: '正式合同', effectiveDate: '2026-02-16', paymentMethod: '预付', mainVehicleModels: '型号A1、型号A2', endDate: '2027-02-16', paymentPeriod: '1个月', signingCompany: '嘉兴羚牛', deliveryRegion: '浙江省 / 嘉兴市', deliveryLocation: '嘉兴市南湖区科技大道1号', remarks: '' };
|
||||
var mockAuthorized = [{ name: '张三', phone: '13800138001', idCard: '330102199001011234' }];
|
||||
var mockRentalOrders = [
|
||||
{ brand: '品牌A', model: '型号A1', plateNo: '浙A10001', vin: 'L1234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '保养费用', fee: '200', effectiveDate: '2026-03-01' }, { project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] },
|
||||
{ brand: '品牌A', model: '型号A2', plateNo: '浙B20002', vin: 'L2234567890ABCDEF', monthRent: '8000', serviceFee: '500', deposit: '10000', remark: '', serviceItems: [{ project: '清洗费', fee: '80', effectiveDate: '2026-03-01' }] }
|
||||
];
|
||||
var mockRentalSummary = { vehicleCount: 2, totalRentService: '17000.00', totalDeposit: '20000.00', hydrogenPrepay: '5000.00' };
|
||||
var mockHydrogen = { bearer: '客户', paymentMethod: '预付', hydrogenPrepay: '5000', returnPrice: '80' };
|
||||
var mockFeeTemplate = '标准费用模板A';
|
||||
var mockBillingMethod = '按自然月结算';
|
||||
var feeTemplateCertFees = [{ project: '补办行驶证', standard: '50元/次', serviceFee: '20' }, { project: '补办驾驶证', standard: '30元/次', serviceFee: '10' }, { project: '补办牌照', standard: '100元/次', serviceFee: '50' }];
|
||||
var feeTemplatePenaltyFees = [{ project: '提前退车违约金', standard: '月租金×1', serviceFee: '0' }, { project: '违章处理违约金', standard: '按实际发生', serviceFee: '50' }];
|
||||
var feeTemplateConsumables = [{ category: '轮胎', part: '前轮', partName: '轮胎A型', qty: 1, feeDetail: '500.00' }, { category: '易损件', part: '雨刮', partName: '雨刮片', qty: 2, feeDetail: '80.00' }];
|
||||
var feeTemplateOtherFees = [{ project: '上门送车费', standard: '100元/次', serviceFee: '50' }, { project: '上门收车费', standard: '100元/次', serviceFee: '50' }, { project: '清洗费', standard: '80元/次', serviceFee: '30' }];
|
||||
var feeTableHeader3 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '项目'), React.createElement('th', { style: styles.rentalTh }, '收费标准'), React.createElement('th', { style: styles.rentalTh }, '服务费'));
|
||||
var feeTableHeader5 = React.createElement('tr', null, React.createElement('th', { style: styles.rentalTh }, '类别'), React.createElement('th', { style: styles.rentalTh }, '损坏部位'), React.createElement('th', { style: styles.rentalTh }, '配件'), React.createElement('th', { style: styles.rentalTh }, '数量'), React.createElement('th', { style: styles.rentalTh }, '费用明细'));
|
||||
var makeFeeRow3 = function(r, i) {
|
||||
var td1 = React.createElement('td', { style: styles.rentalTd }, r.project);
|
||||
var td2 = React.createElement('td', { style: styles.rentalTd }, r.standard);
|
||||
var td3 = React.createElement('td', { style: styles.rentalTd }, r.serviceFee);
|
||||
return React.createElement('tr', { key: i }, td1, td2, td3);
|
||||
};
|
||||
var makeFeeRow5 = function(r, i) {
|
||||
var td1 = React.createElement('td', { style: styles.rentalTd }, r.category);
|
||||
var td2 = React.createElement('td', { style: styles.rentalTd }, r.part);
|
||||
var td3 = React.createElement('td', { style: styles.rentalTd }, r.partName);
|
||||
var td4 = React.createElement('td', { style: styles.rentalTd }, r.qty);
|
||||
var td5 = React.createElement('td', { style: styles.rentalTd }, r.feeDetail);
|
||||
return React.createElement('tr', { key: i }, td1, td2, td3, td4, td5);
|
||||
};
|
||||
var feeCertRows = feeTemplateCertFees.map(makeFeeRow3);
|
||||
var feePenaltyRows = feeTemplatePenaltyFees.map(makeFeeRow3);
|
||||
var feeConsumablesRows = feeTemplateConsumables.map(makeFeeRow5);
|
||||
var feeOtherRows = feeTemplateOtherFees.map(makeFeeRow3);
|
||||
var feeCertTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeCertRows));
|
||||
var feePenaltyTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feePenaltyRows));
|
||||
var feeConsumablesTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader5), React.createElement('tbody', null, feeConsumablesRows));
|
||||
var feeOtherTable = React.createElement('table', { style: styles.rentalTable }, React.createElement('thead', null, feeTableHeader3), React.createElement('tbody', null, feeOtherRows));
|
||||
var feeTemplateBody = React.createElement('div', null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.feeSectionTitle, styles.feeSectionTitleFirst) }, '证照补办费用'),
|
||||
feeCertTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '违约金费用'),
|
||||
feePenaltyTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '易损件信息'),
|
||||
feeConsumablesTable,
|
||||
React.createElement('div', { style: styles.feeSectionTitle }, '其他费用信息'),
|
||||
feeOtherTable
|
||||
);
|
||||
|
||||
var customerFields = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '客户名称', value: mockCustomer.name }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户统一信用代码', value: mockCustomer.creditCode }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户地址', value: mockCustomer.address }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户联系人', value: mockCustomer.contact }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户电话', value: mockCustomer.phone }),
|
||||
React.createElement(FormItemReadOnly, { label: '客户电子邮箱', value: mockCustomer.email }),
|
||||
React.createElement(FormItemReadOnly, { label: '企业名称', value: mockCustomer.companyName }),
|
||||
React.createElement(FormItemReadOnly, { label: '企业电话', value: mockCustomer.companyPhone }),
|
||||
React.createElement(FormItemReadOnly, { label: '邮寄地址', value: mockCustomer.mailingAddress }),
|
||||
React.createElement(FormItemReadOnly, { label: '开户银行', value: mockCustomer.bank }),
|
||||
React.createElement(FormItemReadOnly, { label: '银行账号', value: mockCustomer.bankAccount }),
|
||||
React.createElement(FormItemReadOnly, { label: '纳税人识别号', value: mockCustomer.taxId }),
|
||||
React.createElement(FormItemReadOnly, { label: '业务部门', value: '业务1部' }),
|
||||
React.createElement(FormItemReadOnly, { label: '业务负责人', value: '张经理' })
|
||||
);
|
||||
|
||||
var contractFormRow1 = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '项目名称', value: mockContract.projectName }),
|
||||
React.createElement(FormItemReadOnly, { label: '合同编码', value: mockContract.contractCode }),
|
||||
React.createElement(FormItemReadOnly, { label: '合同类型', value: mockContract.contractType }),
|
||||
React.createElement(FormItemReadOnly, { label: '生效日期', value: mockContract.effectiveDate }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款方式', value: mockContract.paymentMethod }),
|
||||
React.createElement(FormItemReadOnly, { label: '主要车型', value: mockContract.mainVehicleModels }),
|
||||
React.createElement(FormItemReadOnly, { label: '结束日期', value: mockContract.endDate }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款周期', value: mockContract.paymentPeriod }),
|
||||
React.createElement(FormItemReadOnly, { label: '签约公司', value: mockContract.signingCompany }),
|
||||
React.createElement('div', { style: styles.formCol },
|
||||
React.createElement(FormItemReadOnly, { label: '交车区域', value: mockContract.deliveryRegion }),
|
||||
React.createElement('div', null,
|
||||
React.createElement('label', { style: styles.label }, '合同原件'),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { padding: '8px 12px' }) },
|
||||
contractOriginal
|
||||
? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
|
||||
React.createElement('a', { href: '#', style: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' }, onClick: function(e) { e.preventDefault(); window.open('#', '_blank'); } }, contractOriginal.name),
|
||||
(contractOriginal.size || contractOriginal.uploadTime) ? React.createElement('span', { style: { color: '#999', fontSize: 12 } }, (contractOriginal.size || '') + (contractOriginal.size && contractOriginal.uploadTime ? ' · ' : '') + (contractOriginal.uploadTime || '')) : null
|
||||
)
|
||||
: '—'
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(FormItemReadOnly, { label: '交车地点', value: mockContract.deliveryLocation })
|
||||
);
|
||||
var contractFormRow2 = React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '备注', fullWidth: true, value: mockContract.remarks || '—' }));
|
||||
|
||||
var authorizedContent = React.createElement('div', null,
|
||||
React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 8 } },
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人姓名'),
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人联系电话'),
|
||||
React.createElement('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, '被授权人身份证')
|
||||
),
|
||||
mockAuthorized.map(function(item, index) {
|
||||
return React.createElement('div', { key: index, style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } },
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.name),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.phone),
|
||||
React.createElement('div', { style: Object.assign({}, styles.input, styles.inputDisabled, { flex: 1 }) }, item.idCard)
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
var rentalSummaryEl = React.createElement('div', { style: styles.summaryList },
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租赁车辆数'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.vehicleCount + ' 辆')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '租金及服务费合计'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalRentService + ' 元')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '保证金总额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.totalDeposit + ' 元')),
|
||||
React.createElement('div', { style: styles.summaryListItem }, React.createElement('span', { style: styles.summaryListLabel }, '氢气预付款金额'), React.createElement('span', { style: styles.summaryListValue }, mockRentalSummary.hydrogenPrepay + ' 元'))
|
||||
);
|
||||
var Popover = window.antd && window.antd.Popover;
|
||||
var renderServiceItemsPopover = function(row) {
|
||||
var items = row.serviceItems || [];
|
||||
if (items.length === 0) {
|
||||
return React.createElement('div', { style: { padding: 12, minWidth: 320, fontSize: 13 } }, '暂无服务项明细');
|
||||
}
|
||||
var thead = React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.rentalTh }, '服务项目'),
|
||||
React.createElement('th', { style: styles.rentalTh }, '费用'),
|
||||
React.createElement('th', { style: styles.rentalTh }, '生效时间')
|
||||
)
|
||||
);
|
||||
var tbody = React.createElement('tbody', null,
|
||||
items.map(function(si, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: styles.rentalTd }, si.project || '—'),
|
||||
React.createElement('td', { style: styles.rentalTd }, si.fee != null && si.fee !== '' ? si.fee + ' 元' : '—'),
|
||||
React.createElement('td', { style: styles.rentalTd }, si.effectiveDate || '—')
|
||||
);
|
||||
})
|
||||
);
|
||||
return React.createElement('div', { style: { padding: 8 } },
|
||||
React.createElement('table', { style: styles.rentalTable }, thead, tbody)
|
||||
);
|
||||
};
|
||||
var rentalTableBody = mockRentalOrders.map(function(row, idx) {
|
||||
var servicePopoverContent = renderServiceItemsPopover(row);
|
||||
var serviceCell = Popover
|
||||
? React.createElement(Popover, { content: servicePopoverContent, title: '服务项明细', trigger: 'click' },
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 } }, '查看'))
|
||||
: React.createElement('span', {
|
||||
style: { color: '#1890ff', cursor: 'pointer', fontSize: 13 },
|
||||
onClick: function() { setServicePopoverRow(servicePopoverRow === idx ? null : idx); }
|
||||
}, '查看');
|
||||
return React.createElement('tr', { key: idx },
|
||||
React.createElement('td', { style: styles.rentalTd }, idx + 1),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.brand)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.model)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.plateNo)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.vin)),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.monthRent + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, serviceCell),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.serviceFee + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.deposit + ' 元')),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('div', { style: styles.rentalInputDisabled }, row.remark || '—'))
|
||||
);
|
||||
});
|
||||
var rentalTableEl = React.createElement('table', { style: styles.rentalTable },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.rentalTh, width: 50 }, '序号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '品牌'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '型号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 120 }, '车牌号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 160 }, '车辆识别代码'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 120 }, '车辆月租金'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 80 }, '服务费项目'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 90 }, '服务费'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '保证金'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 80 }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null, rentalTableBody)
|
||||
);
|
||||
var hydrogenReadOnly = React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItemReadOnly, { label: '氢费承担方', value: mockHydrogen.bearer }),
|
||||
React.createElement(FormItemReadOnly, { label: '付款方式', value: mockHydrogen.paymentMethod }),
|
||||
React.createElement(FormItemReadOnly, { label: '氢气预付款', value: mockHydrogen.hydrogenPrepay + ' 元' }),
|
||||
React.createElement(FormItemReadOnly, { label: '退还车氢气单价', value: mockHydrogen.returnPrice + ' 元' })
|
||||
);
|
||||
var rentalContent = React.createElement('div', null, rentalSummaryEl, React.createElement('div', { style: { overflowX: 'auto', marginBottom: 16 } }, rentalTableEl), hydrogenReadOnly);
|
||||
|
||||
var feeContent = React.createElement('div', null, React.createElement('div', { style: styles.formRow }, React.createElement(FormItemReadOnly, { label: '选择费用模板', value: mockFeeTemplate })), feeTemplateBody);
|
||||
|
||||
var billingContent = React.createElement('div', null, React.createElement('div', { style: { padding: '12px 16px', border: '1px solid #e8e8e8', borderRadius: 4, backgroundColor: '#fafafa', fontSize: 14, color: '#333' } }, mockBillingMethod));
|
||||
|
||||
var attachmentContent = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader },
|
||||
React.createElement('span', { style: styles.cardTitle }, '盖章合同附件')
|
||||
),
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
React.createElement('div', { style: styles.attachmentRow },
|
||||
React.createElement('button', {
|
||||
type: 'button',
|
||||
style: Object.assign({}, styles.attachmentFile, attachmentHover ? styles.attachmentFileHover : {}),
|
||||
onMouseEnter: function() { setAttachmentHover(true); },
|
||||
onMouseLeave: function() { setAttachmentHover(false); },
|
||||
onClick: function() { window.open('#', '_blank'); }
|
||||
}, '📄 ' + uploadedFile.name, React.createElement('span', { style: styles.attachmentMeta }, uploadedFile.size + ' · ' + uploadedFile.uploadTime))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
var historyTableRows = changeHistorySorted.map(function(row, index) {
|
||||
return React.createElement('tr', { key: index },
|
||||
React.createElement('td', { style: styles.rentalTd }, index + 1),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.changeTime),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.opType),
|
||||
React.createElement('td', { style: styles.rentalTd }, row.operator),
|
||||
React.createElement('td', { style: styles.rentalTd }, React.createElement('button', { type: 'button', style: styles.historyLink, onClick: function() { window.open('#', '_blank'); } }, '查看变更前记录')),
|
||||
React.createElement('td', { style: Object.assign({}, styles.rentalTd, { verticalAlign: 'top', whiteSpace: 'pre-line' }) }, row.remark)
|
||||
);
|
||||
});
|
||||
var historyTable = React.createElement('table', { style: styles.rentalTable },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: styles.rentalTh, width: 60 }, '序号'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 140 }, '变更时间'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 100 }, '操作类型'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 90 }, '操作人'),
|
||||
React.createElement('th', { style: styles.rentalTh, width: 120 }, '原始记录'),
|
||||
React.createElement('th', { style: styles.rentalTh }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null, historyTableRows)
|
||||
);
|
||||
var changeHistoryContent = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardHeader },
|
||||
React.createElement('span', { style: styles.cardTitle }, '合同变更历史记录')
|
||||
),
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
React.createElement('div', { style: { overflowX: 'auto' } }, historyTable)
|
||||
)
|
||||
);
|
||||
|
||||
var reqSpecBlock = { marginBottom: 8 };
|
||||
var reqSpecH2 = { fontSize: 14, fontWeight: 600, marginTop: 16, marginBottom: 8, color: '#333' };
|
||||
var reqSpecH2First = { marginTop: 0 };
|
||||
var reqSpecP = { fontSize: 13, lineHeight: 1.6, marginBottom: 6, color: '#555' };
|
||||
var reqSpecLi = { fontSize: 13, lineHeight: 1.6, marginBottom: 4, marginLeft: 20, color: '#555' };
|
||||
var reqSpecDoc = React.createElement('div', { style: { padding: '0 4px' } },
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: Object.assign({}, reqSpecH2, reqSpecH2First) }, '1.其他部分与新增租赁合同相同,只是不可编辑,新增以下几块内容:')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '2.审批状态卡片:'), React.createElement('div', { style: reqSpecP }, '显示所有审批步骤及当前节点、审批人姓名、已通过/待审批/驳回三种状态;最下方为对应审批节点审批操作时间,格式为 YYYY-MM-DD HH:MM。')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '3.盖章合同附件:'), React.createElement('div', { style: reqSpecP }, '当法务未完成审核并上传盖章合同时,盖章合同附件显示为暂无附件;已上传盖章合同时,显示合同文件名称、文件大小、上传时间(YYYY-MM-DD HH:MM)。')),
|
||||
React.createElement('div', { style: reqSpecBlock }, React.createElement('div', { style: reqSpecH2 }, '4.合同变更历史记录:'), React.createElement('div', { style: reqSpecP }, '列表字段为序号、变更时间、操作类型、操作人、原始记录、备注。'), React.createElement('div', { style: reqSpecLi }, '4.1.序号:1.2.3...以此类推;'), React.createElement('div', { style: reqSpecLi }, '4.2.变更时间:YYYY-MM-DD HH:MM 格式,倒序展示数据;'), React.createElement('div', { style: reqSpecLi }, '4.3.操作类型:包括变更内容、合同续签、撤回合同、添加授权人、附加费用、转正式合同、终止合同;'), React.createElement('div', { style: reqSpecLi }, '4.4.操作人:记录对应操作用户名称;'), React.createElement('div', { style: reqSpecLi }, '4.5.原始记录:显示查看变更前记录,点击会新开页查看未修改前合同原始记录;'), React.createElement('div', { style: reqSpecLi }, '4.6.备注:'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.1.操作类型为变更内容时,显示:"字段名"由"xx"修改为"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.2.操作类型为合同续签时,显示:原合同编码"xxxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.3.操作类型为撤回合同时,显示:-;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.4.操作类型为添加授权人时,显示:添加授权人"xxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.5.操作类型为附加费用时,显示:添加服务项目"xxxxxx",费用为"xxxxxxx",生效时间为"YYYY-MM-DD",如果有多条附加费用,支持多行显示;'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.6.操作类型为转正式合同时,显示:原合同编码"xxxxxxxx";'), React.createElement('div', { style: Object.assign({}, reqSpecLi, { marginLeft: 36 }) }, '4.6.7.操作类型为终止合同时,显示:终止原因"xxxxxx"。'))
|
||||
);
|
||||
var reqSpecModalContent = reqSpecOpen ? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setReqSpecOpen(false); } }, React.createElement('div', { style: styles.modalBox, onClick: function(e) { e.stopPropagation(); } }, React.createElement('div', { style: styles.modalHeader }, '需求说明'), React.createElement('div', { style: Object.assign({}, styles.modalBody, { maxHeight: '70vh', padding: '20px 24px' }) }, reqSpecDoc), React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setReqSpecOpen(false); } }, '关闭')))) : null;
|
||||
|
||||
var servicePopoverContentCustom = !Popover && servicePopoverRow !== null && mockRentalOrders[servicePopoverRow]
|
||||
? React.createElement('div', { style: styles.modalMask, onClick: function(e) { if (e.target === e.currentTarget) setServicePopoverRow(null); } },
|
||||
React.createElement('div', { style: Object.assign({}, styles.modalBox, { maxWidth: 480 }), onClick: function(e) { e.stopPropagation(); } },
|
||||
React.createElement('div', { style: styles.modalHeader }, '服务项明细'),
|
||||
React.createElement('div', { style: Object.assign({}, styles.modalBody, { padding: '16px 20px' }) }, renderServiceItemsPopover(mockRentalOrders[servicePopoverRow])),
|
||||
React.createElement('div', { style: { padding: '12px 20px', borderTop: '1px solid #f0f0f0', textAlign: 'right' } }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault), onClick: function() { setServicePopoverRow(null); } }, '关闭'))
|
||||
))
|
||||
: null;
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
servicePopoverContentCustom,
|
||||
React.createElement('div', { style: { marginBottom: 16 } }, React.createElement('div', { style: styles.breadcrumb }, React.createElement('span', null, '业务管理'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', null, '车辆租赁合同'), React.createElement('span', { style: styles.breadcrumbSep }, ' / '), React.createElement('span', { style: { color: '#1890ff' } }, '查看租赁合同'))),
|
||||
approvalCardEl,
|
||||
React.createElement('div', { style: styles.anchorWrap },
|
||||
React.createElement('div', { style: { marginBottom: 8, fontWeight: 600, fontSize: 14 } }, '锚点导航'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-customer'); } }, '客户基本信息'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-contract'); } }, '合同基本信息'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-authorized'); } }, '被授权人信息'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-rental'); } }, '租赁订单信息'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-fee'); } }, '其他费用信息'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-billing'); } }, '账单计算方式'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-attachment'); } }, '盖章合同附件'),
|
||||
React.createElement('button', { style: styles.anchorItem, onClick: function() { scrollToCard('card-history'); } }, '合同变更历史记录')
|
||||
),
|
||||
React.createElement('div', { id: 'card-customer' }, React.createElement(CardBlock, { id: 'card-customer', title: '客户基本信息', collapsed: cc1, setCollapsed: setCc1 }, customerFields)),
|
||||
React.createElement('div', { id: 'card-contract', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '合同基本信息', collapsed: cc2, setCollapsed: setCc2 }, React.createElement('div', null, contractFormRow1, contractFormRow2))),
|
||||
React.createElement('div', { id: 'card-authorized', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '被授权人信息', collapsed: cc3, setCollapsed: setCc3 }, authorizedContent)),
|
||||
React.createElement('div', { id: 'card-rental', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '租赁订单信息', collapsed: cc4, setCollapsed: setCc4 }, rentalContent)),
|
||||
React.createElement('div', { id: 'card-fee', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '其他费用信息', collapsed: cc5, setCollapsed: setCc5 }, feeContent)),
|
||||
React.createElement('div', { id: 'card-billing', style: { marginTop: 16 } }, React.createElement(CardBlock, { title: '账单计算方式', collapsed: cc6, setCollapsed: setCc6 }, billingContent)),
|
||||
React.createElement('div', { id: 'card-attachment', style: { marginTop: 16 } }, attachmentContent),
|
||||
React.createElement('div', { id: 'card-history', style: { marginTop: 16 } }, changeHistoryContent),
|
||||
React.createElement('div', { style: { height: 60 } }),
|
||||
reqSpecModalContent,
|
||||
React.createElement('div', { style: styles.footer }, React.createElement('button', { style: Object.assign({}, styles.btn, styles.btnDefault) }, '返回'))
|
||||
);
|
||||
};
|
||||
1064
web端/车辆租赁合同/车辆租赁合同.jsx
Normal file
1064
web端/车辆租赁合同/车辆租赁合同.jsx
Normal file
File diff suppressed because it is too large
Load Diff
690
web端/车辆租赁合同/转正式合同.jsx
Normal file
690
web端/车辆租赁合同/转正式合同.jsx
Normal file
File diff suppressed because one or more lines are too long
591
web端/车辆管理.jsx
Normal file
591
web端/车辆管理.jsx
Normal file
File diff suppressed because one or more lines are too long
BIN
web端/运维管理/.DS_Store
vendored
Normal file
BIN
web端/运维管理/.DS_Store
vendored
Normal file
Binary file not shown.
1319
web端/运维管理/基本数据维护/停车场管理.jsx
Normal file
1319
web端/运维管理/基本数据维护/停车场管理.jsx
Normal file
File diff suppressed because it is too large
Load Diff
938
web端/运维管理/车辆业务/上牌管理.jsx
Executable file
938
web端/运维管理/车辆业务/上牌管理.jsx
Executable file
@@ -0,0 +1,938 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 车辆上牌管理 - 车辆资产管理后台模块
|
||||
var ARCO_TOKEN = {
|
||||
primary: '#165DFF',
|
||||
primaryHover: '#4080FF',
|
||||
danger: '#F53F3F',
|
||||
success: '#00B42A',
|
||||
neutral1: '#FFFFFF',
|
||||
neutral2: '#F7F8FA',
|
||||
neutral3: '#F2F3F5',
|
||||
neutral4: '#E5E6EB',
|
||||
neutral5: '#C9CDD4',
|
||||
neutral6: '#86909C',
|
||||
neutral7: '#4E5969',
|
||||
neutral8: '#1D2129',
|
||||
border: '#E5E6EB',
|
||||
fill: '#F2F3F5',
|
||||
fillSecondary: '#F7F8FA',
|
||||
shadowLight: '0 1px 2px rgba(0,0,0,0.05)',
|
||||
shadowMedium: '0 2px 8px rgba(0,0,0,0.08)',
|
||||
radiusSmall: '2px',
|
||||
radiusMedium: '4px',
|
||||
radiusLarge: '8px',
|
||||
spacing8: '8px',
|
||||
spacing12: '12px',
|
||||
spacing16: '16px',
|
||||
spacing24: '24px',
|
||||
fontSize14: '14px',
|
||||
fontSize16: '16px',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif',
|
||||
link: '#165DFF'
|
||||
};
|
||||
|
||||
const Component = function () {
|
||||
var antd = window.antd;
|
||||
var Input = antd.Input;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Table = antd.Table;
|
||||
var Modal = antd.Modal;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
var Spin = antd.Spin;
|
||||
|
||||
var _useState = React.useState('');
|
||||
var filterDateStart = _useState[0];
|
||||
var setFilterDateStart = _useState[1];
|
||||
|
||||
var _useState2 = React.useState('');
|
||||
var filterDateEnd = _useState2[0];
|
||||
var setFilterDateEnd = _useState2[1];
|
||||
|
||||
var _useState3 = React.useState('');
|
||||
var filterOperator = _useState3[0];
|
||||
var setFilterOperator = _useState3[1];
|
||||
|
||||
var _useState4 = React.useState('');
|
||||
var filterPlateNo = _useState4[0];
|
||||
var setFilterPlateNo = _useState4[1];
|
||||
|
||||
var _useState5 = React.useState('');
|
||||
var filterVin = _useState5[0];
|
||||
var setFilterVin = _useState5[1];
|
||||
|
||||
var _useState5h = React.useState('');
|
||||
var appliedDateStart = _useState5h[0];
|
||||
var setAppliedDateStart = _useState5h[1];
|
||||
var _useState5i = React.useState('');
|
||||
var appliedDateEnd = _useState5i[0];
|
||||
var setAppliedDateEnd = _useState5i[1];
|
||||
var _useState5j = React.useState('');
|
||||
var appliedOperator = _useState5j[0];
|
||||
var setAppliedOperator = _useState5j[1];
|
||||
var _useState5k = React.useState('');
|
||||
var appliedPlateNo = _useState5k[0];
|
||||
var setAppliedPlateNo = _useState5k[1];
|
||||
var _useState5l = React.useState('');
|
||||
var appliedVin = _useState5l[0];
|
||||
var setAppliedVin = _useState5l[1];
|
||||
|
||||
var _useState6 = React.useState(1);
|
||||
var currentPage = _useState6[0];
|
||||
var setCurrentPage = _useState6[1];
|
||||
|
||||
var _useState7 = React.useState(10);
|
||||
var pageSize = _useState7[0];
|
||||
var setPageSize = _useState7[1];
|
||||
|
||||
var _useState9 = React.useState(null);
|
||||
var viewPhotoRecord = _useState9[0];
|
||||
var setViewPhotoRecord = _useState9[1];
|
||||
|
||||
var _useState10 = React.useState(false);
|
||||
var ocrModalVisible = _useState10[0];
|
||||
var setOcrModalVisible = _useState10[1];
|
||||
|
||||
var _useState11 = React.useState(false);
|
||||
var confirmModalVisible = _useState11[0];
|
||||
var setConfirmModalVisible = _useState11[1];
|
||||
|
||||
var _useState12 = React.useState(null);
|
||||
var confirmData = _useState12[0];
|
||||
var setConfirmData = _useState12[1];
|
||||
|
||||
var _useState13 = React.useState([]);
|
||||
var batchConfirmList = _useState13[0];
|
||||
var setBatchConfirmList = _useState13[1];
|
||||
|
||||
var _useState14 = React.useState(0);
|
||||
var batchConfirmIndex = _useState14[0];
|
||||
var setBatchConfirmIndex = _useState14[1];
|
||||
|
||||
var _useState15 = React.useState(false);
|
||||
var isBatchMode = _useState15[0];
|
||||
var setIsBatchMode = _useState15[1];
|
||||
|
||||
var _useState15b = React.useState(0);
|
||||
var batchTotalCount = _useState15b[0];
|
||||
var setBatchTotalCount = _useState15b[1];
|
||||
|
||||
var _useState16 = React.useState('');
|
||||
var confirmVin = _useState16[0];
|
||||
var setConfirmVin = _useState16[1];
|
||||
|
||||
var _useState17 = React.useState('');
|
||||
var confirmPlateNo = _useState17[0];
|
||||
var setConfirmPlateNo = _useState17[1];
|
||||
|
||||
var _useState17a = React.useState('');
|
||||
var confirmScrapDate = _useState17a[0];
|
||||
var setConfirmScrapDate = _useState17a[1];
|
||||
|
||||
var _useState17b = React.useState('');
|
||||
var confirmInspectionExpiry = _useState17b[0];
|
||||
var setConfirmInspectionExpiry = _useState17b[1];
|
||||
|
||||
var _useState18b = React.useState(false);
|
||||
var showRequirementModal = _useState18b[0];
|
||||
var setShowRequirementModal = _useState18b[1];
|
||||
|
||||
var _useState18c = React.useState(false);
|
||||
var batchTaskCardVisible = _useState18c[0];
|
||||
var setBatchTaskCardVisible = _useState18c[1];
|
||||
|
||||
var fileInputRef = React.useRef(null);
|
||||
var batchFullListRef = React.useRef(null);
|
||||
var batchUploadFromCardRef = React.useRef(false);
|
||||
var batchFileInputRef = React.useRef(null);
|
||||
|
||||
var _useState18e = React.useState([]);
|
||||
var batchTaskList = _useState18e[0];
|
||||
var setBatchTaskList = _useState18e[1];
|
||||
|
||||
var mockVehicleList = [
|
||||
{ id: 'v001', frameNo: 'LGW123456', brand: '比亚迪', model: '秦', vehicleType: '轿车' },
|
||||
{ id: 'v002', frameNo: 'LGW789012', brand: '特斯拉', model: 'Model 3', vehicleType: '轿车' },
|
||||
{ id: 'v003', frameNo: 'HZ111222', brand: '小鹏', model: 'P7', vehicleType: '轿车' }
|
||||
];
|
||||
|
||||
var initialRecordList = [
|
||||
{
|
||||
id: 'r001',
|
||||
plateDate: '2025-02-01',
|
||||
operator: '张明',
|
||||
plateNo: '粤A12345',
|
||||
vin: 'LGW123456',
|
||||
vehicleType: '轿车',
|
||||
brand: '比亚迪',
|
||||
model: '秦',
|
||||
photoUrl: 'https://picsum.photos/300/200?random=1'
|
||||
},
|
||||
{
|
||||
id: 'r002',
|
||||
plateDate: '2025-02-03',
|
||||
operator: '王芳',
|
||||
plateNo: '粤A67890',
|
||||
vin: 'LGW789012',
|
||||
vehicleType: '轿车',
|
||||
brand: '特斯拉',
|
||||
model: 'Model 3',
|
||||
photoUrl: 'https://picsum.photos/300/200?random=2'
|
||||
}
|
||||
];
|
||||
|
||||
// 近5条批量上牌任务(时间、照片数量、完成进度);进度100%时有 items 用于「识别」进入确认界面
|
||||
var getInitialBatchTaskList = function () {
|
||||
return [
|
||||
{ id: 'bt1', createTime: '2025-02-12 10:30', photoCount: 3, progress: 100, items: [
|
||||
{ photoUrl: 'https://picsum.photos/300/200?random=b1', vin: 'LGW123456', plateNo: '粤A11111', vehicle: mockVehicleList[0] },
|
||||
{ photoUrl: 'https://picsum.photos/300/200?random=b2', vin: 'LGW789012', plateNo: '粤A22222', vehicle: mockVehicleList[1] },
|
||||
{ photoUrl: 'https://picsum.photos/300/200?random=b3', vin: 'HZ111222', plateNo: '粤A33333', vehicle: mockVehicleList[2] }
|
||||
]},
|
||||
{ id: 'bt2', createTime: '2025-02-12 09:15', photoCount: 5, progress: 100, items: [
|
||||
{ photoUrl: 'https://picsum.photos/300/200?random=b4', vin: 'LGW123456', plateNo: '粤A44444', vehicle: mockVehicleList[0] },
|
||||
{ photoUrl: 'https://picsum.photos/300/200?random=b5', vin: 'LGW789012', plateNo: '粤A55555', vehicle: mockVehicleList[1] }
|
||||
]},
|
||||
{ id: 'bt3', createTime: '2025-02-11 16:20', photoCount: 4, progress: 80, items: null },
|
||||
{ id: 'bt4', createTime: '2025-02-11 14:00', photoCount: 2, progress: 50, items: null },
|
||||
{ id: 'bt5', createTime: '2025-02-10 11:30', photoCount: 6, progress: 30, items: null }
|
||||
];
|
||||
};
|
||||
|
||||
React.useEffect(function () {
|
||||
setBatchTaskList(function (prev) {
|
||||
if (prev.length === 0) return getInitialBatchTaskList();
|
||||
return prev;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var _useState18 = React.useState(initialRecordList);
|
||||
var recordList = _useState18[0];
|
||||
var setRecordList = _useState18[1];
|
||||
|
||||
var getUniqueOperators = function () {
|
||||
var seen = {};
|
||||
var list = [];
|
||||
recordList.forEach(function (r) {
|
||||
if (r.operator && !seen[r.operator]) {
|
||||
seen[r.operator] = true;
|
||||
list.push(r.operator);
|
||||
}
|
||||
});
|
||||
return list.sort();
|
||||
};
|
||||
|
||||
var getUniquePlateNos = function () {
|
||||
var seen = {};
|
||||
var list = [];
|
||||
recordList.forEach(function (r) {
|
||||
if (r.plateNo && !seen[r.plateNo]) {
|
||||
seen[r.plateNo] = true;
|
||||
list.push(r.plateNo);
|
||||
}
|
||||
});
|
||||
return list.sort();
|
||||
};
|
||||
|
||||
var getUniqueVins = function () {
|
||||
var seen = {};
|
||||
var list = [];
|
||||
recordList.forEach(function (r) {
|
||||
if (r.vin && !seen[r.vin]) {
|
||||
seen[r.vin] = true;
|
||||
list.push(r.vin);
|
||||
}
|
||||
});
|
||||
mockVehicleList.forEach(function (v) {
|
||||
if (v.frameNo && !seen[v.frameNo]) {
|
||||
seen[v.frameNo] = true;
|
||||
list.push(v.frameNo);
|
||||
}
|
||||
});
|
||||
return list.sort();
|
||||
};
|
||||
|
||||
var allOperators = getUniqueOperators();
|
||||
var allPlateNos = getUniquePlateNos();
|
||||
var allVins = getUniqueVins();
|
||||
|
||||
var getFilteredList = function () {
|
||||
var list = recordList;
|
||||
if (appliedDateStart) {
|
||||
list = list.filter(function (r) { return r.plateDate >= appliedDateStart; });
|
||||
}
|
||||
if (appliedDateEnd) {
|
||||
list = list.filter(function (r) { return r.plateDate <= appliedDateEnd; });
|
||||
}
|
||||
if (appliedOperator) {
|
||||
list = list.filter(function (r) {
|
||||
return r.operator && r.operator.indexOf(appliedOperator) >= 0;
|
||||
});
|
||||
}
|
||||
if (appliedPlateNo) {
|
||||
list = list.filter(function (r) {
|
||||
return r.plateNo && r.plateNo.indexOf(appliedPlateNo) >= 0;
|
||||
});
|
||||
}
|
||||
if (appliedVin) {
|
||||
list = list.filter(function (r) {
|
||||
return r.vin && r.vin.indexOf(appliedVin) >= 0;
|
||||
});
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
var filteredList = getFilteredList();
|
||||
var totalItems = filteredList.length;
|
||||
var totalPages = Math.ceil(totalItems / pageSize) || 1;
|
||||
var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage);
|
||||
var startIndex = (validPage - 1) * pageSize;
|
||||
var endIndex = startIndex + pageSize;
|
||||
var paginatedList = filteredList.slice(startIndex, endIndex);
|
||||
|
||||
var findVehicleByVin = function (vin) {
|
||||
return mockVehicleList.find(function (v) { return v.frameNo === vin; });
|
||||
};
|
||||
|
||||
var todayStr = function () {
|
||||
var d = new Date();
|
||||
var y = d.getFullYear();
|
||||
var m = (d.getMonth() + 1).toString();
|
||||
var day = d.getDate().toString();
|
||||
if (m.length === 1) { m = '0' + m; }
|
||||
if (day.length === 1) { day = '0' + day; }
|
||||
return y + '-' + m + '-' + day;
|
||||
};
|
||||
|
||||
var sampleScrapDates = ['2035-12-31', '2030-06-15', '2028-03-20'];
|
||||
var sampleInspectionExpiries = ['2026-06', '2025-12', '2027-03'];
|
||||
var getSampleScrapDate = function () { return sampleScrapDates[Math.floor(Math.random() * sampleScrapDates.length)]; };
|
||||
var getSampleInspectionExpiry = function () { return sampleInspectionExpiries[Math.floor(Math.random() * sampleInspectionExpiries.length)]; };
|
||||
|
||||
var processFileAndGetItem = function (file, callback) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
var photoUrl = reader.result;
|
||||
var mockVin = mockVehicleList[0].frameNo;
|
||||
var mockPlate = '粤A' + Math.floor(Math.random() * 90000 + 10000).toString();
|
||||
var vehicle = findVehicleByVin(mockVin);
|
||||
var item = {
|
||||
photoUrl: photoUrl,
|
||||
vin: mockVin,
|
||||
plateNo: mockPlate,
|
||||
vehicle: vehicle
|
||||
};
|
||||
callback(item);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
var simulateOcrAndConfirm = function (items, isMulti) {
|
||||
setOcrModalVisible(false);
|
||||
if (isMulti && items.length > 1) {
|
||||
batchFullListRef.current = items.slice();
|
||||
setBatchConfirmList(items);
|
||||
setBatchConfirmIndex(0);
|
||||
setBatchTotalCount(items.length);
|
||||
setConfirmData(items[0]);
|
||||
setConfirmVin(items[0].vin);
|
||||
setConfirmPlateNo(items[0].plateNo);
|
||||
setConfirmScrapDate(getSampleScrapDate());
|
||||
setConfirmInspectionExpiry(getSampleInspectionExpiry());
|
||||
setConfirmModalVisible(true);
|
||||
setIsBatchMode(true);
|
||||
} else if (items.length > 0) {
|
||||
setConfirmData(items[0]);
|
||||
setConfirmVin(items[0].vin);
|
||||
setConfirmPlateNo(items[0].plateNo);
|
||||
setConfirmScrapDate(getSampleScrapDate());
|
||||
setConfirmInspectionExpiry(getSampleInspectionExpiry());
|
||||
setConfirmModalVisible(true);
|
||||
setIsBatchMode(false);
|
||||
}
|
||||
};
|
||||
|
||||
var handleUpload = function (e, isBatch) {
|
||||
var files = e.target.files;
|
||||
if (!files || files.length === 0) return;
|
||||
// 从批量上传弹框内「上传车牌」触发:不进入识别/确认页,只在弹框中新增一条识别任务
|
||||
if (batchUploadFromCardRef.current) {
|
||||
batchUploadFromCardRef.current = false;
|
||||
var fileCount = files.length;
|
||||
var collected = [];
|
||||
var processed = 0;
|
||||
var checkDone = function () {
|
||||
processed = processed + 1;
|
||||
if (processed === fileCount) {
|
||||
var now = new Date();
|
||||
var timeStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
|
||||
setBatchTaskList(function (prev) {
|
||||
var next = [{ id: 'bt' + Date.now(), createTime: timeStr, photoCount: collected.length, progress: 100, items: collected }].concat(prev);
|
||||
return next.slice(0, 5);
|
||||
});
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < fileCount; i++) {
|
||||
(function (idx) {
|
||||
processFileAndGetItem(files[idx], function (item) {
|
||||
collected.push(item);
|
||||
checkDone();
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
setOcrModalVisible(true);
|
||||
var fileCount = files.length;
|
||||
var collected = [];
|
||||
var processed = 0;
|
||||
var checkDone = function () {
|
||||
processed = processed + 1;
|
||||
if (processed === fileCount) {
|
||||
setTimeout(function () {
|
||||
simulateOcrAndConfirm(collected, isBatch && collected.length > 1);
|
||||
}, 1500);
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < fileCount; i++) {
|
||||
(function (idx) {
|
||||
processFileAndGetItem(files[idx], function (item) {
|
||||
collected.push(item);
|
||||
checkDone();
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
e.target.value = '';
|
||||
};
|
||||
|
||||
var handleConfirmSubmit = function () {
|
||||
var vehicle = confirmData && confirmData.vehicle;
|
||||
var newRecord = {
|
||||
id: 'r' + Date.now(),
|
||||
plateDate: todayStr(),
|
||||
operator: '当前用户',
|
||||
plateNo: confirmPlateNo,
|
||||
vin: confirmVin,
|
||||
vehicleType: vehicle ? vehicle.vehicleType : '轿车',
|
||||
brand: vehicle ? vehicle.brand : '-',
|
||||
model: vehicle ? vehicle.model : '-',
|
||||
photoUrl: confirmData && confirmData.photoUrl ? confirmData.photoUrl : 'https://picsum.photos/300/200?random=' + Date.now()
|
||||
};
|
||||
var nextList = recordList.slice();
|
||||
nextList.unshift(newRecord);
|
||||
setRecordList(nextList);
|
||||
message.success('车辆上牌成功');
|
||||
if (isBatchMode && batchConfirmList.length > 1) {
|
||||
var nextBatch = batchConfirmList.slice(1);
|
||||
var nextItem = nextBatch[0];
|
||||
setBatchConfirmList(nextBatch);
|
||||
setBatchConfirmIndex(batchConfirmIndex + 1);
|
||||
if (nextItem) {
|
||||
setConfirmData(nextItem);
|
||||
setConfirmVin(nextItem.vin);
|
||||
setConfirmPlateNo(nextItem.plateNo);
|
||||
setConfirmScrapDate(getSampleScrapDate());
|
||||
setConfirmInspectionExpiry(getSampleInspectionExpiry());
|
||||
} else {
|
||||
var fullList = batchFullListRef.current;
|
||||
if (fullList && fullList.length > 0) {
|
||||
var now = new Date();
|
||||
var timeStr = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
|
||||
setBatchTaskList(function (prev) {
|
||||
var next = [{ id: 'bt' + Date.now(), createTime: timeStr, photoCount: fullList.length, progress: 100, items: fullList }].concat(prev);
|
||||
return next.slice(0, 5);
|
||||
});
|
||||
batchFullListRef.current = null;
|
||||
}
|
||||
setConfirmModalVisible(false);
|
||||
setConfirmData(null);
|
||||
setConfirmVin('');
|
||||
setConfirmPlateNo('');
|
||||
setConfirmScrapDate('');
|
||||
setConfirmInspectionExpiry('');
|
||||
setBatchConfirmList([]);
|
||||
setBatchTotalCount(0);
|
||||
setIsBatchMode(false);
|
||||
}
|
||||
} else {
|
||||
setConfirmModalVisible(false);
|
||||
setConfirmData(null);
|
||||
setConfirmVin('');
|
||||
setConfirmPlateNo('');
|
||||
setConfirmScrapDate('');
|
||||
setConfirmInspectionExpiry('');
|
||||
setBatchConfirmList([]);
|
||||
setBatchTotalCount(0);
|
||||
setIsBatchMode(false);
|
||||
}
|
||||
};
|
||||
|
||||
var handleConfirmCancel = function () {
|
||||
setConfirmModalVisible(false);
|
||||
setConfirmData(null);
|
||||
setConfirmVin('');
|
||||
setConfirmPlateNo('');
|
||||
setConfirmScrapDate('');
|
||||
setConfirmInspectionExpiry('');
|
||||
setBatchConfirmList([]);
|
||||
setBatchConfirmIndex(0);
|
||||
setBatchTotalCount(0);
|
||||
setIsBatchMode(false);
|
||||
};
|
||||
|
||||
var t = ARCO_TOKEN;
|
||||
var styles = {
|
||||
page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' },
|
||||
breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
|
||||
breadcrumbLeft: { display: 'flex', alignItems: 'center' },
|
||||
breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: t.spacing8 },
|
||||
breadcrumbCurrent: { color: t.neutral8 },
|
||||
breadcrumbRight: { display: 'flex', alignItems: 'center' },
|
||||
requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' },
|
||||
card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing16 },
|
||||
filterRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: t.spacing12 },
|
||||
filterRowRight: { display: 'flex', alignItems: 'center', gap: t.spacing8, marginLeft: 'auto' },
|
||||
label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap' },
|
||||
input: { padding: '0 10px', height: '32px', width: '180px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, boxSizing: 'border-box' },
|
||||
btn: { padding: t.spacing8 + ' ' + t.spacing16, borderRadius: t.radiusMedium, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '6px', boxSizing: 'border-box' },
|
||||
btnFixed: { width: '82px', height: '32px', padding: 0, borderRadius: '2px', lineHeight: '1' },
|
||||
btnIcon: { width: '14px', height: '14px', flexShrink: 0, display: 'block' },
|
||||
btnFillBlue: { backgroundColor: t.primary, color: t.neutral1, border: 'none' },
|
||||
btnOutlineBlue: { backgroundColor: t.neutral1, color: t.primary, border: '1px solid ' + t.primary },
|
||||
btnDefault: { backgroundColor: t.neutral1, color: t.neutral8, border: '1px solid ' + t.border },
|
||||
btnSize82: { width: '82px', height: '32px', padding: 0, lineHeight: '1', boxSizing: 'border-box' },
|
||||
toolbar: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: t.spacing12, marginBottom: t.spacing16 },
|
||||
tableWrap: { overflowX: 'auto', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.neutral4 },
|
||||
table: { width: '100%', borderCollapse: 'separate', borderSpacing: 0, fontSize: t.fontSize14 },
|
||||
th: { textAlign: 'left', padding: '12px 16px', backgroundColor: t.fillSecondary, borderBottom: '1px solid ' + t.neutral4, fontWeight: 600, color: t.neutral8, fontSize: t.fontSize14, whiteSpace: 'nowrap' },
|
||||
td: { padding: '12px 16px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8, fontSize: t.fontSize14 },
|
||||
pagination: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px', borderTop: '1px solid ' + t.neutral4, backgroundColor: t.neutral1 },
|
||||
paginationLeft: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: t.fontSize14, color: t.neutral7 },
|
||||
paginationRight: { display: 'flex', alignItems: 'center', gap: '8px' },
|
||||
paginationSelect: { padding: '4px 8px', height: '28px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, backgroundColor: t.neutral1 },
|
||||
paginationBtn: { minWidth: '28px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, backgroundColor: t.neutral1, color: t.neutral8, cursor: 'pointer', fontSize: t.fontSize14, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' },
|
||||
paginationBtnActive: { backgroundColor: t.primary, color: t.neutral1, borderColor: t.primary },
|
||||
paginationBtnDisabled: { opacity: 0.5, cursor: 'not-allowed' },
|
||||
paginationInput: { width: '50px', height: '28px', padding: '0 8px', borderRadius: '2px', border: '1px solid ' + t.border, fontSize: t.fontSize14, textAlign: 'center' },
|
||||
actionLink: { color: t.link, cursor: 'pointer', marginRight: t.spacing12, fontSize: t.fontSize14 },
|
||||
modalMask: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
||||
modalBox: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, maxWidth: '90%', maxHeight: '90%', overflow: 'auto', padding: t.spacing24, minWidth: '500px', position: 'relative' },
|
||||
modalTitle: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing16, color: t.neutral8 },
|
||||
modalFooter: { marginTop: t.spacing24, display: 'flex', justifyContent: 'flex-end', gap: t.spacing8 },
|
||||
toast: { position: 'fixed', top: t.spacing24, left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.75)', color: t.neutral1, padding: '10px 20px', borderRadius: t.radiusMedium, zIndex: 2000, fontSize: t.fontSize14 },
|
||||
autocompleteWrap: { position: 'relative', width: '180px', display: 'inline-block' },
|
||||
autocompletePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, minWidth: '100%', maxHeight: '200px', overflowY: 'auto' },
|
||||
autocompleteOption: { padding: '8px 12px', fontSize: t.fontSize14, color: t.neutral8, cursor: 'pointer' },
|
||||
autocompleteOptionHover: { backgroundColor: t.fill },
|
||||
confirmCard: { display: 'flex', gap: t.spacing24, marginTop: t.spacing16 },
|
||||
confirmPhoto: { flex: '0 0 300px', height: '200px', borderRadius: t.radiusMedium, overflow: 'hidden', backgroundColor: t.neutral3 },
|
||||
confirmPhotoImg: { width: '100%', height: '100%', objectFit: 'cover' },
|
||||
confirmForm: { flex: 1, display: 'flex', flexDirection: 'column', gap: t.spacing16 },
|
||||
formLabel: { display: 'block', marginBottom: '6px', fontSize: t.fontSize14, color: t.neutral8 },
|
||||
formInput: { padding: '8px 12px', height: '36px', borderRadius: t.radiusMedium, border: '1px solid ' + t.border, fontSize: t.fontSize14, width: '100%', boxSizing: 'border-box' },
|
||||
photoViewModal: { maxWidth: '800px', textAlign: 'center' },
|
||||
photoViewImg: { maxWidth: '100%', maxHeight: '70vh', borderRadius: t.radiusMedium },
|
||||
modalCloseBtn: { position: 'absolute', right: t.spacing16, top: t.spacing16, width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', borderRadius: t.radiusMedium, backgroundColor: 'transparent', border: 'none', color: t.neutral6, fontSize: '18px', lineHeight: '1', padding: 0 },
|
||||
modalContent: { fontSize: t.fontSize14, color: t.neutral8, lineHeight: '1.6' },
|
||||
requirementSection: { marginBottom: t.spacing16 },
|
||||
requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: t.spacing8 },
|
||||
flowWrap: { marginTop: t.spacing8, marginBottom: t.spacing16 },
|
||||
flowCol: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0 },
|
||||
flowArrow: { width: '2px', height: '20px', backgroundColor: t.neutral5 },
|
||||
flowNodeOval: { padding: '8px 20px', borderRadius: '20px', border: '1px solid ' + t.neutral5, backgroundColor: t.neutral2, fontSize: t.fontSize14, color: t.neutral8 },
|
||||
flowNodeRect: { padding: '8px 20px', border: '1px solid ' + t.neutral5, backgroundColor: t.neutral1, fontSize: t.fontSize14, color: t.neutral8 },
|
||||
flowNodeDiamond: { padding: '10px 16px', border: '1px solid ' + t.neutral5, backgroundColor: t.fillSecondary, fontSize: t.fontSize14, color: t.neutral8, transform: 'rotate(0deg)', width: '140px', textAlign: 'center', boxSizing: 'border-box', clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)' },
|
||||
flowNodeDiamondWrap: { padding: '8px 16px', border: '1px solid ' + t.neutral5, backgroundColor: t.fillSecondary, fontSize: t.fontSize14, color: t.neutral8, minWidth: '120px', textAlign: 'center' },
|
||||
flowNodeToast: { padding: '8px 16px', borderRadius: '8px', border: '1px solid ' + t.neutral4, backgroundColor: t.neutral2, fontSize: t.fontSize14, color: t.neutral7 },
|
||||
flowRow: { display: 'flex', alignItems: 'flex-start', justifyContent: 'center', gap: '24px', flexWrap: 'wrap' },
|
||||
flowBranch: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0 },
|
||||
requirementItem: { marginBottom: t.spacing8, paddingLeft: t.spacing16 },
|
||||
requirementSubItem: { marginBottom: t.spacing4, paddingLeft: t.spacing16, fontSize: t.fontSize14, color: t.neutral7 },
|
||||
batchTaskCardHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: t.spacing16 },
|
||||
batchTaskTable: { width: '100%', borderCollapse: 'collapse', fontSize: t.fontSize14 },
|
||||
batchTaskTh: { textAlign: 'left', padding: '10px 12px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral7, fontWeight: 500 },
|
||||
batchTaskTd: { padding: '10px 12px', borderBottom: '1px solid ' + t.neutral4, color: t.neutral8 },
|
||||
progressWrap: { width: '120px', height: '8px', backgroundColor: t.neutral4, borderRadius: '4px', overflow: 'hidden' },
|
||||
progressBar: { height: '100%', backgroundColor: t.primary, borderRadius: '4px', transition: 'width 0.2s' },
|
||||
recognizeLink: { color: t.link, cursor: 'pointer', fontSize: t.fontSize14 },
|
||||
dateRangeWrap: { position: 'relative', display: 'inline-block' },
|
||||
dateRangeTrigger: { display: 'flex', alignItems: 'center', height: '32px', padding: '0 12px', border: '1px solid ' + t.border, borderRadius: '2px', backgroundColor: t.neutral1, cursor: 'pointer', minWidth: '280px', boxSizing: 'border-box' },
|
||||
dateRangeTriggerFocused: { borderColor: t.primary, outline: 'none' },
|
||||
dateRangeLabel: { display: 'flex', alignItems: 'center', gap: '6px', padding: '0 8px', height: '100%', fontSize: t.fontSize14, color: t.neutral8 },
|
||||
dateRangeLabelActive: { backgroundColor: 'rgba(22,93,255,0.1)', color: t.primary, borderBottom: '2px solid ' + t.primary },
|
||||
dateRangeLabelText: { whiteSpace: 'nowrap' },
|
||||
dateRangeDash: { color: t.neutral5, margin: '0 4px', fontSize: t.fontSize14 },
|
||||
dateRangeIcon: { marginLeft: 'auto', width: '16px', height: '16px', color: t.neutral6, flexShrink: 0 },
|
||||
dateRangePanel: { position: 'absolute', left: 0, top: '100%', marginTop: '4px', backgroundColor: t.neutral1, borderRadius: t.radiusMedium, border: '1px solid ' + t.border, boxShadow: t.shadowMedium, zIndex: 100, padding: '16px', display: 'flex', gap: '24px' },
|
||||
dateRangeCalendar: { width: '280px' },
|
||||
dateRangeCalendarHeader: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px', padding: '0 4px' },
|
||||
dateRangeCalendarTitle: { fontSize: t.fontSize14, fontWeight: 500, color: t.neutral8 },
|
||||
dateRangeCalendarNav: { display: 'flex', alignItems: 'center', gap: '4px' },
|
||||
dateRangeNavBtn: { width: '28px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none', backgroundColor: 'transparent', color: t.neutral6, cursor: 'pointer', borderRadius: t.radiusSmall },
|
||||
dateRangeNavBtnHover: { color: t.primary, backgroundColor: t.fill },
|
||||
dateRangeWeekRow: { display: 'flex', marginBottom: '4px' },
|
||||
dateRangeWeekCell: { width: '36px', height: '28px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', color: t.neutral6 },
|
||||
dateRangeDayRow: { display: 'flex' },
|
||||
dateRangeDayCell: { width: '36px', height: '36px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontSize: t.fontSize14, cursor: 'pointer', borderRadius: t.radiusSmall, color: t.neutral8 },
|
||||
dateRangeDayCellOther: { color: t.neutral5 },
|
||||
dateRangeDayCellSelected: { color: t.primary },
|
||||
dateRangeDayCellInRange: { backgroundColor: 'rgba(22,93,255,0.08)', color: t.primary },
|
||||
dateRangeDayDot: { width: '4px', height: '4px', borderRadius: '50%', backgroundColor: t.primary, marginTop: '2px' }
|
||||
};
|
||||
|
||||
var renderFilterSelect = function (value, setValue, options, placeholder) {
|
||||
var opts = (options || []).map(function (o) { return React.createElement(Option, { key: o, value: o }, o); });
|
||||
return React.createElement(Select, {
|
||||
placeholder: placeholder || '请选择或输入搜索',
|
||||
style: { width: 180 },
|
||||
value: value || undefined,
|
||||
onChange: function (v) { setValue(v || ''); },
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: function (input, opt) {
|
||||
var c = opt && opt.children;
|
||||
return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0;
|
||||
}
|
||||
}, opts);
|
||||
};
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ style: styles.page },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.breadcrumb },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.breadcrumbLeft },
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('span', { style: styles.breadcrumbCurrent }, '上牌管理')
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.breadcrumbRight },
|
||||
React.createElement('a', { href: '#', style: styles.requirementLink, onClick: function (e) { e.preventDefault(); setShowRequirementModal(true); } }, '查看需求说明')
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.card },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.filterRow },
|
||||
React.createElement('span', { style: styles.label }, '上牌日期:'),
|
||||
React.createElement(DatePicker, {
|
||||
style: { width: 180 },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '开始日期',
|
||||
value: filterDateStart && window.moment ? window.moment(filterDateStart, 'YYYY-MM-DD') : null,
|
||||
onChange: function (d, dateStr) { setFilterDateStart(dateStr || ''); }
|
||||
}),
|
||||
React.createElement('span', { style: { color: t.neutral6, margin: '0 4px' } }, '至'),
|
||||
React.createElement(DatePicker, {
|
||||
style: { width: 180 },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '结束日期',
|
||||
value: filterDateEnd && window.moment ? window.moment(filterDateEnd, 'YYYY-MM-DD') : null,
|
||||
onChange: function (d, dateStr) { setFilterDateEnd(dateStr || ''); }
|
||||
}),
|
||||
React.createElement('span', { style: styles.label }, '操作人:'),
|
||||
renderFilterSelect(filterOperator, setFilterOperator, allOperators, '请选择操作人'),
|
||||
React.createElement('span', { style: styles.label }, '车牌号:'),
|
||||
renderFilterSelect(filterPlateNo, setFilterPlateNo, allPlateNos, '请选择车牌号'),
|
||||
React.createElement('span', { style: styles.label }, '车辆识别代码:'),
|
||||
renderFilterSelect(filterVin, setFilterVin, allVins, '请选择车辆识别代码'),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.filterRowRight },
|
||||
React.createElement(Button, {
|
||||
type: 'primary',
|
||||
onClick: function () {
|
||||
setAppliedDateStart(filterDateStart);
|
||||
setAppliedDateEnd(filterDateEnd);
|
||||
setAppliedOperator(filterOperator);
|
||||
setAppliedPlateNo(filterPlateNo);
|
||||
setAppliedVin(filterVin);
|
||||
setCurrentPage(1);
|
||||
message.success('查询成功');
|
||||
}
|
||||
}, '查询'),
|
||||
React.createElement(Button, {
|
||||
onClick: function () {
|
||||
setFilterDateStart('');
|
||||
setFilterDateEnd('');
|
||||
setAppliedDateStart('');
|
||||
setAppliedDateEnd('');
|
||||
setFilterOperator('');
|
||||
setFilterPlateNo('');
|
||||
setFilterVin('');
|
||||
setAppliedOperator('');
|
||||
setAppliedPlateNo('');
|
||||
setAppliedVin('');
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}, '重置')
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.card },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.toolbar },
|
||||
React.createElement('input', {
|
||||
type: 'file',
|
||||
ref: fileInputRef,
|
||||
accept: 'image/*',
|
||||
style: { display: 'none' },
|
||||
onChange: function (e) { handleUpload(e, false); }
|
||||
}),
|
||||
React.createElement('input', {
|
||||
type: 'file',
|
||||
ref: batchFileInputRef,
|
||||
accept: 'image/*',
|
||||
multiple: true,
|
||||
style: { display: 'none' },
|
||||
onChange: function (e) { handleUpload(e, true); }
|
||||
}),
|
||||
React.createElement(Button, {
|
||||
type: 'primary',
|
||||
onClick: function () { if (fileInputRef.current) fileInputRef.current.click(); }
|
||||
}, '新增'),
|
||||
React.createElement(Button, {
|
||||
onClick: function () { setBatchTaskCardVisible(true); }
|
||||
}, '批量上传')
|
||||
),
|
||||
React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
columns: [
|
||||
{ title: '上牌日期', dataIndex: 'plateDate', key: 'plateDate', width: 120 },
|
||||
{ title: '操作人', dataIndex: 'operator', key: 'operator', width: 100 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 },
|
||||
{ title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 140 },
|
||||
{ title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 },
|
||||
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 100 },
|
||||
{ title: '型号', dataIndex: 'model', key: 'model', width: 120 },
|
||||
{ title: '操作', key: 'action', width: 80, render: function (_, row) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setViewPhotoRecord(row); } }, '查看'); } }
|
||||
],
|
||||
dataSource: paginatedList,
|
||||
pagination: {
|
||||
current: validPage,
|
||||
pageSize: pageSize,
|
||||
total: totalItems,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: function (total) { return '共 ' + total + ' 条'; },
|
||||
onChange: function (page, size) {
|
||||
setCurrentPage(page);
|
||||
if (size !== pageSize) setPageSize(size);
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '识别中,请勿关闭页面',
|
||||
visible: ocrModalVisible,
|
||||
footer: null,
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
children: React.createElement('div', { style: { padding: '24px 0', textAlign: 'center' } },
|
||||
React.createElement(Spin, { size: 'large' }),
|
||||
React.createElement('p', { style: { marginTop: 12, color: t.neutral6 } }, '正在识别行驶证信息...')
|
||||
)
|
||||
}),
|
||||
React.createElement(Modal, {
|
||||
title: isBatchMode && batchTotalCount > 1 ? '确认上牌信息(' + (batchTotalCount - batchConfirmList.length + 1) + '/' + batchTotalCount + ')' : '确认上牌信息',
|
||||
visible: confirmModalVisible && !!confirmData,
|
||||
onCancel: handleConfirmCancel,
|
||||
onOk: handleConfirmSubmit,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
width: 560,
|
||||
children: confirmData ? React.createElement(
|
||||
'div',
|
||||
{ style: styles.confirmCard },
|
||||
React.createElement('div', { style: styles.confirmPhoto },
|
||||
React.createElement('img', { src: confirmData.photoUrl, alt: '行驶证', style: styles.confirmPhotoImg })
|
||||
),
|
||||
React.createElement('div', { style: styles.confirmForm },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('label', { style: styles.formLabel }, '车辆识别代号'),
|
||||
React.createElement(Input, {
|
||||
style: { width: '100%' },
|
||||
value: confirmVin,
|
||||
onChange: function (e) { setConfirmVin(e.target.value); }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('label', { style: styles.formLabel }, '车牌号'),
|
||||
React.createElement(Input, {
|
||||
style: { width: '100%' },
|
||||
value: confirmPlateNo,
|
||||
onChange: function (e) { setConfirmPlateNo(e.target.value); }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('label', { style: styles.formLabel }, '强制报废日期'),
|
||||
React.createElement(Input, {
|
||||
style: { width: '100%' },
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
value: confirmScrapDate,
|
||||
onChange: function (e) { setConfirmScrapDate(e.target.value); }
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('label', { style: styles.formLabel }, '检验有效期'),
|
||||
React.createElement(Input, {
|
||||
style: { width: '100%' },
|
||||
placeholder: 'YYYY-MM',
|
||||
value: confirmInspectionExpiry,
|
||||
onChange: function (e) { setConfirmInspectionExpiry(e.target.value); }
|
||||
})
|
||||
)
|
||||
)
|
||||
) : null
|
||||
}),
|
||||
React.createElement(Modal, {
|
||||
title: '行驶证照片',
|
||||
visible: !!viewPhotoRecord,
|
||||
footer: React.createElement(Button, { onClick: function () { setViewPhotoRecord(null); } }, '关闭'),
|
||||
onCancel: function () { setViewPhotoRecord(null); },
|
||||
width: 640,
|
||||
children: viewPhotoRecord ? React.createElement('img', { src: viewPhotoRecord.photoUrl, alt: '行驶证', style: Object.assign({}, styles.photoViewImg, { width: '100%' }) }) : null
|
||||
}),
|
||||
React.createElement(Modal, {
|
||||
title: '批量上牌任务',
|
||||
visible: batchTaskCardVisible,
|
||||
onCancel: function () { setBatchTaskCardVisible(false); },
|
||||
footer: React.createElement(Button, { onClick: function () { setBatchTaskCardVisible(false); } }, '关闭'),
|
||||
width: 560,
|
||||
children: React.createElement(React.Fragment, null,
|
||||
React.createElement('div', { style: Object.assign({}, styles.batchTaskCardHeader, { marginBottom: 16 }) },
|
||||
React.createElement(Button, {
|
||||
type: 'primary',
|
||||
onClick: function () {
|
||||
batchUploadFromCardRef.current = true;
|
||||
if (batchFileInputRef.current) batchFileInputRef.current.click();
|
||||
}
|
||||
}, '批量上传')
|
||||
),
|
||||
React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
columns: [
|
||||
{ title: '任务时间', dataIndex: 'createTime', key: 'createTime', width: 160 },
|
||||
{ title: '照片数量', dataIndex: 'photoCount', key: 'photoCount', width: 100, render: function (c) { return c + ' 张'; } },
|
||||
{ title: '完成进度', key: 'progress', width: 160, render: function (_, task) {
|
||||
return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
||||
React.createElement('div', { style: styles.progressWrap },
|
||||
React.createElement('div', { style: Object.assign({}, styles.progressBar, { width: (task.progress || 0) + '%' }) })
|
||||
),
|
||||
React.createElement('span', { style: { fontSize: t.fontSize14, color: t.neutral7, minWidth: 36 } }, (task.progress || 0) + '%')
|
||||
);
|
||||
} },
|
||||
{ title: '操作', key: 'action', width: 80, render: function (_, task) {
|
||||
return task.progress === 100 && task.items && task.items.length > 0
|
||||
? React.createElement(Button, { type: 'link', size: 'small', onClick: function () {
|
||||
setBatchConfirmList(task.items);
|
||||
setBatchConfirmIndex(0);
|
||||
setBatchTotalCount(task.items.length);
|
||||
setConfirmData(task.items[0]);
|
||||
setConfirmVin(task.items[0].vin);
|
||||
setConfirmPlateNo(task.items[0].plateNo);
|
||||
setConfirmScrapDate(getSampleScrapDate());
|
||||
setConfirmInspectionExpiry(getSampleInspectionExpiry());
|
||||
setConfirmModalVisible(true);
|
||||
setIsBatchMode(true);
|
||||
setBatchTaskCardVisible(false);
|
||||
} }, '识别')
|
||||
: React.createElement('span', { style: { color: t.neutral5, fontSize: t.fontSize14 } }, '-');
|
||||
} }
|
||||
],
|
||||
dataSource: batchTaskList.slice(0, 5),
|
||||
pagination: false
|
||||
})
|
||||
)
|
||||
}),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
visible: showRequirementModal,
|
||||
onCancel: function () { setShowRequirementModal(false); },
|
||||
footer: React.createElement(Button, { onClick: function () { setShowRequirementModal(false); } }, '关闭'),
|
||||
width: 720,
|
||||
children: React.createElement('div', { style: styles.modalContent },
|
||||
React.createElement('div', { style: Object.assign({}, styles.requirementSection, { marginBottom: t.spacing24 }) },
|
||||
React.createElement('div', { style: Object.assign({}, styles.requirementSectionTitle, { fontSize: '18px', marginBottom: t.spacing12 }) }, '上牌管理'),
|
||||
React.createElement('div', { style: Object.assign({}, styles.requirementItem, { marginTop: 0, color: t.neutral7 }) }, '用以识别行驶证正反面,识别车架号、车牌号、强制报废日期(YYYY-MM-DD)、检验有效期(YYYY-MM)')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '1.面包屑:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '运维管理-车辆业务-上牌管理')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '2.筛选:'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.1.上牌日期:双日历日期选择器,支持选择开始-结束时间,支持手动修改日期,但检验格式及判断结束日期不得早于开始日期;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.2.操作人:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择操作人;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.3.车牌号:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择车牌号;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.4.车辆识别代码:选择器,支持从输入框输入内容模糊搜索,默认提示内容为:请选择车辆识别代码;')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '3.列表:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '列表右侧按钮为新增、批量上传,字段依次为上牌日期、操作人、车牌号、车辆识别代码、车辆类型、品牌、型号、操作;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.上牌日期:车辆上牌操作完成日期,格式为YYYY-MM-DD'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.操作人:车辆上牌记录操作用户姓名;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.3.车牌号:显示上牌车牌号;通过新增按钮上传行驶证照片后,通过OCR识别技术,自动识别出该车辆识别代号对应车牌号,确认无误提交后自动反写'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.4.车辆识别代码:显示上牌车辆车辆识别代码;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.5.车辆类型:显示该车辆识别代码对应车辆类型;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.6.品牌:显示该车辆识别代码对应品牌;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.7.型号:显示该车辆识别代码对应型号;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.8.操作:行驶证,点击放大预览行驶证照片;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '下方增加分页功能,支持选择单页数据条数;')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '4.新增:'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '4.1.点击新增按钮,上传行驶证照片附件,上传后OCR识别过程中弹出卡片提示:识别中,请勿关闭页面;如果照片识别失败,则提示:识别失败,请重新尝试;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '4.2.上传成功后确认卡片中显示照片、车辆识别代码、车牌号、强制报废日期、检验有效期,可通过二次编辑进行校正;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '4.3.点击卡片页面底部"确认"按钮,确认根据车辆识别代码判断是否存在该车辆,如果是则toast提示"上牌成功",并在列表中生成操作,如果否则toast提示:该车辆不存在。')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '5.批量上传:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '5.1.点击批量上牌按钮,弹出批量上牌任务卡片,再点击右上角上传车牌,上传多张行驶证照片附件,上传后弹出批量上牌任务卡片:卡片中记录最近5条批量上牌任务,任务字段为任务时间、照片数量、识别错误、完成进度、操作;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '5.1.1.任务时间:上传批量照片的时间,精确至分钟,格式为YYYY-MM-DD HH:MM;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '5.1.2.照片数量:显示这一批照片总数;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '5.1.3.识别错误:显示识别失败的照片总数,hover时气泡卡片显示识别失败的照片名称,以;分隔;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '5.1.4.完成进度:显示ocr识别进度条和已完成百分比;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '5.1.5.操作:已完成任务操作栏显示识别,未完成任务操作栏显示-;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '5.2.识别:点击识别确认卡片中当前张数、显示照片、车辆识别代码、车牌号、强制报废日期、检验有效期,可通过二次编辑进行校正;当前张数显示在确认上牌信息标题右侧,格式为(当前/总计),依次操作直到完成所有记录提交完成,全部完成后toast提示:批量上传成功;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '5.3.在识别确认卡片中点击确认后将立刻上传该照片,可再次通过点击批量上传拉取上传任务框的方式,自动跳转至最后一条上传记录进行操作;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '5.4.批量上传任务中照片全部识别完成后,不再显示识别按钮;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '5.5.具体识别流程请参考axure菜单目录中上牌管理下的流程图;')
|
||||
)
|
||||
)
|
||||
})
|
||||
);
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
function mount() {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', mount);
|
||||
} else {
|
||||
setTimeout(mount, 0);
|
||||
}
|
||||
}
|
||||
538
web端/运维管理/车辆业务/交车管理.jsx
Normal file
538
web端/运维管理/车辆业务/交车管理.jsx
Normal file
@@ -0,0 +1,538 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 车辆业务 - 交车管理(ONEOS运管平台,布局参照新增租赁合同)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var Tabs = antd.Tabs;
|
||||
var Table = antd.Table;
|
||||
var Popover = antd.Popover;
|
||||
var Cascader = antd.Cascader;
|
||||
var Modal = antd.Modal;
|
||||
var message = antd.message;
|
||||
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
|
||||
var filterState = useState({
|
||||
contractCode: undefined,
|
||||
projectName: undefined,
|
||||
customerName: undefined,
|
||||
deliveryRegion: undefined,
|
||||
dateStart: '',
|
||||
dateEnd: '',
|
||||
deliveryPerson: undefined
|
||||
});
|
||||
var filters = filterState[0];
|
||||
var setFilters = filterState[1];
|
||||
var appliedFilterState = useState({
|
||||
contractCode: undefined,
|
||||
projectName: undefined,
|
||||
customerName: undefined,
|
||||
deliveryRegion: undefined,
|
||||
dateStart: '',
|
||||
dateEnd: '',
|
||||
deliveryPerson: undefined
|
||||
});
|
||||
var appliedFilters = appliedFilterState[0];
|
||||
var setAppliedFilters = appliedFilterState[1];
|
||||
|
||||
var activeTabState = useState('pending');
|
||||
var activeTab = activeTabState[0];
|
||||
var setActiveTab = activeTabState[1];
|
||||
var pageState = useState(1);
|
||||
var pageSizeState = useState(10);
|
||||
var requirementModalOpen = useState(false);
|
||||
var setRequirementModalOpen = requirementModalOpen[1];
|
||||
|
||||
var requirementDocContent = '交车管理\n\n1.面包屑:\n1.1.运维管理-车辆业务-交车管理\n\n2.筛选:\n2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.4.交车区域:地区选择器,支持省-市2级筛选;\n3.5.交车时间:日期选择器,默认提示信息为:请选择交车开始时间 请选择交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;\n3.6.交车人:选择器,默认为所有交车人;提示信息为:请输入或选择交车人姓名,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n3.7.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;\n3.8.重置:点击清空查询条件至默认;\n\n3.列表:\n分为两个tab:待处理、历史记录\n3.1.待处理:显示以下字段:\n3.1.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.1.2.任务发布时间:显示交车任务单生成时间,格式为:YYYY-MM-DD HH:MM;\n3.1.3.合同编码:显示租赁/自营合同编码;\n3.1.4.项目名称:显示租赁/自营合同项目名称;\n3.1.5.客户名称:显示租赁/自营合同客户名称;\n3.1.6.交车数量:显示交车数量,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号;\n 3.1.6.1.车辆类型:显示车辆类型;\n 3.1.6.2.品牌:显示车辆品牌;\n 3.1.6.3.型号:显示车辆型号;\n 3.1.6.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n3.1.7.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.1.8.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.1.9.操作:查看、交车;\n 3.1.8.1.交车:点击跳转交车单页面;\n\n3.2.历史记录:显示以下字段:\n3.2.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.2.2.任务发布时间:显示交车任务单生成时间,格式为:YYYY-MM-DD HH:MM;\n3.2.3.交车完成时间:显示交车单完成时间,格式为:YYYY-MM-DD HH:MM;\n3.2.4.交车人:显示交车单最终提交用户;\n3.2.5.合同编码:显示租赁/自营合同编码;\n3.2.6.项目名称:显示租赁/自营合同项目名称;\n3.2.7.客户名称:显示租赁/自营合同客户名称;\n3.2.8.交车数量:显示交车数,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、实际交车日期、交车人;\n 3.2.8.1.车辆类型:显示车辆类型;\n 3.2.8.2.品牌:显示车辆品牌;\n 3.2.8.3.型号:显示车辆型号;\n 3.2.8.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n 3.2.8.5.实际交车日期:显示实际交车日期,格式为YYYY-MM-DD,如该车还未交车则显示为-;\n 3.2.8.6.交车人:显示实际交车人用户姓名,如该车还未还车则显示为-;\n3.2.9.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.2.10.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.2.11.操作:查看;\n 3.2.11.1.查看:点击跳转查看交车单页面;';
|
||||
|
||||
// 交车区域:省-市 二级
|
||||
var regionOptions = [
|
||||
{ value: 'zhejiang', label: '浙江省', children: [{ value: 'hangzhou', label: '杭州市' }, { value: 'jiaxing', label: '嘉兴市' }, { value: 'ningbo', label: '宁波市' }] },
|
||||
{ value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] },
|
||||
{ value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] }
|
||||
];
|
||||
|
||||
var contractCodeOptions = [
|
||||
{ value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' },
|
||||
{ value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' },
|
||||
{ value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' }
|
||||
];
|
||||
var projectNameOptions = [
|
||||
{ value: 'p1', label: '嘉兴氢能示范项目' },
|
||||
{ value: 'p2', label: '上海物流租赁项目' },
|
||||
{ value: 'p3', label: '杭州城配租赁项目' }
|
||||
];
|
||||
var customerNameOptions = [
|
||||
{ value: 'c1', label: '嘉兴某某物流有限公司' },
|
||||
{ value: 'c2', label: '上海某某运输公司' },
|
||||
{ value: 'c3', label: '杭州某某租赁有限公司' }
|
||||
];
|
||||
var deliveryPersonOptions = [
|
||||
{ value: '张三', label: '张三' },
|
||||
{ value: '李四', label: '李四' },
|
||||
{ value: '王五', label: '王五' }
|
||||
];
|
||||
|
||||
// 待处理 mock(10条样例)
|
||||
var pendingListState = useState([
|
||||
{ id: 'd1', expectedDate: '2025-02-28', taskPublishTime: '2025-02-20 09:00', contractCode: 'HT-ZL-2025-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '京A12345' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区科技大道1号' },
|
||||
{ id: 'd2', expectedDate: '2025-03-01 至 2025-03-05', taskPublishTime: '2025-02-21 10:30', contractCode: 'HT-ZL-2025-002', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '浦东新区张江高科技园区' },
|
||||
{ id: 'd3', expectedDate: '2025-02-25', taskPublishTime: '2025-02-18 14:00', contractCode: 'HT-ZL-2025-003', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, vehicleList: [{ vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '浙A10001' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '-' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '余杭区未来科技城' },
|
||||
{ id: 'd4', expectedDate: '2025-03-08', taskPublishTime: '2025-02-25 08:15', contractCode: 'HT-ZL-2025-004', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1160', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市秀洲区洪兴西路288号' },
|
||||
{ id: 'd5', expectedDate: '2025-03-10 至 2025-03-12', taskPublishTime: '2025-02-26 11:20', contractCode: 'HT-ZL-2025-005', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '-' }, { vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '闵行区莘庄工业区申富路669号' },
|
||||
{ id: 'd6', expectedDate: '2025-03-15', taskPublishTime: '2025-03-01 09:30', contractCode: 'HT-ZL-2025-006', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 4, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '-' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '-' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '-' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '萧山区市心北路108号' },
|
||||
{ id: 'd7', expectedDate: '2025-03-18', taskPublishTime: '2025-03-03 14:00', contractCode: 'HT-ZL-2025-007', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙F80088' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市经开区昌盛路1号' },
|
||||
{ id: 'd8', expectedDate: '2025-03-20 至 2025-03-22', taskPublishTime: '2025-03-05 10:00', contractCode: 'HT-ZL-2025-008', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 3, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1190', plateNo: '-' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1190', plateNo: '-' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '-' }], deliveryRegion: '上海市-上海市', deliveryAddress: '宝山区沪太路5008号' },
|
||||
{ id: 'd9', expectedDate: '2025-03-25', taskPublishTime: '2025-03-08 08:45', contractCode: 'HT-ZL-2025-009', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '-' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙A20002' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '滨江区网商路699号' },
|
||||
{ id: 'd10', expectedDate: '2025-03-28', taskPublishTime: '2025-03-10 16:20', contractCode: 'HT-ZL-2025-010', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1180', plateNo: '-' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区广益路与由拳路交叉口' }
|
||||
]);
|
||||
var pendingList = pendingListState[0];
|
||||
|
||||
// 历史记录 mock(10条样例,含交车完成时间、交车人,vehicleList 含实际交车日期、交车人)
|
||||
var historyListState = useState([
|
||||
{ id: 'h1', expectedDate: '2025-02-15', taskPublishTime: '2025-02-10 09:00', completeTime: '2025-02-15 14:30', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '京A12345', actualDate: '2025-02-15', deliveryPerson: '张三' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '京C11111', actualDate: '2025-02-15', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区科技大道1号' },
|
||||
{ id: 'h2', expectedDate: '2025-02-10 至 2025-02-12', taskPublishTime: '2025-02-05 10:00', completeTime: '2025-02-12 11:00', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-002', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '沪A20002', actualDate: '2025-02-11', deliveryPerson: '王五' }], deliveryRegion: '上海市-上海市', deliveryAddress: '浦东新区张江路100号' },
|
||||
{ id: 'h3', expectedDate: '2025-02-18', taskPublishTime: '2025-02-12 08:30', completeTime: '2025-02-18 15:00', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-003', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 3, vehicleList: [{ vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', plateNo: '浙A10001', actualDate: '2025-02-18', deliveryPerson: '张三' }, { vehicleType: '厢式货车', brand: '东风', model: 'DFH1190', plateNo: '浙A10002', actualDate: '2025-02-18', deliveryPerson: '李四' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '浙A10003', actualDate: '2025-02-18', deliveryPerson: '王五' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '余杭区未来科技城' },
|
||||
{ id: 'h4', expectedDate: '2025-02-20', taskPublishTime: '2025-02-14 11:00', completeTime: '2025-02-20 10:15', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-004', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', plateNo: '浙F60001', actualDate: '2025-02-20', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市秀洲区洪兴西路288号' },
|
||||
{ id: 'h5', expectedDate: '2025-02-22 至 2025-02-24', taskPublishTime: '2025-02-16 09:45', completeTime: '2025-02-24 16:30', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-005', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 2, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '沪B30001', actualDate: '2025-02-23', deliveryPerson: '王五' }, { vehicleType: '栏板货车', brand: '东风', model: 'DFH1160', plateNo: '沪B30002', actualDate: '2025-02-24', deliveryPerson: '张三' }], deliveryRegion: '上海市-上海市', deliveryAddress: '闵行区莘庄工业区申富路669号' },
|
||||
{ id: 'h6', expectedDate: '2025-02-25', taskPublishTime: '2025-02-18 14:20', completeTime: '2025-02-25 11:00', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-006', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '厢式货车', brand: '重汽', model: 'ZZ1160', plateNo: '浙A40001', actualDate: '2025-02-25', deliveryPerson: '张三' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '萧山区市心北路108号' },
|
||||
{ id: 'h7', expectedDate: '2025-02-28', taskPublishTime: '2025-02-20 10:10', completeTime: '2025-02-28 14:45', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-007', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', plateNo: '浙F70001', actualDate: '2025-02-28', deliveryPerson: '李四' }, { vehicleType: '平板货车', brand: '福田', model: 'BJ1180', plateNo: '浙F70002', actualDate: '2025-02-28', deliveryPerson: '王五' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市经开区昌盛路1号' },
|
||||
{ id: 'h8', expectedDate: '2025-03-02', taskPublishTime: '2025-02-22 08:00', completeTime: '2025-03-02 09:30', deliveryPerson: '王五', contractCode: 'HT-ZL-2024-008', projectName: '上海物流租赁项目', customerName: '上海某某运输公司', deliveryCount: 1, vehicleList: [{ vehicleType: '栏板货车', brand: '江淮', model: 'HFC1160', plateNo: '沪A50001', actualDate: '2025-03-02', deliveryPerson: '王五' }], deliveryRegion: '上海市-上海市', deliveryAddress: '宝山区沪太路5008号' },
|
||||
{ id: 'h9', expectedDate: '2025-03-05 至 2025-03-07', taskPublishTime: '2025-02-25 15:30', completeTime: '2025-03-07 13:20', deliveryPerson: '张三', contractCode: 'HT-ZL-2024-009', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', deliveryCount: 2, vehicleList: [{ vehicleType: '厢式货车', brand: '江淮', model: 'HFC1190', plateNo: '浙A60001', actualDate: '2025-03-06', deliveryPerson: '张三' }, { vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1190', plateNo: '浙A60002', actualDate: '2025-03-07', deliveryPerson: '李四' }], deliveryRegion: '浙江省-杭州市', deliveryAddress: '滨江区网商路699号' },
|
||||
{ id: 'h10', expectedDate: '2025-03-10', taskPublishTime: '2025-03-01 11:00', completeTime: '2025-03-10 10:00', deliveryPerson: '李四', contractCode: 'HT-ZL-2024-010', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', deliveryCount: 1, vehicleList: [{ vehicleType: '平板货车', brand: '福田', model: 'BJ1190', plateNo: '浙F90001', actualDate: '2025-03-10', deliveryPerson: '李四' }], deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴市南湖区广益路与由拳路交叉口' }
|
||||
]);
|
||||
var historyList = historyListState[0];
|
||||
|
||||
var filteredPending = useMemo(function () {
|
||||
var list = pendingList.slice();
|
||||
if (appliedFilters.contractCode) list = list.filter(function (r) { return (r.contractCode || '').indexOf(appliedFilters.contractCode) !== -1; });
|
||||
if (appliedFilters.projectName) list = list.filter(function (r) { return r.projectName === appliedFilters.projectName; });
|
||||
if (appliedFilters.customerName) list = list.filter(function (r) { return r.customerName === appliedFilters.customerName; });
|
||||
if (appliedFilters.deliveryRegion) list = list.filter(function (r) { return (r.deliveryRegion || '').indexOf(appliedFilters.deliveryRegion) !== -1; });
|
||||
if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.expectedDate || '').slice(0, 10); return d >= appliedFilters.dateStart; });
|
||||
if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.expectedDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; });
|
||||
return list;
|
||||
}, [pendingList, appliedFilters]);
|
||||
|
||||
var filteredHistory = useMemo(function () {
|
||||
var list = historyList.slice();
|
||||
if (appliedFilters.contractCode) list = list.filter(function (r) { return (r.contractCode || '').indexOf(appliedFilters.contractCode) !== -1; });
|
||||
if (appliedFilters.projectName) list = list.filter(function (r) { return r.projectName === appliedFilters.projectName; });
|
||||
if (appliedFilters.customerName) list = list.filter(function (r) { return r.customerName === appliedFilters.customerName; });
|
||||
if (appliedFilters.deliveryRegion) list = list.filter(function (r) { return (r.deliveryRegion || '').indexOf(appliedFilters.deliveryRegion) !== -1; });
|
||||
if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.completeTime || '').slice(0, 10); return d >= appliedFilters.dateStart; });
|
||||
if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.completeTime || '').slice(0, 10); return d <= appliedFilters.dateEnd; });
|
||||
if (appliedFilters.deliveryPerson) list = list.filter(function (r) { return (r.deliveryPerson || '').indexOf(appliedFilters.deliveryPerson) !== -1; });
|
||||
return list;
|
||||
}, [historyList, appliedFilters]);
|
||||
|
||||
var page = pageState[0];
|
||||
var setPage = pageState[1];
|
||||
var pageSize = pageSizeState[0];
|
||||
var setPageSize = pageSizeState[1];
|
||||
var totalCount = activeTab === 'pending' ? filteredPending.length : filteredHistory.length;
|
||||
var displayPending = useMemo(function () {
|
||||
var start = (page - 1) * pageSize;
|
||||
return filteredPending.slice(start, start + pageSize);
|
||||
}, [filteredPending, page, pageSize]);
|
||||
var displayHistory = useMemo(function () {
|
||||
var start = (page - 1) * pageSize;
|
||||
return filteredHistory.slice(start, start + pageSize);
|
||||
}, [filteredHistory, page, pageSize]);
|
||||
|
||||
var handleQuery = useCallback(function () {
|
||||
var regionVal = filters.deliveryRegion;
|
||||
if (Array.isArray(regionVal) && regionVal.length >= 2) {
|
||||
var prov = regionOptions.find(function (r) { return r.value === regionVal[0]; });
|
||||
var city = prov && prov.children && prov.children.find(function (c) { return c.value === regionVal[1]; });
|
||||
regionVal = prov && city ? prov.label + '-' + city.label : undefined;
|
||||
} else if (Array.isArray(regionVal) && regionVal.length === 1) {
|
||||
var p = regionOptions.find(function (r) { return r.value === regionVal[0]; });
|
||||
regionVal = p ? p.label : undefined;
|
||||
}
|
||||
setAppliedFilters({
|
||||
contractCode: filters.contractCode,
|
||||
projectName: filters.projectName,
|
||||
customerName: filters.customerName,
|
||||
deliveryRegion: regionVal,
|
||||
dateStart: filters.dateStart,
|
||||
dateEnd: filters.dateEnd,
|
||||
deliveryPerson: filters.deliveryPerson
|
||||
});
|
||||
setPage(1);
|
||||
}, [filters]);
|
||||
|
||||
var handleReset = useCallback(function () {
|
||||
var empty = { contractCode: undefined, projectName: undefined, customerName: undefined, deliveryRegion: undefined, dateStart: '', dateEnd: '', deliveryPerson: undefined };
|
||||
setFilters(empty);
|
||||
setAppliedFilters(empty);
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
var handleExport = useCallback(function () {
|
||||
var rows = activeTab === 'pending' ? filteredPending : filteredHistory;
|
||||
if (!rows || rows.length === 0) {
|
||||
message.warning('当前无数据可导出');
|
||||
return;
|
||||
}
|
||||
var escapeCsv = function (v) {
|
||||
var s = v == null ? '' : String(v);
|
||||
if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1) return '"' + s.replace(/"/g, '""') + '"';
|
||||
return s;
|
||||
};
|
||||
var headers;
|
||||
var rowToCells = function (r) {
|
||||
if (activeTab === 'pending') {
|
||||
return [r.expectedDate, r.taskPublishTime, r.contractCode, r.projectName, r.customerName, r.deliveryCount, r.deliveryRegion, r.deliveryAddress];
|
||||
}
|
||||
return [r.expectedDate, r.taskPublishTime, r.completeTime, r.deliveryPerson, r.contractCode, r.projectName, r.customerName, r.deliveryCount, r.deliveryRegion, r.deliveryAddress];
|
||||
};
|
||||
if (activeTab === 'pending') {
|
||||
headers = ['预计交车时间', '任务发布时间', '合同编码', '项目名称', '客户名称', '交车数量', '交车区域', '交车地点'];
|
||||
} else {
|
||||
headers = ['预计交车时间', '任务发布时间', '交车完成时间', '交车人', '合同编码', '项目名称', '客户名称', '交车数量', '交车区域', '交车地点'];
|
||||
}
|
||||
var csv = headers.map(escapeCsv).join(',') + '\n';
|
||||
rows.forEach(function (r) {
|
||||
csv += rowToCells(r).map(escapeCsv).join(',') + '\n';
|
||||
});
|
||||
var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = '交车管理_' + (activeTab === 'pending' ? '待处理' : '历史记录') + '_' + new Date().getTime() + '.csv';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
message.success('导出成功');
|
||||
}, [activeTab, filteredPending, filteredHistory]);
|
||||
|
||||
var dateRangeValue = useMemo(function () {
|
||||
if (!filters.dateStart && !filters.dateEnd) return null;
|
||||
try {
|
||||
if (typeof window !== 'undefined' && window.dayjs && filters.dateStart && filters.dateEnd) {
|
||||
return [window.dayjs(filters.dateStart), window.dayjs(filters.dateEnd)];
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}, [filters.dateStart, filters.dateEnd]);
|
||||
|
||||
var onDateRangeChange = useCallback(function (dates, dateStrings) {
|
||||
setFilters(function (f) {
|
||||
var g = {}; for (var k in f) g[k] = f[k];
|
||||
g.dateStart = (dateStrings && dateStrings[0]) || '';
|
||||
g.dateEnd = (dateStrings && dateStrings[1]) || '';
|
||||
return g;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 交车区域 Cascader 的 value:用 appliedFilters 反推或存 value 数组。筛选用字符串比较,表单用 Cascader 选省-市
|
||||
var deliveryRegionValue = useMemo(function () {
|
||||
var s = filters.deliveryRegion;
|
||||
if (!s || typeof s !== 'string') return undefined;
|
||||
var parts = s.split('-');
|
||||
if (parts.length < 2) return undefined;
|
||||
for (var i = 0; i < regionOptions.length; i++) {
|
||||
var prov = regionOptions[i];
|
||||
if (prov.label !== parts[0]) continue;
|
||||
for (var j = 0; j < (prov.children || []).length; j++) {
|
||||
if (prov.children[j].label === parts[1]) return [prov.value, prov.children[j].value];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}, [filters.deliveryRegion]);
|
||||
|
||||
// 列表列:待处理
|
||||
var popoverTableStyle = { width: '100%', borderCollapse: 'collapse', fontSize: 12 };
|
||||
var popoverThStyle = { padding: '6px 8px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', backgroundColor: '#fafafa', fontWeight: 600 };
|
||||
var popoverTdStyle = { padding: '6px 8px', borderBottom: '1px solid #f0f0f0' };
|
||||
|
||||
function renderPendingQuantity(record) {
|
||||
var list = record.vehicleList || [];
|
||||
var content = React.createElement('div', { style: { padding: 8, minWidth: 320 } },
|
||||
React.createElement('div', { style: { marginBottom: 8, fontWeight: 600 } }, '车辆明细'),
|
||||
React.createElement('table', { style: popoverTableStyle },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: popoverThStyle }, '车辆类型'),
|
||||
React.createElement('th', { style: popoverThStyle }, '品牌'),
|
||||
React.createElement('th', { style: popoverThStyle }, '型号'),
|
||||
React.createElement('th', { style: popoverThStyle }, '车牌号')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
list.map(function (v, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: popoverTdStyle }, v.vehicleType || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.brand || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.model || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.plateNo || '-')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
return React.createElement(Popover, { content: content, title: null },
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 600 } }, record.deliveryCount + ' 辆')
|
||||
);
|
||||
}
|
||||
|
||||
function renderHistoryQuantity(record) {
|
||||
var list = record.vehicleList || [];
|
||||
var content = React.createElement('div', { style: { padding: 8, minWidth: 420 } },
|
||||
React.createElement('div', { style: { marginBottom: 8, fontWeight: 600 } }, '车辆明细'),
|
||||
React.createElement('table', { style: popoverTableStyle },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: popoverThStyle }, '车辆类型'),
|
||||
React.createElement('th', { style: popoverThStyle }, '品牌'),
|
||||
React.createElement('th', { style: popoverThStyle }, '型号'),
|
||||
React.createElement('th', { style: popoverThStyle }, '车牌号'),
|
||||
React.createElement('th', { style: popoverThStyle }, '实际交车日期'),
|
||||
React.createElement('th', { style: popoverThStyle }, '交车人')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
list.map(function (v, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: popoverTdStyle }, v.vehicleType || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.brand || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.model || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.plateNo || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.actualDate || '-'),
|
||||
React.createElement('td', { style: popoverTdStyle }, v.deliveryPerson || '-')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
return React.createElement(Popover, { content: content, title: null },
|
||||
React.createElement('span', { style: { color: '#1890ff', cursor: 'pointer', fontWeight: 600 } }, record.deliveryCount + ' 辆')
|
||||
);
|
||||
}
|
||||
|
||||
var pendingColumns = [
|
||||
{ title: '预计交车时间', dataIndex: 'expectedDate', key: 'expectedDate', width: 260, ellipsis: true },
|
||||
{ title: '任务发布时间', dataIndex: 'taskPublishTime', key: 'taskPublishTime', width: 160, ellipsis: true },
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 160, ellipsis: true },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true },
|
||||
{ title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, r) { return renderPendingQuantity(r); } },
|
||||
{ title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 130, ellipsis: true },
|
||||
{ title: '交车地点', dataIndex: 'deliveryAddress', key: 'deliveryAddress', width: 220, ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转查看交车单'); } }, '查看');
|
||||
} }
|
||||
];
|
||||
|
||||
var historyColumns = [
|
||||
{ title: '预计交车时间', dataIndex: 'expectedDate', key: 'expectedDate', width: 260, ellipsis: true },
|
||||
{ title: '任务发布时间', dataIndex: 'taskPublishTime', key: 'taskPublishTime', width: 160, ellipsis: true },
|
||||
{ title: '交车完成时间', dataIndex: 'completeTime', key: 'completeTime', width: 160, ellipsis: true },
|
||||
{ title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 100, ellipsis: true },
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 160, ellipsis: true },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true },
|
||||
{ title: '交车数量', key: 'deliveryCount', width: 100, render: function (_, r) { return renderHistoryQuantity(r); } },
|
||||
{ title: '交车区域', dataIndex: 'deliveryRegion', key: 'deliveryRegion', width: 130, ellipsis: true },
|
||||
{ title: '交车地点', dataIndex: 'deliveryAddress', key: 'deliveryAddress', width: 220, ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 80, fixed: 'right', render: function () {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转查看交车单页面'); } }, '查看');
|
||||
} }
|
||||
];
|
||||
|
||||
var tablePagination = useMemo(function () {
|
||||
return {
|
||||
current: page,
|
||||
pageSize: pageSize,
|
||||
total: totalCount,
|
||||
showSizeChanger: true,
|
||||
showTotal: function (t) { return '共 ' + t + ' 条'; },
|
||||
pageSizeOptions: ['10', '20', '50'],
|
||||
onChange: function (p, size) { setPage(p); if (size !== pageSize) setPageSize(size); }
|
||||
};
|
||||
}, [page, pageSize, totalCount]);
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' },
|
||||
formItem: { marginBottom: 4 },
|
||||
formLabel: { display: 'block', marginBottom: 4, color: '#333' }
|
||||
};
|
||||
|
||||
var breadcrumbItems = [
|
||||
{ title: '运维管理' },
|
||||
{ title: '车辆业务' },
|
||||
{ title: '交车管理' }
|
||||
];
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
|
||||
React.createElement(Breadcrumb, { items: breadcrumbItems }),
|
||||
React.createElement(Button, { type: 'link', onClick: function () { setRequirementModalOpen(true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement(Card, { style: { marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
React.createElement('div', { style: styles.formRow },
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '合同编码'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择合同编码',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.contractCode,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.contractCode = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: contractCodeOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '项目名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择项目名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.projectName,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.projectName = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: projectNameOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '客户名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择客户名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.customerName,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.customerName = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: customerNameOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '交车区域'),
|
||||
React.createElement(Cascader, {
|
||||
options: regionOptions,
|
||||
placeholder: '请选择省-市',
|
||||
allowClear: true,
|
||||
style: { width: '100%' },
|
||||
value: deliveryRegionValue,
|
||||
onChange: function (value) {
|
||||
var s;
|
||||
if (value && value.length >= 2) {
|
||||
var prov = regionOptions.find(function (r) { return r.value === value[0]; });
|
||||
var city = prov && prov.children && prov.children.find(function (c) { return c.value === value[1]; });
|
||||
s = prov && city ? prov.label + '-' + city.label : undefined;
|
||||
} else { s = undefined; }
|
||||
setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.deliveryRegion = s; return g; });
|
||||
},
|
||||
displayRender: function (labels) { return labels && labels.length ? labels.join(' / ') : ''; }
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '交车时间'),
|
||||
React.createElement(RangePicker, {
|
||||
style: { width: '100%' },
|
||||
placeholder: ['请选择交车开始时间', '请选择交车结束时间'],
|
||||
value: dateRangeValue,
|
||||
onChange: onDateRangeChange
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: styles.formLabel }, '交车人'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请输入或选择交车人姓名',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.deliveryPerson,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.deliveryPerson = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: deliveryPersonOptions
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } },
|
||||
React.createElement(Button, { onClick: handleReset }, '重置'),
|
||||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询')
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(Card, null,
|
||||
React.createElement(Tabs, {
|
||||
activeKey: activeTab,
|
||||
onChange: function (k) { setActiveTab(k); setPage(1); },
|
||||
tabBarExtraContent: React.createElement(Button, { onClick: handleExport }, '导出'),
|
||||
items: [
|
||||
{
|
||||
key: 'pending',
|
||||
label: '待处理',
|
||||
children: React.createElement(Table, {
|
||||
columns: pendingColumns,
|
||||
dataSource: displayPending,
|
||||
rowKey: 'id',
|
||||
pagination: tablePagination,
|
||||
scroll: { x: 1490 },
|
||||
size: 'middle'
|
||||
})
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '历史记录',
|
||||
children: React.createElement(Table, {
|
||||
columns: historyColumns,
|
||||
dataSource: displayHistory,
|
||||
rowKey: 'id',
|
||||
pagination: tablePagination,
|
||||
scroll: { x: 1650 },
|
||||
size: 'middle'
|
||||
})
|
||||
}
|
||||
]
|
||||
})
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: requirementModalOpen[0],
|
||||
onCancel: function () { setRequirementModalOpen(false); },
|
||||
footer: React.createElement(Button, { onClick: function () { setRequirementModalOpen(false); } }, '关闭'),
|
||||
width: 640,
|
||||
destroyOnClose: true
|
||||
}, React.createElement('div', { style: { maxHeight: 560, overflowY: 'auto', whiteSpace: 'pre-wrap', lineHeight: 1.6, fontSize: 13 } }, requirementDocContent))
|
||||
);
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
481
web端/运维管理/车辆业务/后装设备.jsx
Normal file
481
web端/运维管理/车辆业务/后装设备.jsx
Normal file
@@ -0,0 +1,481 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 后装设备 - 运维管理-车辆业务
|
||||
var ARCO_TOKEN = {
|
||||
primary: '#165DFF',
|
||||
primaryHover: '#4080FF',
|
||||
danger: '#F53F3F',
|
||||
success: '#00B42A',
|
||||
neutral1: '#FFFFFF',
|
||||
neutral2: '#F7F8FA',
|
||||
neutral3: '#F2F3F5',
|
||||
neutral4: '#E5E6EB',
|
||||
neutral5: '#C9CDD4',
|
||||
neutral6: '#86909C',
|
||||
neutral7: '#4E5969',
|
||||
neutral8: '#1D2129',
|
||||
border: '#E5E6EB',
|
||||
fill: '#F2F3F5',
|
||||
fillSecondary: '#F7F8FA',
|
||||
shadowLight: '0 1px 2px rgba(0,0,0,0.05)',
|
||||
radiusMedium: '4px',
|
||||
radiusLarge: '8px',
|
||||
spacing8: '8px',
|
||||
spacing12: '12px',
|
||||
spacing16: '16px',
|
||||
spacing24: '24px',
|
||||
fontSize14: '14px',
|
||||
fontSize16: '16px',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif',
|
||||
link: '#165DFF'
|
||||
};
|
||||
|
||||
var DEVICE_TYPES = ['GPS', '尾板', '车身广告', 'G7安全套件', 'G7普通设备', 'G7温控设备', '备胎'];
|
||||
|
||||
var MOCK_VEHICLE_OPTIONS = ['粤A12345', '粤A67890', '粤B11111', '粤B22222', '粤C33333', '京A88888'];
|
||||
|
||||
var MOCK_LIST = [
|
||||
{ id: '1', deviceType: 'GPS', plateNo: '粤A12345', supplier: '深圳智联', lastOpType: '安装', createTime: '2025-02-20 10:30', removeTime: '', createOperator: '张明', removeOperator: '' },
|
||||
{ id: '2', deviceType: '尾板', plateNo: '粤A67890', supplier: '广州机械', lastOpType: '拆除', createTime: '2025-02-01 09:00', removeTime: '2025-02-18 14:20', createOperator: '李强', removeOperator: '王芳' },
|
||||
{ id: '3', deviceType: '车身广告', plateNo: '粤B11111', supplier: '东莞广告', lastOpType: '安装', createTime: '2025-02-15 09:00', removeTime: '', createOperator: '李强', removeOperator: '' },
|
||||
{ id: '4', deviceType: 'G7安全套件', plateNo: '粤A12345', supplier: 'G7科技', lastOpType: '安装', createTime: '2025-02-10 16:45', removeTime: '', createOperator: '张明', removeOperator: '' },
|
||||
{ id: '5', deviceType: '备胎', plateNo: '粤C33333', supplier: '华南轮胎', lastOpType: '拆除', createTime: '2025-01-20 08:00', removeTime: '2025-02-08 11:20', createOperator: '赵六', removeOperator: '赵六' }
|
||||
];
|
||||
|
||||
const Component = function () {
|
||||
var antd = window.antd;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Table = antd.Table;
|
||||
var Modal = antd.Modal;
|
||||
var Input = antd.Input;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
var Tabs = antd.Tabs;
|
||||
|
||||
var _useStateTab = React.useState('installed');
|
||||
var activeTab = _useStateTab[0];
|
||||
var setActiveTab = _useStateTab[1];
|
||||
|
||||
var _useState = React.useState('');
|
||||
var filterDeviceType = _useState[0];
|
||||
var setFilterDeviceType = _useState[1];
|
||||
|
||||
var _useState2 = React.useState('');
|
||||
var filterVehicle = _useState2[0];
|
||||
var setFilterVehicle = _useState2[1];
|
||||
|
||||
var _useState3 = React.useState([]);
|
||||
var filterDateRange = _useState3[0];
|
||||
var setFilterDateRange = _useState3[1];
|
||||
|
||||
var _useState4 = React.useState({ deviceType: '', vehicle: '', dateStart: '', dateEnd: '' });
|
||||
var appliedFilter = _useState4[0];
|
||||
var setAppliedFilter = _useState4[1];
|
||||
|
||||
var _useState5 = React.useState(1);
|
||||
var currentPage = _useState5[0];
|
||||
var setCurrentPage = _useState5[1];
|
||||
|
||||
var _useState6 = React.useState(10);
|
||||
var pageSize = _useState6[0];
|
||||
var setPageSize = _useState6[1];
|
||||
|
||||
var _useState7 = React.useState(MOCK_LIST);
|
||||
var dataList = _useState7[0];
|
||||
var setDataList = _useState7[1];
|
||||
|
||||
var _useState8 = React.useState(null);
|
||||
var viewRecord = _useState8[0];
|
||||
var setViewRecord = _useState8[1];
|
||||
|
||||
var _useState9 = React.useState(null);
|
||||
var editRecord = _useState9[0];
|
||||
var setEditRecord = _useState9[1];
|
||||
|
||||
var _useState10 = React.useState(null);
|
||||
var removeConfirmRecord = _useState10[0];
|
||||
var setRemoveConfirmRecord = _useState10[1];
|
||||
|
||||
var _useState11 = React.useState(false);
|
||||
var addModalVisible = _useState11[0];
|
||||
var setAddModalVisible = _useState11[1];
|
||||
|
||||
var _useState12 = React.useState(false);
|
||||
var showRequirementModal = _useState12[0];
|
||||
var setShowRequirementModal = _useState12[1];
|
||||
|
||||
var applyBaseFilter = function (list) {
|
||||
if (appliedFilter.deviceType) {
|
||||
list = list.filter(function (r) { return r.deviceType === appliedFilter.deviceType; });
|
||||
}
|
||||
if (appliedFilter.vehicle) {
|
||||
list = list.filter(function (r) { return r.plateNo && r.plateNo.indexOf(appliedFilter.vehicle) >= 0; });
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
var installedList = dataList.filter(function (r) { return !r.removeTime || r.removeTime === ''; });
|
||||
var removedList = dataList.filter(function (r) { return r.removeTime && r.removeTime !== ''; });
|
||||
|
||||
var installedFiltered = applyBaseFilter(installedList).filter(function (r) {
|
||||
if (appliedFilter.dateStart && r.createTime) { if (r.createTime.slice(0, 10) < appliedFilter.dateStart) return false; }
|
||||
if (appliedFilter.dateEnd && r.createTime) { if (r.createTime.slice(0, 10) > appliedFilter.dateEnd) return false; }
|
||||
return true;
|
||||
});
|
||||
var removedFiltered = applyBaseFilter(removedList).filter(function (r) {
|
||||
if (appliedFilter.dateStart && r.removeTime) { if (r.removeTime.slice(0, 10) < appliedFilter.dateStart) return false; }
|
||||
if (appliedFilter.dateEnd && r.removeTime) { if (r.removeTime.slice(0, 10) > appliedFilter.dateEnd) return false; }
|
||||
return true;
|
||||
});
|
||||
|
||||
var filteredList = activeTab === 'installed' ? installedFiltered : removedFiltered;
|
||||
var totalItems = filteredList.length;
|
||||
var totalPages = Math.ceil(totalItems / pageSize) || 1;
|
||||
var validPage = currentPage > totalPages && totalPages > 0 ? 1 : (currentPage < 1 ? 1 : currentPage);
|
||||
var startIndex = (validPage - 1) * pageSize;
|
||||
var paginatedList = filteredList.slice(startIndex, startIndex + pageSize);
|
||||
|
||||
var handleQuery = function () {
|
||||
var dateStart = '';
|
||||
var dateEnd = '';
|
||||
if (filterDateRange && filterDateRange.length >= 2 && filterDateRange[0] && filterDateRange[1]) {
|
||||
if (filterDateRange[0].format) {
|
||||
dateStart = filterDateRange[0].format('YYYY-MM-DD');
|
||||
dateEnd = filterDateRange[1].format('YYYY-MM-DD');
|
||||
} else if (window.dayjs) {
|
||||
dateStart = window.dayjs(filterDateRange[0]).format('YYYY-MM-DD');
|
||||
dateEnd = window.dayjs(filterDateRange[1]).format('YYYY-MM-DD');
|
||||
}
|
||||
}
|
||||
setAppliedFilter({
|
||||
deviceType: filterDeviceType,
|
||||
vehicle: filterVehicle,
|
||||
dateStart: dateStart,
|
||||
dateEnd: dateEnd
|
||||
});
|
||||
setCurrentPage(1);
|
||||
message.success('查询成功');
|
||||
};
|
||||
|
||||
var handleReset = function () {
|
||||
setFilterDeviceType('');
|
||||
setFilterVehicle('');
|
||||
setFilterDateRange([]);
|
||||
setAppliedFilter({ deviceType: '', vehicle: '', dateStart: '', dateEnd: '' });
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
var handleRemoveConfirm = function (row) {
|
||||
var nowStr = '2025-02-24 15:00';
|
||||
if (window.dayjs) { nowStr = window.dayjs().format('YYYY-MM-DD HH:mm'); }
|
||||
setDataList(dataList.map(function (r) {
|
||||
if (r.id === row.id) {
|
||||
return { id: r.id, deviceType: r.deviceType, plateNo: r.plateNo, supplier: r.supplier, lastOpType: '拆除', createTime: r.createTime, removeTime: nowStr, createOperator: r.createOperator, removeOperator: '当前用户' };
|
||||
}
|
||||
return r;
|
||||
}));
|
||||
setRemoveConfirmRecord(null);
|
||||
message.success('拆除成功');
|
||||
};
|
||||
|
||||
var t = ARCO_TOKEN;
|
||||
var styles = {
|
||||
page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' },
|
||||
breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
|
||||
breadcrumbLeft: { display: 'flex', alignItems: 'center' },
|
||||
breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: '8px' },
|
||||
breadcrumbCurrent: { color: t.neutral8 },
|
||||
requirementLink: { color: t.link, textDecoration: 'none', fontSize: t.fontSize14, cursor: 'pointer' },
|
||||
modalContent: { fontSize: t.fontSize14, color: t.neutral8, lineHeight: 1.6 },
|
||||
requirementSection: { marginBottom: t.spacing16 },
|
||||
requirementSectionTitle: { fontSize: t.fontSize16, fontWeight: 600, color: t.neutral8, marginBottom: 8 },
|
||||
requirementItem: { marginBottom: 8, paddingLeft: 16 },
|
||||
requirementSubItem: { marginBottom: 4, paddingLeft: 16, color: t.neutral7 },
|
||||
card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing16 },
|
||||
filterRow: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: t.spacing12 },
|
||||
label: { marginRight: t.spacing8, fontSize: t.fontSize14, color: t.neutral8, whiteSpace: 'nowrap' },
|
||||
filterRight: { marginLeft: 'auto', display: 'flex', gap: t.spacing8 },
|
||||
toolbar: { display: 'flex', justifyContent: 'flex-end', gap: t.spacing12, marginBottom: t.spacing8 }
|
||||
};
|
||||
|
||||
var deviceTypeOptions = DEVICE_TYPES.map(function (type) {
|
||||
return React.createElement(Option, { key: type, value: type }, type);
|
||||
});
|
||||
|
||||
var vehicleOptions = MOCK_VEHICLE_OPTIONS.map(function (v) {
|
||||
return React.createElement(Option, { key: v, value: v }, v);
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ style: styles.page },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.breadcrumb },
|
||||
React.createElement('div', { style: styles.breadcrumbLeft },
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('span', { style: styles.breadcrumbCurrent }, '后装设备')
|
||||
),
|
||||
React.createElement('a', { href: '#', style: styles.requirementLink, onClick: function (e) { e.preventDefault(); setShowRequirementModal(true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.card },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.filterRow },
|
||||
React.createElement('span', { style: styles.label }, '设备类型:'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择设备类型',
|
||||
style: { width: 160 },
|
||||
value: filterDeviceType || undefined,
|
||||
onChange: function (v) { setFilterDeviceType(v || ''); },
|
||||
allowClear: true
|
||||
}, deviceTypeOptions),
|
||||
React.createElement('span', { style: styles.label }, '使用车辆:'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入搜索',
|
||||
style: { width: 180 },
|
||||
value: filterVehicle || undefined,
|
||||
onChange: function (v) { setFilterVehicle(v || ''); },
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: function (input, opt) {
|
||||
var c = opt && opt.children;
|
||||
return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0;
|
||||
}
|
||||
}, vehicleOptions),
|
||||
React.createElement('span', { style: styles.label }, '安装时间:'),
|
||||
React.createElement(RangePicker, {
|
||||
style: { width: 260 },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: ['开始日期', '结束日期'],
|
||||
value: filterDateRange,
|
||||
onChange: function (dates) { setFilterDateRange(dates || []); }
|
||||
}),
|
||||
React.createElement('div', { style: styles.filterRight },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'),
|
||||
React.createElement(Button, { onClick: handleReset }, '重置')
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.card },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.toolbar },
|
||||
activeTab === 'installed' && React.createElement(Button, { type: 'primary', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-新增后装设备'); } }, '新增'),
|
||||
React.createElement(Button, { onClick: function () { message.info('根据筛选条件导出excel'); } }, '导出')
|
||||
),
|
||||
React.createElement(Tabs, {
|
||||
activeKey: activeTab,
|
||||
onChange: function (key) { setActiveTab(key); setCurrentPage(1); },
|
||||
items: [
|
||||
{
|
||||
key: 'installed',
|
||||
label: '已安装',
|
||||
children: React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
columns: [
|
||||
{ title: '设备类型', dataIndex: 'deviceType', key: 'deviceType', width: 120 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 },
|
||||
{ title: '供应商', dataIndex: 'supplier', key: 'supplier', width: 120 },
|
||||
{ title: '安装时间', dataIndex: 'createTime', key: 'createTime', width: 160 },
|
||||
{ title: '安装人', dataIndex: 'createOperator', key: 'createOperator', width: 100 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 160,
|
||||
render: function (_, row) {
|
||||
return React.createElement(React.Fragment, null,
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-查看'); } }, '查看'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-编辑'); } }, '编辑'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { setRemoveConfirmRecord(row); } }, '拆除')
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
dataSource: paginatedList,
|
||||
pagination: {
|
||||
current: validPage,
|
||||
pageSize: pageSize,
|
||||
total: totalItems,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: function (total) { return '共 ' + total + ' 条'; },
|
||||
onChange: function (page, size) {
|
||||
setCurrentPage(page);
|
||||
if (size !== pageSize) setPageSize(size);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
key: 'removed',
|
||||
label: '拆除记录',
|
||||
children: React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
columns: [
|
||||
{ title: '设备类型', dataIndex: 'deviceType', key: 'deviceType', width: 120 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 },
|
||||
{ title: '供应商', dataIndex: 'supplier', key: 'supplier', width: 120 },
|
||||
{ title: '拆除时间', dataIndex: 'removeTime', key: 'removeTime', width: 160 },
|
||||
{ title: '拆除人', dataIndex: 'removeOperator', key: 'removeOperator', width: 100 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 80,
|
||||
render: function (_, row) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('请查看运维管理-车辆业务-后装设备-查看'); } }, '查看');
|
||||
}
|
||||
}
|
||||
],
|
||||
dataSource: paginatedList,
|
||||
pagination: {
|
||||
current: validPage,
|
||||
pageSize: pageSize,
|
||||
total: totalItems,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: function (total) { return '共 ' + total + ' 条'; },
|
||||
onChange: function (page, size) {
|
||||
setCurrentPage(page);
|
||||
if (size !== pageSize) setPageSize(size);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
]
|
||||
})
|
||||
),
|
||||
viewRecord && React.createElement(Modal, {
|
||||
title: '查看后装设备',
|
||||
open: !!viewRecord,
|
||||
onCancel: function () { setViewRecord(null); },
|
||||
footer: React.createElement(Button, { onClick: function () { setViewRecord(null); } }, '关闭'),
|
||||
width: 520,
|
||||
children: viewRecord ? React.createElement('div', { style: { lineHeight: '2' } },
|
||||
React.createElement('div', null, '设备类型:', viewRecord.deviceType),
|
||||
React.createElement('div', null, '车牌号:', viewRecord.plateNo),
|
||||
React.createElement('div', null, '供应商:', viewRecord.supplier),
|
||||
React.createElement('div', null, '安装时间:', viewRecord.createTime || '-'),
|
||||
React.createElement('div', null, '安装人:', viewRecord.createOperator || '-'),
|
||||
React.createElement('div', null, '拆除时间:', viewRecord.removeTime || '-'),
|
||||
React.createElement('div', null, '拆除人:', viewRecord.removeOperator || '-')
|
||||
) : null
|
||||
}),
|
||||
editRecord && React.createElement(Modal, {
|
||||
title: '编辑后装设备',
|
||||
open: !!editRecord,
|
||||
onCancel: function () { setEditRecord(null); },
|
||||
onOk: function () {
|
||||
setEditRecord(null);
|
||||
message.success('保存成功');
|
||||
},
|
||||
okText: '保存',
|
||||
cancelText: '取消',
|
||||
width: 520,
|
||||
children: editRecord ? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 16 } },
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '设备类型'), React.createElement(Select, { style: { width: '100%' }, value: editRecord.deviceType, options: DEVICE_TYPES.map(function (d) { return { label: d, value: d }; }) })),
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '车牌号'), React.createElement(Input, { style: { width: '100%' }, value: editRecord.plateNo, readOnly: true })),
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '供应商'), React.createElement(Input, { style: { width: '100%' }, defaultValue: editRecord.supplier }))
|
||||
) : null
|
||||
}),
|
||||
removeConfirmRecord && React.createElement(Modal, {
|
||||
title: '确认拆除',
|
||||
open: !!removeConfirmRecord,
|
||||
onCancel: function () { setRemoveConfirmRecord(null); },
|
||||
onOk: function () { handleRemoveConfirm(removeConfirmRecord); },
|
||||
okText: '确认拆除',
|
||||
cancelText: '取消',
|
||||
okButtonProps: { danger: true },
|
||||
children: removeConfirmRecord ? React.createElement('div', null, '确定要拆除该车辆「', removeConfirmRecord.deviceType, '」后装设备吗?') : null
|
||||
}),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: showRequirementModal,
|
||||
onCancel: function () { setShowRequirementModal(false); },
|
||||
footer: React.createElement(Button, { onClick: function () { setShowRequirementModal(false); } }, '关闭'),
|
||||
width: 720,
|
||||
children: React.createElement('div', { style: styles.modalContent },
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '1. 面包屑:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '1.1. 运维管理-车辆业务-后装设备')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '2. 筛选:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '2.1. 支持通过设备类型、使用车辆、安装日期进行管理,点击查询后,筛选条件与列表内容联动。点击重置会回到默认筛选条件并在列表展示结果:'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.1.1. 设备类型:选择器,包括GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.1.2. 使用车辆:选择器,支持通过输入框对输入内容模糊搜索,并下拉对应选项;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '2.1.3. 安装时间:日期选择器,支持单输入框内双日历选择开始-结束时间;')
|
||||
),
|
||||
React.createElement('div', { style: styles.requirementSection },
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '3. 列表:'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '列表分为2个tab,已安装/拆除记录;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '当已安装列表对应车辆存在车身广告/尾板时,在备车时会自动拉取车身广告/尾板为有的状态;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '当已安装列表不存在车身广告/尾板时,在备车时车身广告/尾板为无的状态;'),
|
||||
React.createElement('div', { style: styles.requirementItem }, '如果在备车/交车时手动取消或勾选车身广告尾板,并完成提交,后装设备列表也会新增一条该车辆的车身广告/尾板安装数据;'),
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '3.1. 已安装:'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1. 列表展示所有后装设备信息,字段依次为:设备类型、车牌号、供应商、安装时间、安装人、操作;列表右上角为新增、导出;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.1. 设备类型:显示后装设备类型,包括:GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.2. 车牌号:显示后装设备对应车牌号信息;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.3. 供应商:显示后装设备供应商信息;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.4. 安装时间:显示后装设备安装时间,格式为YYYY-MM-DD HH:MM;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.5. 安装人:显示后装设备安装用户名称;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.1.1.6. 操作:支持查看、编辑、拆除;'),
|
||||
React.createElement('div', { style: styles.requirementSectionTitle }, '3.2. 拆除记录:'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1. 列表展示所有后装设备拆除记录,字段依次为:设备类型、车牌号、供应商、拆除时间、拆除人、操作;列表右上角为新增、导出;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.1. 设备类型:显示后装设备类型,包括:GPS、尾板、车身广告、G7安全套件、G7普通设备、G7温控设备、备胎;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.2. 车牌号:显示后装设备对应车牌号信息;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.3. 供应商:显示后装设备供应商信息;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.4. 拆除时间:显示后装设备拆除时间,格式为YYYY-MM-DD HH:MM;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.5. 拆除人:显示后装设备拆除用户名称;'),
|
||||
React.createElement('div', { style: styles.requirementSubItem }, '3.2.1.6. 操作:支持查看;')
|
||||
)
|
||||
)
|
||||
}),
|
||||
addModalVisible && React.createElement(Modal, {
|
||||
title: '新增后装设备',
|
||||
open: addModalVisible,
|
||||
onCancel: function () { setAddModalVisible(false); },
|
||||
onOk: function () {
|
||||
setDataList([{ id: String(Date.now()), deviceType: 'GPS', plateNo: '粤A12345', supplier: '新供应商', lastOpType: '安装', createTime: '2025-02-24 12:00', removeTime: '', createOperator: '当前用户', removeOperator: '' }].concat(dataList));
|
||||
setAddModalVisible(false);
|
||||
message.success('新增成功');
|
||||
},
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
width: 520,
|
||||
children: React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 16 } },
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '设备类型'), React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择', options: DEVICE_TYPES.map(function (d) { return { label: d, value: d }; }) })),
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '使用车辆'), React.createElement(Select, { style: { width: '100%' }, placeholder: '请选择或搜索', showSearch: true, options: MOCK_VEHICLE_OPTIONS.map(function (v) { return { label: v, value: v }; }) })),
|
||||
React.createElement('div', null, React.createElement('span', { style: styles.label }, '供应商'), React.createElement(Input, { style: { width: '100%' }, placeholder: '请输入供应商' }))
|
||||
)
|
||||
})
|
||||
);
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
function mount() {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', mount);
|
||||
} else {
|
||||
setTimeout(mount, 0);
|
||||
}
|
||||
}
|
||||
652
web端/运维管理/车辆业务/备车管理.jsx
Executable file
652
web端/运维管理/车辆业务/备车管理.jsx
Executable file
@@ -0,0 +1,652 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 备车管理 - 车辆资产管理后台(按 antd 规范)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var Tabs = antd.Tabs;
|
||||
var Table = antd.Table;
|
||||
var Modal = antd.Modal;
|
||||
var Steps = antd.Steps;
|
||||
var Input = antd.Input;
|
||||
var Radio = antd.Radio;
|
||||
var Space = antd.Space;
|
||||
var Row = antd.Row;
|
||||
var Col = antd.Col;
|
||||
var message = antd.message;
|
||||
|
||||
var RangePicker = DatePicker.RangePicker;
|
||||
|
||||
// 当前视图:'list' | 'add'
|
||||
var viewState = useState('list');
|
||||
var currentView = viewState[0];
|
||||
var setCurrentView = viewState[1];
|
||||
|
||||
var filterState = useState({
|
||||
dateStart: '',
|
||||
dateEnd: '',
|
||||
operator: undefined,
|
||||
plateNo: undefined,
|
||||
vehicleType: undefined,
|
||||
parkingLot: undefined
|
||||
});
|
||||
var filters = filterState[0];
|
||||
var setFilters = filterState[1];
|
||||
// 实际参与列表筛选的条件(点击查询后与 filters 同步)
|
||||
var appliedFilterState = useState({
|
||||
dateStart: '',
|
||||
dateEnd: '',
|
||||
operator: undefined,
|
||||
plateNo: undefined,
|
||||
vehicleType: undefined,
|
||||
parkingLot: undefined
|
||||
});
|
||||
var appliedFilters = appliedFilterState[0];
|
||||
var setAppliedFilters = appliedFilterState[1];
|
||||
|
||||
var activeTabState = useState('completed');
|
||||
var activeTab = activeTabState[0];
|
||||
var setActiveTab = activeTabState[1];
|
||||
|
||||
var defaultInspection = [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '正常', treadDepth: '', remark: '' }];
|
||||
var completedListState = useState([
|
||||
{ id: '1', completeDate: '2025-02-10 09:30', operator: '张三', vehicleType: '厢式货车', plateNo: '京A12345', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=1', 'https://picsum.photos/200/150?random=2', 'https://picsum.photos/200/150?random=3'], tailboard: '有', hasTrailer: '有', trailerPlate: '京B67890', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=4', 'https://picsum.photos/200/150?random=5'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '异常', treadDepth: '', remark: '有划痕' }] },
|
||||
{ id: '2', completeDate: '2025-02-09 10:15', operator: '李四', vehicleType: '平板货车', plateNo: '京C11111', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=6'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '异常', treadDepth: '3.1mm', remark: '需更换' }] },
|
||||
{ id: '3', completeDate: '2025-02-08 11:00', operator: '王五', vehicleType: '厢式货车', plateNo: '京D22222', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=7', 'https://picsum.photos/200/150?random=8'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection },
|
||||
{ id: '4', completeDate: '2025-02-07 13:45', operator: '赵六', vehicleType: '栏板货车', plateNo: '京E33333', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京E33334', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=9'], inspectionList: defaultInspection },
|
||||
{ id: '5', completeDate: '2025-02-06 08:20', operator: '张三', vehicleType: '平板货车', plateNo: '京A23456', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M6666666', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=10'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection },
|
||||
{ id: '6', completeDate: '2025-02-05 14:30', operator: '李四', vehicleType: '厢式货车', plateNo: '京B34567', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M5555555', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=11', 'https://picsum.photos/200/150?random=12'], inspectionList: defaultInspection },
|
||||
{ id: '7', completeDate: '2025-02-04 16:00', operator: '王五', vehicleType: '栏板货车', plateNo: '京C45678', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M4444444', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=13', 'https://picsum.photos/200/150?random=14'], tailboard: '有', hasTrailer: '有', trailerPlate: '京C45679', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection },
|
||||
{ id: '8', completeDate: '2025-02-03 09:00', operator: '赵六', vehicleType: '厢式货车', plateNo: '京D56789', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M3333333', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=15'], inspectionList: defaultInspection },
|
||||
{ id: '9', completeDate: '2025-02-02 10:30', operator: '张三', vehicleType: '平板货车', plateNo: '京E67890', brand: '东风', model: 'DFH1200', vin: 'LGHXCAE28M2222222', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=16', 'https://picsum.photos/200/150?random=17', 'https://picsum.photos/200/150?random=18'], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection },
|
||||
{ id: '10', completeDate: '2025-02-01 15:45', operator: '李四', vehicleType: '厢式货车', plateNo: '京A78901', brand: '福田', model: 'BJ1200', vin: 'LGHXCAE28M1111111', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '有', trailerPlate: '京A78902', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }
|
||||
]);
|
||||
var completedList = completedListState[0];
|
||||
var setCompletedList = completedListState[1];
|
||||
|
||||
var pendingListState = useState([
|
||||
{ id: 'p1', operateDate: '2025-02-10 14:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10001', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=19', 'https://picsum.photos/200/150?random=20'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p2', operateDate: '2025-02-09 09:15', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10002', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M9990002', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=21'] },
|
||||
{ id: 'p3', operateDate: '2025-02-08 11:45', operator: '张三', vehicleType: '栏板货车', plateNo: '京F10003', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M9990003', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=22'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10004', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p4', operateDate: '2025-02-07 08:00', operator: '李四', vehicleType: '厢式货车', plateNo: '京F10005', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M9990004', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p5', operateDate: '2025-02-06 13:20', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10006', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M9990005', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=23', 'https://picsum.photos/200/150?random=24', 'https://picsum.photos/200/150?random=25'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=26', 'https://picsum.photos/200/150?random=27'] },
|
||||
{ id: 'p6', operateDate: '2025-02-05 15:30', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10007', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M9990006', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京F10008', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p7', operateDate: '2025-02-04 10:00', operator: '张三', vehicleType: '厢式货车', plateNo: '京F10009', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M9990007', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=28', 'https://picsum.photos/200/150?random=29'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p8', operateDate: '2025-02-03 16:45', operator: '李四', vehicleType: '栏板货车', plateNo: '京F10010', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M9990008', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=30'] },
|
||||
{ id: 'p9', operateDate: '2025-02-02 09:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10011', brand: '江淮', model: 'HFC1200', vin: 'LGHXCAE28M9990009', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=31'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10012', flawPhotoCount: 0, flawPhotos: [] },
|
||||
{ id: 'p10', operateDate: '2025-02-01 14:00', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10013', brand: '重汽', model: 'ZZ1200', vin: 'LGHXCAE28M9990010', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }
|
||||
]);
|
||||
var pendingList = pendingListState[0];
|
||||
var setPendingList = pendingListState[1];
|
||||
|
||||
var pageState = useState(1);
|
||||
var page = pageState[0];
|
||||
var setPage = pageState[1];
|
||||
var pageSizeState = useState(10);
|
||||
var pageSize = pageSizeState[0];
|
||||
var setPageSize = pageSizeState[1];
|
||||
|
||||
var operatorOptions = useMemo(function () { return [{ value: '张三', label: '张三' }, { value: '李四', label: '李四' }, { value: '王五', label: '王五' }, { value: '赵六', label: '赵六' }]; }, []);
|
||||
var plateOptions = useMemo(function () { return [{ value: '京A12345', label: '京A12345' }, { value: '京C11111', label: '京C11111' }, { value: '京D22222', label: '京D22222' }, { value: '京E33333', label: '京E33333' }]; }, []);
|
||||
var vehicleTypeOptions = useMemo(function () { return [{ value: '厢式货车', label: '厢式货车' }, { value: '平板货车', label: '平板货车' }, { value: '栏板货车', label: '栏板货车' }]; }, []);
|
||||
var parkingOptions = useMemo(function () { return [{ value: '朝阳停车场', label: '朝阳停车场' }, { value: '海淀停车场', label: '海淀停车场' }, { value: '丰台停车场', label: '丰台停车场' }, { value: '西城停车场', label: '西城停车场' }]; }, []);
|
||||
|
||||
var reqDocOpenState = useState(false);
|
||||
var photoModalState = useState({ open: false, photos: [], currentIndex: 0 });
|
||||
|
||||
var addStepState = useState(1);
|
||||
var addFormState = useState({
|
||||
plateNo: '',
|
||||
vehicleType: '',
|
||||
brand: '',
|
||||
vin: '',
|
||||
bodyAd: '无',
|
||||
adPhotos: [],
|
||||
tailboard: '无',
|
||||
hasTrailer: '',
|
||||
trailerPlate: '',
|
||||
flawPhotos: [],
|
||||
inspectionList: [
|
||||
{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' },
|
||||
{ checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' },
|
||||
{ checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '', remark: '' },
|
||||
{ checkType: '车身', checkItem: '车身外观', result: '正常', treadDepth: '', remark: '' }
|
||||
]
|
||||
});
|
||||
|
||||
var filteredCompleted = useMemo(function () {
|
||||
var list = completedList.slice();
|
||||
if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; });
|
||||
if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; });
|
||||
if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; });
|
||||
if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; });
|
||||
if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d >= appliedFilters.dateStart; });
|
||||
if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; });
|
||||
return list.sort(function (a, b) { return (b.completeDate || '').localeCompare(a.completeDate || ''); });
|
||||
}, [completedList, appliedFilters]);
|
||||
|
||||
var filteredPending = useMemo(function () {
|
||||
var list = pendingList.slice();
|
||||
if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; });
|
||||
if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; });
|
||||
if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; });
|
||||
if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; });
|
||||
if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d >= appliedFilters.dateStart; });
|
||||
if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; });
|
||||
return list.sort(function (a, b) { return (b.operateDate || '').localeCompare(a.operateDate || ''); });
|
||||
}, [pendingList, appliedFilters]);
|
||||
|
||||
var displayCompleted = useMemo(function () {
|
||||
var start = (page - 1) * pageSize;
|
||||
return filteredCompleted.slice(start, start + pageSize);
|
||||
}, [filteredCompleted, page, pageSize]);
|
||||
|
||||
var displayPending = useMemo(function () {
|
||||
var start = (page - 1) * pageSize;
|
||||
return filteredPending.slice(start, start + pageSize);
|
||||
}, [filteredPending, page, pageSize]);
|
||||
|
||||
var totalCompleted = filteredCompleted.length;
|
||||
var totalPending = filteredPending.length;
|
||||
var totalCount = activeTab === 'completed' ? totalCompleted : totalPending;
|
||||
|
||||
var handleQuery = useCallback(function () {
|
||||
setAppliedFilters({ dateStart: filters.dateStart, dateEnd: filters.dateEnd, operator: filters.operator, plateNo: filters.plateNo, vehicleType: filters.vehicleType, parkingLot: filters.parkingLot });
|
||||
setPage(1);
|
||||
}, [filters.dateStart, filters.dateEnd, filters.operator, filters.plateNo, filters.vehicleType, filters.parkingLot]);
|
||||
var handleReset = useCallback(function () {
|
||||
var empty = { dateStart: '', dateEnd: '', operator: undefined, plateNo: undefined, vehicleType: undefined, parkingLot: undefined };
|
||||
setFilters(empty);
|
||||
setAppliedFilters(empty);
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
var handleExport = useCallback(function () {
|
||||
if (activeTab !== 'completed') {
|
||||
message.warning('请在「已完成」Tab 下导出');
|
||||
return;
|
||||
}
|
||||
var rows = filteredCompleted;
|
||||
var headers = ['备车时间', '备车人', '车辆类型', '车牌号', '品牌', '型号', '车辆识别代码', '停车场', '车身广告', '广告照片', '尾板', '是否有挂', '挂车牌号', '瑕疵照片'];
|
||||
var csv = headers.join(',') + '\n';
|
||||
rows.forEach(function (r) {
|
||||
csv += [r.completeDate, r.operator, r.vehicleType, r.plateNo, r.brand, r.model, r.vin, r.parkingLot, r.bodyAd, (r.adPhotoCount || 0) + '张', r.tailboard, r.hasTrailer, r.trailerPlate || '', (r.flawPhotoCount || 0) + '张'].join(',') + '\n';
|
||||
});
|
||||
var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = '备车管理导出_' + new Date().getTime() + '.csv';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
message.success('导出成功');
|
||||
}, [activeTab, filteredCompleted]);
|
||||
|
||||
var openPhotoModal = useCallback(function (photos, index) {
|
||||
if (!photos || photos.length === 0) return;
|
||||
photoModalState[1]({ open: true, photos: photos, currentIndex: index || 0 });
|
||||
}, []);
|
||||
|
||||
var addForm = addFormState[0];
|
||||
var setAddForm = addFormState[1];
|
||||
var addStep = addStepState[0];
|
||||
var setAddStep = addStepState[1];
|
||||
|
||||
var handleAddSave = useCallback(function () {
|
||||
var d = new Date();
|
||||
var pad = function (n) { return String(n).padStart(2, '0'); };
|
||||
var dateTimeStr = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
||||
var item = {
|
||||
id: 'p' + Date.now(),
|
||||
operateDate: dateTimeStr,
|
||||
operator: '当前用户',
|
||||
vehicleType: addForm.vehicleType || '厢式货车',
|
||||
plateNo: addForm.plateNo,
|
||||
brand: addForm.brand || '-',
|
||||
model: addForm.model || '-',
|
||||
vin: addForm.vin,
|
||||
parkingLot: addForm.parkingLot || '朝阳停车场',
|
||||
bodyAd: addForm.bodyAd,
|
||||
adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0,
|
||||
adPhotos: addForm.adPhotos || [],
|
||||
tailboard: addForm.tailboard,
|
||||
hasTrailer: addForm.trailerPlate ? '有' : '无',
|
||||
trailerPlate: addForm.trailerPlate || '',
|
||||
flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0,
|
||||
flawPhotos: addForm.flawPhotos || []
|
||||
};
|
||||
setPendingList(pendingList.concat([item]));
|
||||
setCurrentView('list');
|
||||
setActiveTab('pending');
|
||||
setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList });
|
||||
setAddStep(1);
|
||||
message.success('已保存至待提交列表');
|
||||
}, [addForm, pendingList]);
|
||||
|
||||
var handleAddNext = useCallback(function () {
|
||||
if (!addForm.plateNo) {
|
||||
message.warning('请选择车牌号');
|
||||
return;
|
||||
}
|
||||
setAddStep(2);
|
||||
}, [addForm.plateNo]);
|
||||
|
||||
var handleAddSubmit = useCallback(function () {
|
||||
var d = new Date();
|
||||
var pad = function (n) { return String(n).padStart(2, '0'); };
|
||||
var dateTimeStr = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
||||
var item = {
|
||||
id: String(Date.now()),
|
||||
completeDate: dateTimeStr,
|
||||
operator: '当前用户',
|
||||
vehicleType: addForm.vehicleType || '厢式货车',
|
||||
plateNo: addForm.plateNo,
|
||||
brand: addForm.brand || '-',
|
||||
model: addForm.model || '-',
|
||||
vin: addForm.vin,
|
||||
parkingLot: addForm.parkingLot || '朝阳停车场',
|
||||
bodyAd: addForm.bodyAd,
|
||||
adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0,
|
||||
adPhotos: addForm.adPhotos || [],
|
||||
tailboard: addForm.tailboard,
|
||||
hasTrailer: addForm.trailerPlate ? '有' : '无',
|
||||
trailerPlate: addForm.trailerPlate || '',
|
||||
flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0,
|
||||
flawPhotos: addForm.flawPhotos || [],
|
||||
inspectionList: addForm.inspectionList || []
|
||||
};
|
||||
setCompletedList(completedList.concat([item]));
|
||||
setCurrentView('list');
|
||||
setActiveTab('completed');
|
||||
setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList });
|
||||
setAddStep(1);
|
||||
message.success('提交成功');
|
||||
}, [addForm, completedList]);
|
||||
|
||||
var onPlateSelect = useCallback(function (plateNo) {
|
||||
setAddForm(function (prev) {
|
||||
var next = {};
|
||||
for (var k in prev) next[k] = prev[k];
|
||||
next.plateNo = plateNo;
|
||||
next.vehicleType = '厢式货车';
|
||||
next.brand = '东风';
|
||||
next.vin = 'LGHXCAE28M' + Math.floor(Math.random() * 10000000);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var requirementDocContent = '#.面包屑:运维管理-车辆业务-备车管理,支持点击对应模块跳转\n\n#筛选:支持通过备车日期、备车人、车牌号、车辆类型、停车场等方式进行筛选;\n1.备车日期:日期选择器,点击显示两个日历控件,支持选择开始和结束时间,时间格式为YYYY-MM-DD;\n2.备车人:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n3.车牌号:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n4.车辆类型:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n5.停车场:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n6.右侧显示查询、重置按钮,点击查询按钮按筛选条件显示列表内容,点击重置清除筛选条件并按默认条件显示列表内容;\n\n#列表:\n列表分为两个tab,分别为:已完成、待检查;已完成列表右侧包含新增、导出按钮,下方增加分页功能,支持选择单页显示数据记录条数;\n1.已完成tab显示完成车辆记录及备车检查两个步骤表单并点击提交的记录;\n2.待检查tab显示新增备车时只点击保存,但未提交的记录;\n3.点击新增按钮,打开新页面进行新增表单页;\n4.导出需全选/多选对应数据记录,再点击导出下载csv;\n5.列表以区域进行数据权限划分,用户表中对应区域的用户只能查询自己区域的数据;';
|
||||
|
||||
// 日期范围变化(antd RangePicker 返回 dayjs 或 [string, string] 依项目而定,此处用字符串存)
|
||||
function onDateRangeChange(dates, dateStrings) {
|
||||
setFilters(function (f) {
|
||||
var g = {};
|
||||
for (var k in f) g[k] = f[k];
|
||||
g.dateStart = (dateStrings && dateStrings[0]) || '';
|
||||
g.dateEnd = (dateStrings && dateStrings[1]) || '';
|
||||
return g;
|
||||
});
|
||||
}
|
||||
|
||||
var dateRangeValue = useMemo(function () {
|
||||
if (!filters.dateStart && !filters.dateEnd) return null;
|
||||
try {
|
||||
if (typeof window.dayjs === 'function' && filters.dateStart && filters.dateEnd) {
|
||||
return [window.dayjs(filters.dateStart), window.dayjs(filters.dateEnd)];
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}, [filters.dateStart, filters.dateEnd]);
|
||||
|
||||
// 已完成表格列(含多选、照片链接、操作列查看按钮,点击不打开新页面)
|
||||
function getCompletedColumns(openPhoto) {
|
||||
return [
|
||||
{ title: '备车时间', dataIndex: 'completeDate', key: 'completeDate', width: 160 },
|
||||
{ title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 },
|
||||
{ title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 },
|
||||
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 },
|
||||
{ title: '型号', dataIndex: 'model', key: 'model', width: 100 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 },
|
||||
{ title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 },
|
||||
{ title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 },
|
||||
{ title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 },
|
||||
{ title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) {
|
||||
var photos = r.adPhotos && r.adPhotos.length > 0 ? r.adPhotos : [];
|
||||
return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-';
|
||||
}},
|
||||
{ title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 },
|
||||
{ title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 },
|
||||
{ title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } },
|
||||
{ title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) {
|
||||
var photos = r.flawPhotos && r.flawPhotos.length > 0 ? r.flawPhotos : [];
|
||||
return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-';
|
||||
}},
|
||||
{ title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () {} }, '查看');
|
||||
}}
|
||||
];
|
||||
}
|
||||
|
||||
function getPendingColumns(openPhoto, onEdit) {
|
||||
return [
|
||||
{ title: '创建时间', dataIndex: 'operateDate', key: 'operateDate', width: 160 },
|
||||
{ title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 },
|
||||
{ title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 },
|
||||
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 },
|
||||
{ title: '型号', dataIndex: 'model', key: 'model', width: 100 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 },
|
||||
{ title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 },
|
||||
{ title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 },
|
||||
{ title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 },
|
||||
{ title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) {
|
||||
var photos = (r.adPhotos && r.adPhotos.length > 0) ? r.adPhotos : [];
|
||||
return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-';
|
||||
}},
|
||||
{ title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 },
|
||||
{ title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 },
|
||||
{ title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } },
|
||||
{ title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) {
|
||||
var photos = (r.flawPhotos && r.flawPhotos.length > 0) ? r.flawPhotos : [];
|
||||
return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-';
|
||||
}},
|
||||
{ title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () {
|
||||
setAddForm(function (prev) {
|
||||
var n = {};
|
||||
for (var k in prev) n[k] = prev[k];
|
||||
n.plateNo = r.plateNo; n.vehicleType = r.vehicleType; n.brand = r.brand; n.vin = r.vin;
|
||||
n.bodyAd = r.bodyAd; n.adPhotos = r.adPhotos || []; n.tailboard = r.tailboard;
|
||||
n.trailerPlate = r.trailerPlate || ''; n.flawPhotos = r.flawPhotos || [];
|
||||
return n;
|
||||
});
|
||||
setAddStep(1);
|
||||
setCurrentView('add');
|
||||
} }, '编辑');
|
||||
}}
|
||||
];
|
||||
}
|
||||
|
||||
var tablePagination = useMemo(function () {
|
||||
return {
|
||||
current: page,
|
||||
pageSize: pageSize,
|
||||
total: totalCount,
|
||||
showSizeChanger: true,
|
||||
showTotal: function (t) { return '共 ' + t + ' 条'; },
|
||||
pageSizeOptions: ['10', '20', '50'],
|
||||
onChange: function (p, size) {
|
||||
setPage(p);
|
||||
if (size !== pageSize) setPageSize(size);
|
||||
}
|
||||
};
|
||||
}, [page, pageSize, totalCount]);
|
||||
|
||||
// —————— 列表页 ——————
|
||||
function ListView() {
|
||||
var breadcrumbItems = [
|
||||
{ title: '运维管理' },
|
||||
{ title: '车辆业务' },
|
||||
{ title: '备车管理' }
|
||||
];
|
||||
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
|
||||
React.createElement(Breadcrumb, { items: breadcrumbItems }),
|
||||
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: reqDocOpenState[0],
|
||||
onCancel: function () { reqDocOpenState[1](false); },
|
||||
footer: null,
|
||||
width: 720,
|
||||
styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14 } }
|
||||
}, requirementDocContent.split('\n').map(function (line, idx) {
|
||||
var trimmed = line;
|
||||
if (trimmed.indexOf('#') === 0) {
|
||||
return React.createElement('div', { key: idx, style: { fontWeight: 'bold', marginTop: idx === 0 ? 0 : 16, marginBottom: 8, paddingBottom: 4, borderBottom: '1px solid #f0f0f0' } }, trimmed.replace(/^#\.?/, '').trim());
|
||||
}
|
||||
if (/^\d+\./.test(trimmed)) return React.createElement('div', { key: idx, style: { marginLeft: 12, marginBottom: 4 } }, trimmed);
|
||||
if (trimmed === '') return React.createElement('div', { key: idx, style: { height: 12 } });
|
||||
return React.createElement('div', { key: idx, style: { marginBottom: 8 } }, trimmed);
|
||||
})),
|
||||
React.createElement(Card, { style: { marginBottom: 16 } },
|
||||
React.createElement('div', {
|
||||
style: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr 1fr',
|
||||
gap: '16px 24px',
|
||||
alignItems: 'start',
|
||||
flex: 1,
|
||||
minWidth: 0
|
||||
}
|
||||
},
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 4 } }, '备车日期'),
|
||||
React.createElement(RangePicker, {
|
||||
value: dateRangeValue,
|
||||
onChange: onDateRangeChange,
|
||||
style: { width: '100%' },
|
||||
placeholder: ['开始日期', '结束日期']
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 4 } }, '备车人'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择备车人',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.operator,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.operator = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: operatorOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 4 } }, '车牌号'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择车牌号',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.plateNo,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.plateNo = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: plateOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 4 } }, '车辆类型'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择车辆类型',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.vehicleType,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.vehicleType = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: vehicleTypeOptions
|
||||
})
|
||||
),
|
||||
React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 4 } }, '停车场'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择停车场',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
value: filters.parkingLot,
|
||||
onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.parkingLot = v; return g; }); },
|
||||
style: { width: '100%' },
|
||||
options: parkingOptions
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } },
|
||||
React.createElement(Button, { onClick: handleReset }, '重置'),
|
||||
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询')
|
||||
)
|
||||
),
|
||||
React.createElement(Card, null,
|
||||
React.createElement(Tabs, {
|
||||
activeKey: activeTab,
|
||||
onChange: function (k) { setActiveTab(k); setPage(1); },
|
||||
tabBarExtraContent: React.createElement('div', { style: { display: 'flex', gap: 8 } },
|
||||
React.createElement(Button, { type: 'primary', onClick: function () {} }, '新增'),
|
||||
activeTab === 'completed' ? React.createElement(Button, { onClick: handleExport }, '导出') : null
|
||||
),
|
||||
items: [
|
||||
{
|
||||
key: 'completed',
|
||||
label: '已完成',
|
||||
children: React.createElement(Table, {
|
||||
columns: getCompletedColumns(openPhotoModal),
|
||||
dataSource: displayCompleted,
|
||||
rowKey: 'id',
|
||||
pagination: tablePagination,
|
||||
scroll: { x: 1400 },
|
||||
size: 'middle'
|
||||
})
|
||||
},
|
||||
{
|
||||
key: 'pending',
|
||||
label: '待提交',
|
||||
children: React.createElement(Table, {
|
||||
columns: getPendingColumns(openPhotoModal),
|
||||
dataSource: displayPending,
|
||||
rowKey: 'id',
|
||||
pagination: tablePagination,
|
||||
scroll: { x: 1400 },
|
||||
size: 'middle'
|
||||
})
|
||||
}
|
||||
]
|
||||
})
|
||||
),
|
||||
// 照片预览:全屏遮罩 + 放大图片,点击遮罩关闭
|
||||
photoModalState[0].open && photoModalState[0].photos && photoModalState[0].photos.length > 0 ? React.createElement('div', {
|
||||
style: {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.75)',
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
onClick: function () { photoModalState[1]({ open: false, photos: [], currentIndex: 0 }); }
|
||||
}, React.createElement('div', {
|
||||
style: { position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', cursor: 'default', maxWidth: '100%', maxHeight: '100%' },
|
||||
onClick: function (e) { if (e) e.stopPropagation(); }
|
||||
},
|
||||
React.createElement('img', {
|
||||
src: photoModalState[0].photos[photoModalState[0].currentIndex] || photoModalState[0].photos[0],
|
||||
alt: '',
|
||||
style: { maxWidth: '90vw', maxHeight: '85vh', objectFit: 'contain' }
|
||||
}),
|
||||
photoModalState[0].photos.length > 1 ? React.createElement('div', { style: { marginTop: 16, display: 'flex', alignItems: 'center', gap: 16 } },
|
||||
React.createElement(Button, {
|
||||
disabled: photoModalState[0].currentIndex === 0,
|
||||
onClick: function (e) { e.stopPropagation(); photoModalState[1]({ open: true, photos: photoModalState[0].photos, currentIndex: Math.max(0, photoModalState[0].currentIndex - 1) }); }
|
||||
}, '上一张'),
|
||||
React.createElement('span', { style: { color: '#fff' } }, (photoModalState[0].currentIndex + 1) + ' / ' + photoModalState[0].photos.length),
|
||||
React.createElement(Button, {
|
||||
disabled: photoModalState[0].currentIndex >= photoModalState[0].photos.length - 1,
|
||||
onClick: function (e) { e.stopPropagation(); photoModalState[1]({ open: true, photos: photoModalState[0].photos, currentIndex: Math.min(photoModalState[0].photos.length - 1, photoModalState[0].currentIndex + 1) }); }
|
||||
}, '下一张')
|
||||
) : null
|
||||
)) : null
|
||||
);
|
||||
}
|
||||
|
||||
// —————— 新增页 ——————
|
||||
function AddView() {
|
||||
var form = addFormState[0];
|
||||
var setForm = addFormState[1];
|
||||
var step = addStepState[0];
|
||||
var addBreadcrumbItems = [
|
||||
{ title: '运维管理' },
|
||||
{ title: '车辆业务' },
|
||||
{ title: '备车管理' },
|
||||
{ title: '新增备车' }
|
||||
];
|
||||
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
|
||||
React.createElement(Breadcrumb, { items: addBreadcrumbItems, style: { marginBottom: 16 } }),
|
||||
React.createElement(Card, null,
|
||||
React.createElement(Steps, { current: step - 1, style: { marginBottom: 24 }, items: [
|
||||
{ title: '步骤1 车辆记录' },
|
||||
{ title: '步骤2 备车检查' }
|
||||
] }),
|
||||
step === 1 ? React.createElement(Row, { gutter: [16, 16] },
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车牌号 *'), React.createElement(Select, { placeholder: '请选择车牌号(仅库存车辆)', allowClear: true, showSearch: true, optionFilterProp: 'label', value: form.plateNo || undefined, onChange: onPlateSelect, style: { width: 200 }, options: plateOptions })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车辆类型'), React.createElement(Input, { value: form.vehicleType, readOnly: true, style: { width: 200, background: '#f5f5f5' } })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '品牌'), React.createElement(Input, { value: form.brand, readOnly: true, style: { width: 200, background: '#f5f5f5' } })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车架号'), React.createElement(Input, { value: form.vin, readOnly: true, style: { width: 200, background: '#f5f5f5' } })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '车身广告 *'), React.createElement(Radio.Group, { value: form.bodyAd, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.bodyAd = e.target.value; return n; }); }, options: [{ value: '无', label: '无' }, { value: '有', label: '有' }] })),
|
||||
form.bodyAd === '有' ? React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '广告照片(最多5张)'), React.createElement(Space, null, (form.adPhotos || []).slice(0, 5).map(function (url, i) { return React.createElement('img', { key: i, src: url, alt: '', style: { width: 60, height: 60, objectFit: 'cover', borderRadius: 4 } }); }), (form.adPhotos || []).length < 5 ? React.createElement(Button, { onClick: function () { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = (p.adPhotos || []).concat(['https://picsum.photos/200/150?r=' + Date.now()]); return n; }); } }, '上传') : null)) : null,
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '尾板 *'), React.createElement(Radio.Group, { value: form.tailboard, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = e.target.value; return n; }); }, options: [{ value: '无', label: '无' }, { value: '有', label: '有' }] })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '挂车牌号(有挂时填写)'), React.createElement(Input, { value: form.trailerPlate, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.trailerPlate = e.target.value; return n; }); }, placeholder: '不填视为无挂', style: { width: 200 } })),
|
||||
React.createElement(Col, { span: 24 }, React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '瑕疵照片(最多5张)'), React.createElement(Space, null, (form.flawPhotos || []).slice(0, 5).map(function (url, i) { return React.createElement('img', { key: i, src: url, alt: '', style: { width: 60, height: 60, objectFit: 'cover', borderRadius: 4 } }); }), (form.flawPhotos || []).length < 5 ? React.createElement(Button, { onClick: function () { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.flawPhotos = (p.flawPhotos || []).concat(['https://picsum.photos/200/150?f=' + Date.now()]); return n; }); } }, '上传') : null)),
|
||||
React.createElement(Col, { span: 24 }, React.createElement(Space, null, React.createElement(Button, { type: 'primary', onClick: handleAddNext }, '下一步'), React.createElement(Button, { onClick: handleAddSave }, '保存'), React.createElement(Button, { onClick: function () { setCurrentView('list'); } }, '取消')))
|
||||
) : React.createElement('div', null,
|
||||
React.createElement('div', { style: { marginBottom: 16, fontWeight: 600 } }, '备车检查'),
|
||||
React.createElement(Table, {
|
||||
dataSource: form.inspectionList || [],
|
||||
rowKey: function (_, i) { return i; },
|
||||
pagination: false,
|
||||
columns: [
|
||||
{ title: '检查类型', dataIndex: 'checkType', width: 100 },
|
||||
{ title: '检查项', dataIndex: 'checkItem', width: 120 },
|
||||
{ title: '检查项结果', dataIndex: 'result', width: 120, render: function (val, record, i) {
|
||||
return React.createElement(Select, { value: val, onChange: function (v) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { result: v }); n.inspectionList = list; return n; }); }, style: { width: '100%' }, options: [{ value: '正常', label: '正常' }, { value: '异常', label: '异常' }] });
|
||||
}},
|
||||
{ title: '胎纹深度(轮胎时)', key: 'treadDepth', width: 140, render: function (_, record, i) {
|
||||
if (record.checkItem && record.checkItem.indexOf('胎') !== -1) {
|
||||
return React.createElement(Input, { value: record.treadDepth, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { treadDepth: e.target.value }); n.inspectionList = list; return n; }); }, placeholder: '胎纹深度' });
|
||||
}
|
||||
return '-';
|
||||
}},
|
||||
{ title: '备注', dataIndex: 'remark', render: function (val, record, i) {
|
||||
return React.createElement(Input, { value: val, onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; var list = (p.inspectionList || []).slice(); list[i] = Object.assign({}, list[i], { remark: e.target.value }); n.inspectionList = list; return n; }); }, placeholder: '备注' });
|
||||
}}
|
||||
]
|
||||
}),
|
||||
React.createElement('div', { style: { marginTop: 16 } }, React.createElement(Space, null, React.createElement(Button, { type: 'primary', onClick: handleAddSubmit }, '提交'), React.createElement(Button, { onClick: handleAddSave }, '保存'), React.createElement(Button, { onClick: function () { setCurrentView('list'); } }, '取消')))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return ListView();
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
143
web端/运维管理/车辆业务/新增后装设备.jsx
Normal file
143
web端/运维管理/车辆业务/新增后装设备.jsx
Normal file
@@ -0,0 +1,143 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 新增后装设备 - 运维管理-车辆业务
|
||||
var ARCO_TOKEN = {
|
||||
primary: '#165DFF',
|
||||
primaryHover: '#4080FF',
|
||||
neutral1: '#FFFFFF',
|
||||
neutral4: '#E5E6EB',
|
||||
neutral6: '#86909C',
|
||||
neutral8: '#1D2129',
|
||||
border: '#E5E6EB',
|
||||
fill: '#F2F3F5',
|
||||
shadowLight: '0 1px 2px rgba(0,0,0,0.05)',
|
||||
radiusLarge: '8px',
|
||||
spacing16: '16px',
|
||||
spacing24: '24px',
|
||||
fontSize14: '14px',
|
||||
fontSize16: '16px',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif',
|
||||
link: '#165DFF'
|
||||
};
|
||||
|
||||
var DEVICE_TYPES = ['GPS', '尾板', '车身广告', 'G7安全套件', 'G7普通设备', 'G7温控设备', '备胎'];
|
||||
|
||||
var MOCK_VEHICLE_OPTIONS = ['粤A12345', '粤A67890', '粤B11111', '粤B22222', '粤C33333', '京A88888'];
|
||||
|
||||
const Component = function () {
|
||||
var antd = window.antd;
|
||||
var Select = antd.Select;
|
||||
var Button = antd.Button;
|
||||
var Input = antd.Input;
|
||||
var message = antd.message;
|
||||
var Option = Select.Option;
|
||||
|
||||
var _useState = React.useState('');
|
||||
var deviceType = _useState[0];
|
||||
var setDeviceType = _useState[1];
|
||||
|
||||
var _useState2 = React.useState('');
|
||||
var vehicle = _useState2[0];
|
||||
var setVehicle = _useState2[1];
|
||||
|
||||
var _useState3 = React.useState('');
|
||||
var supplier = _useState3[0];
|
||||
var setSupplier = _useState3[1];
|
||||
|
||||
var handleSubmit = function () {
|
||||
message.success('新增成功');
|
||||
setDeviceType('');
|
||||
setVehicle('');
|
||||
setSupplier('');
|
||||
};
|
||||
|
||||
var t = ARCO_TOKEN;
|
||||
var styles = {
|
||||
page: { padding: t.spacing24, fontFamily: t.fontFamily, backgroundColor: t.fill, minHeight: '100vh' },
|
||||
breadcrumb: { marginBottom: t.spacing16, fontSize: t.fontSize14, color: t.neutral6, display: 'flex', alignItems: 'center' },
|
||||
breadcrumbLink: { color: t.link, textDecoration: 'none', marginRight: '8px' },
|
||||
breadcrumbCurrent: { color: t.neutral8 },
|
||||
card: { backgroundColor: t.neutral1, borderRadius: t.radiusLarge, boxShadow: t.shadowLight, marginBottom: t.spacing16, padding: t.spacing24, maxWidth: 560 },
|
||||
label: { display: 'block', marginBottom: 8, fontSize: t.fontSize14, color: t.neutral8 },
|
||||
row: { marginBottom: t.spacing16 },
|
||||
actions: { marginTop: t.spacing24, display: 'flex', gap: 12 }
|
||||
};
|
||||
|
||||
var deviceTypeOptions = DEVICE_TYPES.map(function (type) {
|
||||
return React.createElement(Option, { key: type, value: type }, type);
|
||||
});
|
||||
|
||||
var vehicleOptions = MOCK_VEHICLE_OPTIONS.map(function (v) {
|
||||
return React.createElement(Option, { key: v, value: v }, v);
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ style: styles.page },
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.breadcrumb },
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '运维管理'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('a', { href: '#', style: styles.breadcrumbLink, onClick: function (e) { e.preventDefault(); } }, '车辆业务'),
|
||||
React.createElement('span', { style: { marginRight: '8px' } }, '/'),
|
||||
React.createElement('span', { style: styles.breadcrumbCurrent }, '新增后装设备')
|
||||
),
|
||||
React.createElement(
|
||||
'div',
|
||||
{ style: styles.card },
|
||||
React.createElement('div', { style: { fontSize: t.fontSize16, fontWeight: 600, marginBottom: t.spacing24, color: t.neutral8 } }, '新增后装设备'),
|
||||
React.createElement('div', { style: styles.row },
|
||||
React.createElement('label', { style: styles.label }, '设备类型'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择设备类型',
|
||||
style: { width: '100%' },
|
||||
value: deviceType || undefined,
|
||||
onChange: function (v) { setDeviceType(v || ''); },
|
||||
allowClear: true
|
||||
}, deviceTypeOptions)
|
||||
),
|
||||
React.createElement('div', { style: styles.row },
|
||||
React.createElement('label', { style: styles.label }, '使用车辆'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入搜索',
|
||||
style: { width: '100%' },
|
||||
value: vehicle || undefined,
|
||||
onChange: function (v) { setVehicle(v || ''); },
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
filterOption: function (input, opt) {
|
||||
var c = opt && opt.children;
|
||||
return c && String(c).toLowerCase().indexOf((input || '').toLowerCase()) >= 0;
|
||||
}
|
||||
}, vehicleOptions)
|
||||
),
|
||||
React.createElement('div', { style: styles.row },
|
||||
React.createElement('label', { style: styles.label }, '供应商'),
|
||||
React.createElement(Input, {
|
||||
placeholder: '请输入供应商',
|
||||
value: supplier,
|
||||
onChange: function (e) { setSupplier(e.target.value); }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: styles.actions },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'),
|
||||
React.createElement(Button, { onClick: function () { setDeviceType(''); setVehicle(''); setSupplier(''); } }, '重置')
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
function mount() {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', mount);
|
||||
} else {
|
||||
setTimeout(mount, 0);
|
||||
}
|
||||
}
|
||||
472
web端/运维管理/车辆业务/新增备车.jsx
Normal file
472
web端/运维管理/车辆业务/新增备车.jsx
Normal file
@@ -0,0 +1,472 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 车辆业务 - 新增备车(ONEOS运管平台,布局参照新增租赁合同)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
var useRef = React.useRef;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var Select = antd.Select;
|
||||
var Input = antd.Input;
|
||||
var Button = antd.Button;
|
||||
var Switch = antd.Switch;
|
||||
var Drawer = antd.Drawer;
|
||||
var Table = antd.Table;
|
||||
var message = antd.message;
|
||||
var Modal = antd.Modal;
|
||||
var Space = antd.Space;
|
||||
|
||||
// 车牌管理 mock:车牌号及对应车辆信息、后装设备(车身广告、尾板)
|
||||
var plateManageList = [
|
||||
{ plateNo: '京A12345', vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, tailboard: true },
|
||||
{ plateNo: '京C11111', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', bodyAd: false, tailboard: false },
|
||||
{ plateNo: '京D22222', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', bodyAd: true, tailboard: false },
|
||||
{ plateNo: '京E33333', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', bodyAd: false, tailboard: true },
|
||||
{ plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true }
|
||||
];
|
||||
|
||||
// 备车检查单:类别 -> 检查项目列表
|
||||
var inspectionCategoryItems = {
|
||||
'车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'],
|
||||
'仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'],
|
||||
'驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'],
|
||||
'轮胎': ['前左胎', '前右胎', '后左胎', '后右胎'],
|
||||
'液位检查': ['冷却液', '制动液', '玻璃水'],
|
||||
'外观检查': ['车身外观', '漆面', '玻璃'],
|
||||
'车辆外观': ['整车外观'],
|
||||
'其他': ['其他检查项'],
|
||||
'随车工具': ['三角牌', '灭火器', '反光背心'],
|
||||
'随车证件': ['行驶证', '营运证', '保险单'],
|
||||
'整车': ['整车状态'],
|
||||
'燃料电池系统': ['氢系统', '储氢瓶'],
|
||||
'冷机': ['冷机运行'],
|
||||
'制动系统': ['制动踏板', '驻车制动']
|
||||
};
|
||||
|
||||
function buildInspectionList() {
|
||||
var list = [];
|
||||
var categories = Object.keys(inspectionCategoryItems);
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
var cat = categories[i];
|
||||
var items = inspectionCategoryItems[cat];
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
list.push({ category: cat, checkItem: items[j], checked: true, remark: '' });
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
var formState = useState({
|
||||
plateNo: '',
|
||||
vehicleType: '',
|
||||
brand: '',
|
||||
model: '',
|
||||
vin: '',
|
||||
bodyAd: true,
|
||||
adPhotos: [],
|
||||
tailboard: false,
|
||||
trailerPlate: '',
|
||||
flawPhotos: [],
|
||||
inspectionList: buildInspectionList()
|
||||
});
|
||||
var form = formState[0];
|
||||
var setForm = formState[1];
|
||||
|
||||
var drawerOpenState = useState(false);
|
||||
var photoPreviewState = useState({ open: false, url: null });
|
||||
var reqDocOpenState = useState(false);
|
||||
var adPhotoInputRef = useRef(null);
|
||||
var flawPhotoInputRef = useRef(null);
|
||||
var plateOptions = useMemo(function () {
|
||||
return plateManageList.map(function (p) { return { value: p.plateNo, label: p.plateNo }; });
|
||||
}, []);
|
||||
|
||||
var onPlateSelect = useCallback(function (plateNo) {
|
||||
var vehicle = plateManageList.find(function (p) { return p.plateNo === plateNo; });
|
||||
if (!vehicle) return;
|
||||
setForm(function (prev) {
|
||||
var next = {};
|
||||
for (var k in prev) next[k] = prev[k];
|
||||
next.plateNo = vehicle.plateNo;
|
||||
next.vehicleType = vehicle.vehicleType || '';
|
||||
next.brand = vehicle.brand || '';
|
||||
next.model = vehicle.model || '';
|
||||
next.vin = vehicle.vin || '';
|
||||
next.bodyAd = !!vehicle.bodyAd;
|
||||
next.tailboard = !!vehicle.tailboard;
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var updateInspection = useCallback(function (index, field, value) {
|
||||
setForm(function (prev) {
|
||||
var n = {};
|
||||
for (var k in prev) n[k] = prev[k];
|
||||
var list = (prev.inspectionList || []).slice();
|
||||
var row = list[index] || {};
|
||||
list[index] = Object.assign({}, row, { [field]: value });
|
||||
n.inspectionList = list;
|
||||
return n;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var addAdPhoto = useCallback(function () {
|
||||
if ((form.adPhotos || []).length >= 4) { message.warning('最多上传4张'); return; }
|
||||
if (adPhotoInputRef.current) adPhotoInputRef.current.click();
|
||||
}, [form.adPhotos]);
|
||||
var onAdPhotoFileChange = useCallback(function (e) {
|
||||
var files = e.target.files;
|
||||
if (!files || files.length === 0) return;
|
||||
setForm(function (p) {
|
||||
var current = p.adPhotos || [];
|
||||
var remain = 4 - current.length;
|
||||
if (remain <= 0) return p;
|
||||
var urls = [];
|
||||
for (var i = 0; i < files.length && urls.length < remain; i++) {
|
||||
if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i]));
|
||||
}
|
||||
if (urls.length === 0) return p;
|
||||
var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = current.concat(urls); return n;
|
||||
});
|
||||
e.target.value = '';
|
||||
}, []);
|
||||
var removeAdPhoto = useCallback(function (idx) {
|
||||
setForm(function (p) {
|
||||
var n = {};
|
||||
for (var k in p) n[k] = p[k];
|
||||
var arr = (p.adPhotos || []).slice();
|
||||
var url = arr[idx];
|
||||
if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url);
|
||||
arr.splice(idx, 1);
|
||||
n.adPhotos = arr;
|
||||
return n;
|
||||
});
|
||||
}, []);
|
||||
var addFlawPhoto = useCallback(function () {
|
||||
if ((form.flawPhotos || []).length >= 4) { message.warning('最多上传4张'); return; }
|
||||
if (flawPhotoInputRef.current) flawPhotoInputRef.current.click();
|
||||
}, [form.flawPhotos]);
|
||||
var onFlawPhotoFileChange = useCallback(function (e) {
|
||||
var files = e.target.files;
|
||||
if (!files || files.length === 0) return;
|
||||
setForm(function (p) {
|
||||
var current = p.flawPhotos || [];
|
||||
var remain = 4 - current.length;
|
||||
if (remain <= 0) return p;
|
||||
var urls = [];
|
||||
for (var i = 0; i < files.length && urls.length < remain; i++) {
|
||||
if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i]));
|
||||
}
|
||||
if (urls.length === 0) return p;
|
||||
var n = {}; for (var k in p) n[k] = p[k]; n.flawPhotos = current.concat(urls); return n;
|
||||
});
|
||||
e.target.value = '';
|
||||
}, []);
|
||||
var removeFlawPhoto = useCallback(function (idx) {
|
||||
setForm(function (p) {
|
||||
var n = {};
|
||||
for (var k in p) n[k] = p[k];
|
||||
var arr = (p.flawPhotos || []).slice();
|
||||
var url = arr[idx];
|
||||
if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url);
|
||||
arr.splice(idx, 1);
|
||||
n.flawPhotos = arr;
|
||||
return n;
|
||||
});
|
||||
}, []);
|
||||
|
||||
var handleSubmit = useCallback(function () {
|
||||
if (!form.plateNo) { message.warning('请选择车牌号'); return; }
|
||||
message.success('备车成功');
|
||||
// 保存数据并跳转至备车管理-已完成(实际项目接路由)
|
||||
if (typeof window !== 'undefined' && window.history) window.history.back();
|
||||
}, [form.plateNo]);
|
||||
var handleSave = useCallback(function () {
|
||||
if (!form.plateNo) { message.warning('请选择车牌号'); return; }
|
||||
message.success('暂存成功');
|
||||
// 保存数据并跳转至备车管理-待提交
|
||||
if (typeof window !== 'undefined' && window.history) window.history.back();
|
||||
}, [form.plateNo]);
|
||||
|
||||
// 布局样式(参照新增租赁合同)
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' },
|
||||
formColFull: { flex: '0 0 100%', marginBottom: 8, boxSizing: 'border-box' },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
labelRequired: { color: '#ff4d4f', marginRight: 4 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }
|
||||
};
|
||||
|
||||
var FormItem = function (props) {
|
||||
var colStyle = props.fullWidth ? styles.formColFull : styles.formCol;
|
||||
return React.createElement('div', { style: colStyle },
|
||||
React.createElement('label', { style: styles.label },
|
||||
props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null,
|
||||
props.label
|
||||
),
|
||||
props.children
|
||||
);
|
||||
};
|
||||
|
||||
var inspectionList = form.inspectionList || [];
|
||||
var categoryRowSpans = useMemo(function () {
|
||||
var list = inspectionList;
|
||||
var spans = [];
|
||||
var i = 0;
|
||||
while (i < list.length) {
|
||||
var cat = list[i].category;
|
||||
var start = i;
|
||||
while (i < list.length && list[i].category === cat) i++;
|
||||
spans[start] = i - start;
|
||||
for (var j = start + 1; j < i; j++) spans[j] = 0;
|
||||
}
|
||||
return spans;
|
||||
}, [form.inspectionList]);
|
||||
|
||||
var inspectionColumns = [
|
||||
{
|
||||
title: '类别',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
width: 120,
|
||||
onCell: function (_, index) { return { rowSpan: categoryRowSpans[index] !== undefined ? categoryRowSpans[index] : 1 }; }
|
||||
},
|
||||
{ title: '检查项目', dataIndex: 'checkItem', key: 'checkItem', width: 180 },
|
||||
{
|
||||
title: '检查情况',
|
||||
key: 'checked',
|
||||
width: 100,
|
||||
render: function (_, record, index) {
|
||||
return React.createElement(Switch, {
|
||||
checked: record.checked !== false,
|
||||
onChange: function (checked) { updateInspection(index, 'checked', checked); }
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
render: function (val, record, index) {
|
||||
return React.createElement(Input, {
|
||||
value: val,
|
||||
onChange: function (e) { updateInspection(index, 'remark', e.target.value); },
|
||||
placeholder: '备注'
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 方形拍照图标(广告照片与瑕疵共用)
|
||||
var cameraIconBox = function (onClick) {
|
||||
return React.createElement('div', {
|
||||
role: 'button',
|
||||
tabIndex: 0,
|
||||
onClick: onClick,
|
||||
style: { width: 64, height: 64, border: '1px dashed #d9d9d9', borderRadius: 4, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', background: '#fafafa', boxSizing: 'border-box' }
|
||||
}, React.createElement('svg', { width: 28, height: 28, viewBox: '0 0 24 24', fill: 'none', stroke: '#999', strokeWidth: 2 },
|
||||
React.createElement('path', { d: 'M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z' }),
|
||||
React.createElement('circle', { cx: 12, cy: 13, r: 4 })
|
||||
));
|
||||
};
|
||||
|
||||
var requirementDocContent = '新增备车\n\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增备车\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂;\n2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;';
|
||||
|
||||
var breadcrumbNodes = [
|
||||
React.createElement('span', { key: '1' }, '运维管理'),
|
||||
React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '3' }, '车辆业务'),
|
||||
React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '5' }, '备车管理'),
|
||||
React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '新增备车')
|
||||
];
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes),
|
||||
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '车牌号', required: true },
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择车牌号',
|
||||
style: { width: '100%' },
|
||||
value: form.plateNo || undefined,
|
||||
onChange: onPlateSelect,
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
optionFilterProp: 'label',
|
||||
filterOption: function (input, opt) {
|
||||
return (opt && opt.label && String(opt.label).toLowerCase().indexOf((input || '').toLowerCase()) >= 0);
|
||||
},
|
||||
options: plateOptions
|
||||
})
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆类型' },
|
||||
React.createElement(Input, { value: form.vehicleType, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '品牌' },
|
||||
React.createElement(Input, { value: form.brand, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '型号' },
|
||||
React.createElement(Input, { value: form.model, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆识别代码' },
|
||||
React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph0' }),
|
||||
React.createElement(FormItem, { label: '车身广告' },
|
||||
React.createElement(Switch, {
|
||||
checked: form.bodyAd,
|
||||
checkedChildren: '开',
|
||||
unCheckedChildren: '关',
|
||||
onChange: function (checked) {
|
||||
setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.bodyAd = checked; return n; });
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph1' }),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph2' }),
|
||||
React.createElement(FormItem, { label: '尾板' },
|
||||
React.createElement(Switch, {
|
||||
checked: form.tailboard,
|
||||
checkedChildren: '开',
|
||||
unCheckedChildren: '关',
|
||||
onChange: function (checked) {
|
||||
setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = checked; return n; });
|
||||
}
|
||||
})
|
||||
),
|
||||
React.createElement(FormItem, { label: '广告照片', fullWidth: true },
|
||||
React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
||||
React.createElement('input', { type: 'file', ref: adPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onAdPhotoFileChange }),
|
||||
(form.adPhotos || []).length < 4 ? cameraIconBox(addAdPhoto) : null,
|
||||
(form.adPhotos || []).map(function (url, i) {
|
||||
return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } },
|
||||
React.createElement('img', {
|
||||
src: url,
|
||||
alt: '',
|
||||
style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
|
||||
onClick: function () { photoPreviewState[1]({ open: true, url: url }); }
|
||||
}),
|
||||
React.createElement('span', {
|
||||
style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' },
|
||||
onClick: function () { removeAdPhoto(i); }
|
||||
}, '×')
|
||||
);
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(FormItem, { label: '是否有挂' },
|
||||
React.createElement(Input, {
|
||||
placeholder: '请输入挂车牌号,不输入为无挂',
|
||||
value: form.trailerPlate,
|
||||
onChange: function (e) {
|
||||
setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.trailerPlate = e.target.value; return n; });
|
||||
},
|
||||
style: { width: '100%', maxWidth: 320 }
|
||||
})
|
||||
),
|
||||
React.createElement(FormItem, { label: '瑕疵', fullWidth: true },
|
||||
React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
||||
React.createElement('input', { type: 'file', ref: flawPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onFlawPhotoFileChange }),
|
||||
(form.flawPhotos || []).length < 4 ? cameraIconBox(addFlawPhoto) : null,
|
||||
(form.flawPhotos || []).map(function (url, i) {
|
||||
return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } },
|
||||
React.createElement('img', {
|
||||
src: url,
|
||||
alt: '',
|
||||
style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
|
||||
onClick: function () { photoPreviewState[1]({ open: true, url: url }); }
|
||||
}),
|
||||
React.createElement('span', {
|
||||
style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' },
|
||||
onClick: function () { removeFlawPhoto(i); }
|
||||
}, '×')
|
||||
);
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆检查', fullWidth: true },
|
||||
React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单')
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'),
|
||||
React.createElement(Button, { onClick: handleSave }, '保存'),
|
||||
React.createElement(Button, { onClick: function () { if (typeof window !== 'undefined' && window.history) window.history.back(); } }, '取消')
|
||||
),
|
||||
React.createElement(Drawer, {
|
||||
title: '备车检查单',
|
||||
placement: 'right',
|
||||
width: 640,
|
||||
open: drawerOpenState[0],
|
||||
onClose: function () { drawerOpenState[1](false); },
|
||||
styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } },
|
||||
footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } },
|
||||
React.createElement(Button, { onClick: function () { drawerOpenState[1](false); } }, '返回'),
|
||||
React.createElement(Button, { type: 'primary', onClick: function () { drawerOpenState[1](false); message.success('检查单已保存'); } }, '提交')
|
||||
)
|
||||
},
|
||||
React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } },
|
||||
React.createElement(Table, {
|
||||
columns: inspectionColumns,
|
||||
dataSource: form.inspectionList || [],
|
||||
rowKey: function (_, i) { return i; },
|
||||
pagination: false,
|
||||
size: 'small'
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: reqDocOpenState[0],
|
||||
onCancel: function () { reqDocOpenState[1](false); },
|
||||
footer: null,
|
||||
width: 720,
|
||||
styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.8 } }
|
||||
}, requirementDocContent),
|
||||
photoPreviewState[0].open && photoPreviewState[0].url
|
||||
? React.createElement(Modal, {
|
||||
title: '预览',
|
||||
open: true,
|
||||
footer: null,
|
||||
onCancel: function () { photoPreviewState[1]({ open: false, url: null }); }
|
||||
}, React.createElement('img', { src: photoPreviewState[0].url, alt: '', style: { width: '100%' } }))
|
||||
: null
|
||||
);
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
275
web端/运维管理/车辆业务/查看备车.jsx
Normal file
275
web端/运维管理/车辆业务/查看备车.jsx
Normal file
@@ -0,0 +1,275 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 车辆业务 - 查看备车(与新增备车相同布局,全部只读)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var Input = antd.Input;
|
||||
var Button = antd.Button;
|
||||
var Drawer = antd.Drawer;
|
||||
var Table = antd.Table;
|
||||
var Modal = antd.Modal;
|
||||
|
||||
var plateManageList = [
|
||||
{ plateNo: '京A12345', vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, tailboard: true },
|
||||
{ plateNo: '京C11111', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', bodyAd: false, tailboard: false },
|
||||
{ plateNo: '京D22222', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', bodyAd: true, tailboard: false },
|
||||
{ plateNo: '京E33333', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', bodyAd: false, tailboard: true },
|
||||
{ plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true }
|
||||
];
|
||||
|
||||
var inspectionCategoryItems = {
|
||||
'车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'],
|
||||
'仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'],
|
||||
'驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'],
|
||||
'轮胎': ['前左胎', '前右胎', '后左胎', '后右胎'],
|
||||
'液位检查': ['冷却液', '制动液', '玻璃水'],
|
||||
'外观检查': ['车身外观', '漆面', '玻璃'],
|
||||
'车辆外观': ['整车外观'],
|
||||
'其他': ['其他检查项'],
|
||||
'随车工具': ['三角牌', '灭火器', '反光背心'],
|
||||
'随车证件': ['行驶证', '营运证', '保险单'],
|
||||
'整车': ['整车状态'],
|
||||
'燃料电池系统': ['氢系统', '储氢瓶'],
|
||||
'冷机': ['冷机运行'],
|
||||
'制动系统': ['制动踏板', '驻车制动']
|
||||
};
|
||||
|
||||
function buildInspectionList() {
|
||||
var list = [];
|
||||
var categories = Object.keys(inspectionCategoryItems);
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
var cat = categories[i];
|
||||
var items = inspectionCategoryItems[cat];
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
list.push({ category: cat, checkItem: items[j], checked: true, remark: '' });
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// 只读:一条已填写的备车记录(不可编辑)
|
||||
var form = useMemo(function () {
|
||||
return {
|
||||
plateNo: '京A12345',
|
||||
vehicleType: '厢式货车',
|
||||
brand: '东风',
|
||||
model: 'DFH1180',
|
||||
vin: 'LGHXCAE28M1234567',
|
||||
bodyAd: true,
|
||||
adPhotos: ['https://picsum.photos/200/150?r=1', 'https://picsum.photos/200/150?r=2'],
|
||||
tailboard: true,
|
||||
trailerPlate: '京B67890',
|
||||
flawPhotos: ['https://picsum.photos/200/150?f=1'],
|
||||
inspectionList: buildInspectionList()
|
||||
};
|
||||
}, []);
|
||||
|
||||
var drawerOpenState = useState(false);
|
||||
var photoPreviewState = useState({ open: false, url: null });
|
||||
var reqDocOpenState = useState(false);
|
||||
|
||||
var styles = {
|
||||
page: { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontSize: 14 },
|
||||
breadcrumb: { marginBottom: 16, color: '#666' },
|
||||
breadcrumbSep: { margin: '0 8px', color: '#999' },
|
||||
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
|
||||
cardBody: { padding: '20px 24px' },
|
||||
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
|
||||
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' },
|
||||
formColFull: { flex: '0 0 100%', marginBottom: 8, boxSizing: 'border-box' },
|
||||
label: { display: 'block', marginBottom: 6, color: '#333' },
|
||||
labelRequired: { color: '#ff4d4f', marginRight: 4 },
|
||||
footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }
|
||||
};
|
||||
|
||||
var FormItem = function (props) {
|
||||
var colStyle = props.fullWidth ? styles.formColFull : styles.formCol;
|
||||
return React.createElement('div', { style: colStyle },
|
||||
React.createElement('label', { style: styles.label },
|
||||
props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null,
|
||||
props.label
|
||||
),
|
||||
props.children
|
||||
);
|
||||
};
|
||||
|
||||
var inspectionList = form.inspectionList || [];
|
||||
var categoryRowSpans = useMemo(function () {
|
||||
var list = inspectionList;
|
||||
var spans = [];
|
||||
var i = 0;
|
||||
while (i < list.length) {
|
||||
var cat = list[i].category;
|
||||
var start = i;
|
||||
while (i < list.length && list[i].category === cat) i++;
|
||||
spans[start] = i - start;
|
||||
for (var j = start + 1; j < i; j++) spans[j] = 0;
|
||||
}
|
||||
return spans;
|
||||
}, [form.inspectionList]);
|
||||
|
||||
var inspectionColumns = [
|
||||
{
|
||||
title: '类别',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
width: 120,
|
||||
onCell: function (_, index) { return { rowSpan: categoryRowSpans[index] !== undefined ? categoryRowSpans[index] : 1 }; }
|
||||
},
|
||||
{ title: '检查项目', dataIndex: 'checkItem', key: 'checkItem', width: 180 },
|
||||
{
|
||||
title: '检查情况',
|
||||
key: 'checked',
|
||||
width: 100,
|
||||
render: function (_, record) { return record.checked !== false ? '开' : '关'; }
|
||||
},
|
||||
{ title: '备注', dataIndex: 'remark', key: 'remark', render: function (val) { return val || '-'; } }
|
||||
];
|
||||
|
||||
var requirementDocContent = '新增备车\n\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增备车\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂;\n2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;';
|
||||
|
||||
var breadcrumbNodes = [
|
||||
React.createElement('span', { key: '1' }, '运维管理'),
|
||||
React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '3' }, '车辆业务'),
|
||||
React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '5' }, '备车管理'),
|
||||
React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '),
|
||||
React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '查看备车')
|
||||
];
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
|
||||
React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes),
|
||||
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { reqDocOpenState[1](true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardBody },
|
||||
React.createElement('div', { style: styles.formRow },
|
||||
React.createElement(FormItem, { label: '车牌号', required: true },
|
||||
React.createElement(Input, { value: form.plateNo, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆类型' },
|
||||
React.createElement(Input, { value: form.vehicleType, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '品牌' },
|
||||
React.createElement(Input, { value: form.brand, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '型号' },
|
||||
React.createElement(Input, { value: form.model, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆识别代码' },
|
||||
React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph0' }),
|
||||
React.createElement(FormItem, { label: '车身广告' },
|
||||
React.createElement(Input, { value: form.bodyAd ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph1' }),
|
||||
React.createElement('div', { style: styles.formCol, key: 'ph2' }),
|
||||
React.createElement(FormItem, { label: '尾板' },
|
||||
React.createElement(Input, { value: form.tailboard ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '广告照片', fullWidth: true },
|
||||
React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
||||
(form.adPhotos || []).map(function (url, i) {
|
||||
return React.createElement('img', {
|
||||
key: i,
|
||||
src: url,
|
||||
alt: '',
|
||||
style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
|
||||
onClick: function () { photoPreviewState[1]({ open: true, url: url }); }
|
||||
});
|
||||
}),
|
||||
(form.adPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null
|
||||
)
|
||||
),
|
||||
React.createElement(FormItem, { label: '是否有挂' },
|
||||
React.createElement(Input, { value: form.trailerPlate || '无', disabled: true, style: { width: '100%', maxWidth: 320, background: '#f5f5f5' } })
|
||||
),
|
||||
React.createElement(FormItem, { label: '瑕疵', fullWidth: true },
|
||||
React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } },
|
||||
(form.flawPhotos || []).map(function (url, i) {
|
||||
return React.createElement('img', {
|
||||
key: i,
|
||||
src: url,
|
||||
alt: '',
|
||||
style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
|
||||
onClick: function () { photoPreviewState[1]({ open: true, url: url }); }
|
||||
});
|
||||
}),
|
||||
(form.flawPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null
|
||||
)
|
||||
),
|
||||
React.createElement(FormItem, { label: '车辆检查', fullWidth: true },
|
||||
React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单')
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: styles.footer },
|
||||
React.createElement(Button, { onClick: function () { if (typeof window !== 'undefined' && window.history) window.history.back(); } }, '返回')
|
||||
),
|
||||
React.createElement(Drawer, {
|
||||
title: '备车检查单',
|
||||
placement: 'right',
|
||||
width: 640,
|
||||
open: drawerOpenState[0],
|
||||
onClose: function () { drawerOpenState[1](false); },
|
||||
styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } },
|
||||
footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end' } },
|
||||
React.createElement(Button, { onClick: function () { drawerOpenState[1](false); } }, '返回')
|
||||
)
|
||||
},
|
||||
React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } },
|
||||
React.createElement(Table, {
|
||||
columns: inspectionColumns,
|
||||
dataSource: form.inspectionList || [],
|
||||
rowKey: function (_, i) { return i; },
|
||||
pagination: false,
|
||||
size: 'small'
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: reqDocOpenState[0],
|
||||
onCancel: function () { reqDocOpenState[1](false); },
|
||||
footer: null,
|
||||
width: 720,
|
||||
styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.8 } }
|
||||
}, requirementDocContent),
|
||||
photoPreviewState[0].open && photoPreviewState[0].url
|
||||
? React.createElement(Modal, {
|
||||
title: '预览',
|
||||
open: true,
|
||||
footer: null,
|
||||
onCancel: function () { photoPreviewState[1]({ open: false, url: null }); }
|
||||
}, React.createElement('img', { src: photoPreviewState[0].url, alt: '', style: { width: '100%' } }))
|
||||
: null
|
||||
);
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var rootEl = document.getElementById('root');
|
||||
if (rootEl && window.ReactDOM && window.React) {
|
||||
var root = ReactDOM.createRoot(rootEl);
|
||||
root.render(React.createElement(Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
2240
web端/运维管理/车辆业务/调拨管理.jsx
Normal file
2240
web端/运维管理/车辆业务/调拨管理.jsx
Normal file
File diff suppressed because it is too large
Load Diff
176
小程序/App.jsx
Normal file
176
小程序/App.jsx
Normal file
@@ -0,0 +1,176 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 集成登录页 + 工作台 于单页面
|
||||
const Component = function () {
|
||||
var _React$useState = React.useState("login");
|
||||
var page = _React$useState[0];
|
||||
var setPage = _React$useState[1];
|
||||
|
||||
// 登录页状态
|
||||
var _React$useState2 = React.useState("");
|
||||
var account = _React$useState2[0];
|
||||
var setAccount = _React$useState2[1];
|
||||
var _React$useState3 = React.useState("");
|
||||
var password = _React$useState3[0];
|
||||
var setPassword = _React$useState3[1];
|
||||
var _React$useState4 = React.useState(false);
|
||||
var agreed = _React$useState4[0];
|
||||
var setAgreed = _React$useState4[1];
|
||||
|
||||
// 工作台状态
|
||||
var _React$useState5 = React.useState(0);
|
||||
var activeTab = _React$useState5[0];
|
||||
var setActiveTab = _React$useState5[1];
|
||||
|
||||
var todos = [
|
||||
{ id: 1, title: "审批设备巡检报告", status: "待处理", time: "今天" },
|
||||
{ id: 2, title: "处理加氢站异常告警", status: "进行中", time: "今天" },
|
||||
{ id: 3, title: "完成月度资产盘点", status: "待处理", time: "本周" }
|
||||
];
|
||||
var stats = [
|
||||
{ label: "待办任务", value: "8", color: "#07c160" },
|
||||
{ label: "本周完成", value: "23", color: "#576b95" },
|
||||
{ label: "告警待处理", value: "2", color: "#fa5151" }
|
||||
];
|
||||
var navItems = [
|
||||
{ key: 0, icon: "\uD83D\uDCCA", text: "工作台" },
|
||||
{ key: 1, icon: "\uD83D\uDCC4", text: "业务" },
|
||||
{ key: 2, icon: "\uD83D\uDC64", text: "我的" }
|
||||
];
|
||||
|
||||
var handleLogin = function () {
|
||||
if (!agreed) {
|
||||
alert("请先阅读并同意用户服务协议和隐私政策");
|
||||
return;
|
||||
}
|
||||
setPage("workbench");
|
||||
};
|
||||
|
||||
var styles = {
|
||||
loginPage: {
|
||||
minHeight: "100vh",
|
||||
backgroundColor: "#f7f7f7",
|
||||
padding: "40px 24px 24px",
|
||||
boxSizing: "border-box",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center"
|
||||
},
|
||||
logo: { width: "80px", height: "80px", backgroundColor: "#07c160", borderRadius: "16px", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "20px", color: "#fff", fontSize: "16px", fontWeight: "bold", textAlign: "center", lineHeight: "1.3" },
|
||||
title: { fontSize: "20px", fontWeight: "600", color: "#333", textAlign: "center", marginBottom: "40px", lineHeight: "1.5", maxWidth: "280px" },
|
||||
form: { width: "100%", maxWidth: "340px" },
|
||||
inputGroup: { marginBottom: "16px" },
|
||||
input: { width: "100%", height: "48px", padding: "0 16px", fontSize: "16px", border: "1px solid #e5e5e5", borderRadius: "8px", backgroundColor: "#fff", boxSizing: "border-box", outline: "none" },
|
||||
checkboxRow: { display: "flex", alignItems: "center", marginBottom: "24px", cursor: "pointer" },
|
||||
checkbox: { width: "18px", height: "18px", marginRight: "8px", accentColor: "#07c160", cursor: "pointer" },
|
||||
agreement: { fontSize: "14px", color: "#666", lineHeight: "1.5" },
|
||||
link: { color: "#07c160", textDecoration: "none" },
|
||||
loginBtn: { width: "100%", height: "48px", backgroundColor: "#07c160", color: "#fff", fontSize: "17px", fontWeight: "500", border: "none", borderRadius: "8px", cursor: "pointer", marginBottom: "32px" },
|
||||
loginBtnDisabled: { width: "100%", height: "48px", backgroundColor: "#b0b0b0", color: "#fff", fontSize: "17px", fontWeight: "500", border: "none", borderRadius: "8px", cursor: "not-allowed", marginBottom: "32px" },
|
||||
divider: { display: "flex", alignItems: "center", width: "100%", maxWidth: "340px", marginBottom: "24px" },
|
||||
dividerLine: { flex: 1, height: "1px", backgroundColor: "#e5e5e5" },
|
||||
dividerText: { padding: "0 16px", fontSize: "14px", color: "#999" },
|
||||
phoneLogin: { display: "flex", flexDirection: "column", alignItems: "center" },
|
||||
iconLabel: { fontSize: "14px", color: "#999", marginTop: "8px" },
|
||||
iconBtn: { width: "48px", height: "48px", borderRadius: "50%", backgroundColor: "#fff", border: "1px solid #e5e5e5", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", fontSize: "24px" },
|
||||
workbenchPage: { minHeight: "100vh", backgroundColor: "#f7f7f7", paddingBottom: "70px", boxSizing: "border-box" },
|
||||
header: { backgroundColor: "#fff", padding: "24px 20px", marginBottom: "12px" },
|
||||
headerTitle: { fontSize: "22px", fontWeight: "600", color: "#333", marginBottom: "4px" },
|
||||
headerSub: { fontSize: "14px", color: "#999" },
|
||||
statsRow: { display: "flex", padding: "0 12px 12px", marginBottom: "12px" },
|
||||
statCard: { flex: 1, backgroundColor: "#fff", borderRadius: "8px", padding: "20px 12px", textAlign: "center", margin: "0 6px" },
|
||||
statValue: { fontSize: "24px", fontWeight: "600", marginBottom: "8px" },
|
||||
statLabel: { fontSize: "13px", color: "#999" },
|
||||
section: { backgroundColor: "#fff", padding: "20px", marginBottom: "12px" },
|
||||
sectionTitle: { fontSize: "17px", fontWeight: "600", color: "#333", marginBottom: "16px", display: "flex", justifyContent: "space-between", alignItems: "center" },
|
||||
sectionMore: { fontSize: "14px", color: "#07c160" },
|
||||
todoItem: { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "1px solid #f0f0f0" },
|
||||
todoLeft: { flex: 1 },
|
||||
todoTitle: { fontSize: "15px", color: "#333", marginBottom: "4px" },
|
||||
todoMeta: { fontSize: "12px", color: "#999" },
|
||||
todoStatus: { fontSize: "12px", padding: "4px 10px", borderRadius: "4px", backgroundColor: "#e8f5e9", color: "#07c160" },
|
||||
todoStatusDoing: { fontSize: "12px", padding: "4px 10px", borderRadius: "4px", backgroundColor: "#fff3e0", color: "#ff9800" },
|
||||
bottomNav: { position: "fixed", bottom: 0, left: 0, right: 0, height: "60px", backgroundColor: "#fff", display: "flex", borderTop: "1px solid #e5e5e5", boxSizing: "border-box" },
|
||||
navItem: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", cursor: "pointer" },
|
||||
navIcon: { fontSize: "24px", marginBottom: "4px" },
|
||||
navText: { fontSize: "11px", color: "#999" }
|
||||
};
|
||||
|
||||
if (page === "login") {
|
||||
return React.createElement("div", { style: styles.loginPage },
|
||||
React.createElement("style", null, "input::placeholder{color:#999;opacity:1;}"),
|
||||
React.createElement("div", { style: styles.logo }, "羚牛氢能"),
|
||||
React.createElement("h1", { style: styles.title }, "数字化资产ONE-OS运管平台"),
|
||||
React.createElement("div", { style: styles.form },
|
||||
React.createElement("div", { style: styles.inputGroup },
|
||||
React.createElement("input", { style: styles.input, type: "text", placeholder: "请输入账号信息", value: account, onChange: function (e) { setAccount(e.target.value); } })
|
||||
),
|
||||
React.createElement("div", { style: styles.inputGroup },
|
||||
React.createElement("input", { style: styles.input, type: "password", placeholder: "请输入密码", value: password, onChange: function (e) { setPassword(e.target.value); } })
|
||||
),
|
||||
React.createElement("label", { style: styles.checkboxRow },
|
||||
React.createElement("input", { style: styles.checkbox, type: "checkbox", checked: agreed, onChange: function (e) { setAgreed(e.target.checked); } }),
|
||||
React.createElement("span", { style: styles.agreement },
|
||||
"阅读并同意",
|
||||
React.createElement("a", { href: "#", style: styles.link }, "《用户服务协议》"),
|
||||
"和",
|
||||
React.createElement("a", { href: "#", style: styles.link }, "《隐私政策》")
|
||||
)
|
||||
),
|
||||
React.createElement("button", { style: agreed ? styles.loginBtn : styles.loginBtnDisabled, onClick: handleLogin, disabled: !agreed }, "登录")
|
||||
),
|
||||
React.createElement("div", { style: styles.divider },
|
||||
React.createElement("div", { style: styles.dividerLine }),
|
||||
React.createElement("span", { style: styles.dividerText }, "其他登录方式"),
|
||||
React.createElement("div", { style: styles.dividerLine })
|
||||
),
|
||||
React.createElement("div", { style: styles.phoneLogin },
|
||||
React.createElement("div", { style: styles.iconBtn, title: "手机号登录" }, "\uD83D\uDCF1"),
|
||||
React.createElement("span", { style: styles.iconLabel }, "授权登录")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return React.createElement("div", { style: styles.workbenchPage },
|
||||
React.createElement("div", { style: styles.header },
|
||||
React.createElement("div", { style: styles.headerTitle }, "工作台"),
|
||||
React.createElement("div", { style: styles.headerSub }, "欢迎使用 ONE-OS 运管平台")
|
||||
),
|
||||
React.createElement("div", { style: styles.statsRow },
|
||||
stats.map(function (stat, i) {
|
||||
var valueStyle = { fontSize: "24px", fontWeight: "600", marginBottom: "8px", color: stat.color };
|
||||
return React.createElement("div", { key: i, style: styles.statCard },
|
||||
React.createElement("div", { style: valueStyle }, stat.value),
|
||||
React.createElement("div", { style: styles.statLabel }, stat.label)
|
||||
);
|
||||
})
|
||||
),
|
||||
React.createElement("div", { style: styles.section },
|
||||
React.createElement("div", { style: styles.sectionTitle },
|
||||
React.createElement("span", null, "待办任务"),
|
||||
React.createElement("span", { style: styles.sectionMore }, "全部")
|
||||
),
|
||||
todos.map(function (todo, i) {
|
||||
var isLast = i === todos.length - 1;
|
||||
var statusStyle = todo.status === "进行中" ? styles.todoStatusDoing : styles.todoStatus;
|
||||
var itemStyle = isLast ? { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "none" } : styles.todoItem;
|
||||
return React.createElement("div", { key: todo.id, style: itemStyle },
|
||||
React.createElement("div", { style: styles.todoLeft },
|
||||
React.createElement("div", { style: styles.todoTitle }, todo.title),
|
||||
React.createElement("div", { style: styles.todoMeta }, todo.time)
|
||||
),
|
||||
React.createElement("span", { style: statusStyle }, todo.status)
|
||||
);
|
||||
})
|
||||
),
|
||||
React.createElement("div", { style: styles.bottomNav },
|
||||
navItems.map(function (item) {
|
||||
var isActive = activeTab === item.key;
|
||||
var textStyle = isActive ? { fontSize: "11px", color: "#07c160" } : styles.navText;
|
||||
return React.createElement("div", { key: item.key, style: styles.navItem, onClick: function () { setActiveTab(item.key); } },
|
||||
React.createElement("div", { style: styles.navIcon }, item.icon),
|
||||
React.createElement("div", { style: textStyle }, item.text)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
286
小程序/LoginPage.jsx
Normal file
286
小程序/LoginPage.jsx
Normal file
@@ -0,0 +1,286 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
const LoginPage = function (props) {
|
||||
var onLoginSuccess = (props && props.onLoginSuccess) ? props.onLoginSuccess : null;
|
||||
var _React$useState = React.useState("");
|
||||
var account = _React$useState[0];
|
||||
var setAccount = _React$useState[1];
|
||||
|
||||
var _React$useState2 = React.useState("");
|
||||
var password = _React$useState2[0];
|
||||
var setPassword = _React$useState2[1];
|
||||
|
||||
var _React$useState3 = React.useState(false);
|
||||
var agreed = _React$useState3[0];
|
||||
var setAgreed = _React$useState3[1];
|
||||
|
||||
var handleAccountChange = function (e) {
|
||||
setAccount(e.target.value);
|
||||
};
|
||||
|
||||
var handlePasswordChange = function (e) {
|
||||
setPassword(e.target.value);
|
||||
};
|
||||
|
||||
var handleAgreedChange = function (e) {
|
||||
setAgreed(e.target.checked);
|
||||
};
|
||||
|
||||
var handleLogin = function () {
|
||||
if (!agreed) {
|
||||
alert("请先阅读并同意用户服务协议和隐私政策");
|
||||
return;
|
||||
}
|
||||
if (onLoginSuccess) {
|
||||
onLoginSuccess();
|
||||
} else {
|
||||
alert("登录功能演示");
|
||||
}
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: {
|
||||
minHeight: "100vh",
|
||||
backgroundColor: "#f7f7f7",
|
||||
padding: "40px 24px 24px",
|
||||
boxSizing: "border-box",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center"
|
||||
},
|
||||
logo: {
|
||||
width: "80px",
|
||||
height: "80px",
|
||||
backgroundColor: "#07c160",
|
||||
borderRadius: "16px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: "20px",
|
||||
color: "#fff",
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
lineHeight: "1.3"
|
||||
},
|
||||
title: {
|
||||
fontSize: "20px",
|
||||
fontWeight: "600",
|
||||
color: "#333",
|
||||
textAlign: "center",
|
||||
marginBottom: "40px",
|
||||
lineHeight: "1.5",
|
||||
maxWidth: "280px"
|
||||
},
|
||||
form: {
|
||||
width: "100%",
|
||||
maxWidth: "340px"
|
||||
},
|
||||
inputGroup: {
|
||||
marginBottom: "16px"
|
||||
},
|
||||
input: {
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
padding: "0 16px",
|
||||
fontSize: "16px",
|
||||
border: "1px solid #e5e5e5",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#fff",
|
||||
boxSizing: "border-box",
|
||||
outline: "none"
|
||||
},
|
||||
inputFocus: {
|
||||
borderColor: "#07c160"
|
||||
},
|
||||
checkboxRow: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: "24px",
|
||||
cursor: "pointer"
|
||||
},
|
||||
checkbox: {
|
||||
width: "18px",
|
||||
height: "18px",
|
||||
marginRight: "8px",
|
||||
accentColor: "#07c160",
|
||||
cursor: "pointer"
|
||||
},
|
||||
agreement: {
|
||||
fontSize: "14px",
|
||||
color: "#666",
|
||||
lineHeight: "1.5"
|
||||
},
|
||||
link: {
|
||||
color: "#07c160",
|
||||
textDecoration: "none"
|
||||
},
|
||||
loginBtn: {
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
backgroundColor: "#07c160",
|
||||
color: "#fff",
|
||||
fontSize: "17px",
|
||||
fontWeight: "500",
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
cursor: "pointer",
|
||||
marginBottom: "32px"
|
||||
},
|
||||
loginBtnDisabled: {
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
backgroundColor: "#b0b0b0",
|
||||
color: "#fff",
|
||||
fontSize: "17px",
|
||||
fontWeight: "500",
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
cursor: "not-allowed",
|
||||
marginBottom: "32px"
|
||||
},
|
||||
divider: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
maxWidth: "340px",
|
||||
marginBottom: "24px"
|
||||
},
|
||||
dividerLine: {
|
||||
flex: 1,
|
||||
height: "1px",
|
||||
backgroundColor: "#e5e5e5"
|
||||
},
|
||||
dividerText: {
|
||||
padding: "0 16px",
|
||||
fontSize: "14px",
|
||||
color: "#999"
|
||||
},
|
||||
phoneLogin: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center"
|
||||
},
|
||||
iconLabel: {
|
||||
fontSize: "14px",
|
||||
color: "#999",
|
||||
marginTop: "8px"
|
||||
},
|
||||
iconBtn: {
|
||||
width: "48px",
|
||||
height: "48px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "#fff",
|
||||
border: "1px solid #e5e5e5",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
fontSize: "24px"
|
||||
}
|
||||
};
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ style: styles.page },
|
||||
React.createElement("style", null, "input::placeholder{color:#999;opacity:1;}"),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.logo },
|
||||
"羚牛氢能"
|
||||
),
|
||||
React.createElement(
|
||||
"h1",
|
||||
{ style: styles.title },
|
||||
"\u6570\u5B57\u5316\u8D44\u4EA7ONE-OS\u8FD0\u7BA1\u5E73\u53F0"
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.form },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.inputGroup },
|
||||
React.createElement("input", {
|
||||
style: styles.input,
|
||||
type: "text",
|
||||
placeholder: "请输入账号信息",
|
||||
value: account,
|
||||
onChange: handleAccountChange
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.inputGroup },
|
||||
React.createElement("input", {
|
||||
style: styles.input,
|
||||
type: "password",
|
||||
placeholder: "请输入密码",
|
||||
value: password,
|
||||
onChange: handlePasswordChange
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
"label",
|
||||
{ style: styles.checkboxRow },
|
||||
React.createElement("input", {
|
||||
style: styles.checkbox,
|
||||
type: "checkbox",
|
||||
checked: agreed,
|
||||
onChange: handleAgreedChange
|
||||
}),
|
||||
React.createElement(
|
||||
"span",
|
||||
{ style: styles.agreement },
|
||||
"阅读并同意",
|
||||
React.createElement(
|
||||
"a",
|
||||
{ href: "#", style: styles.link },
|
||||
"《用户服务协议》"
|
||||
),
|
||||
"和",
|
||||
React.createElement(
|
||||
"a",
|
||||
{ href: "#", style: styles.link },
|
||||
"《隐私政策》"
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
"button",
|
||||
{
|
||||
style: agreed ? styles.loginBtn : styles.loginBtnDisabled,
|
||||
onClick: handleLogin,
|
||||
disabled: !agreed
|
||||
},
|
||||
"\u767B\u5F55"
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.divider },
|
||||
React.createElement("div", { style: styles.dividerLine }),
|
||||
React.createElement(
|
||||
"span",
|
||||
{ style: styles.dividerText },
|
||||
"\u5176\u4ED6\u767B\u5F55\u65B9\u5F0F"
|
||||
),
|
||||
React.createElement("div", { style: styles.dividerLine })
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.phoneLogin },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.iconBtn, title: "手机号登录" },
|
||||
"\uD83D\uDCF1"
|
||||
),
|
||||
React.createElement(
|
||||
"span",
|
||||
{ style: styles.iconLabel },
|
||||
"授权登录"
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Axhub 要求必须定义 Component 变量
|
||||
var Component = LoginPage;
|
||||
232
小程序/WorkbenchPage.jsx
Normal file
232
小程序/WorkbenchPage.jsx
Normal file
@@ -0,0 +1,232 @@
|
||||
const WorkbenchPage = function () {
|
||||
var _React$useState = React.useState(0);
|
||||
var activeTab = _React$useState[0];
|
||||
var setActiveTab = _React$useState[1];
|
||||
|
||||
var todos = [
|
||||
{ id: 1, title: "审批设备巡检报告", status: "待处理", time: "今天" },
|
||||
{ id: 2, title: "处理加氢站异常告警", status: "进行中", time: "今天" },
|
||||
{ id: 3, title: "完成月度资产盘点", status: "待处理", time: "本周" }
|
||||
];
|
||||
|
||||
var stats = [
|
||||
{ label: "待办任务", value: "8", color: "#07c160" },
|
||||
{ label: "本周完成", value: "23", color: "#576b95" },
|
||||
{ label: "告警待处理", value: "2", color: "#fa5151" }
|
||||
];
|
||||
|
||||
var navItems = [
|
||||
{ key: 0, icon: "\uD83D\uDCCA", text: "工作台" },
|
||||
{ key: 1, icon: "\uD83D\uDCC4", text: "业务" },
|
||||
{ key: 2, icon: "\uD83D\uDC64", text: "我的" }
|
||||
];
|
||||
|
||||
var handleNavClick = function (key) {
|
||||
setActiveTab(key);
|
||||
};
|
||||
|
||||
var styles = {
|
||||
page: {
|
||||
minHeight: "100vh",
|
||||
backgroundColor: "#f7f7f7",
|
||||
paddingBottom: "70px",
|
||||
boxSizing: "border-box"
|
||||
},
|
||||
header: {
|
||||
backgroundColor: "#fff",
|
||||
padding: "24px 20px",
|
||||
marginBottom: "12px"
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: "22px",
|
||||
fontWeight: "600",
|
||||
color: "#333",
|
||||
marginBottom: "4px"
|
||||
},
|
||||
headerSub: {
|
||||
fontSize: "14px",
|
||||
color: "#999"
|
||||
},
|
||||
statsRow: {
|
||||
display: "flex",
|
||||
padding: "0 12px 12px",
|
||||
marginBottom: "12px"
|
||||
},
|
||||
statCard: {
|
||||
flex: 1,
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "8px",
|
||||
padding: "20px 12px",
|
||||
textAlign: "center",
|
||||
margin: "0 6px"
|
||||
},
|
||||
statValue: {
|
||||
fontSize: "24px",
|
||||
fontWeight: "600",
|
||||
marginBottom: "8px"
|
||||
},
|
||||
statLabel: {
|
||||
fontSize: "13px",
|
||||
color: "#999"
|
||||
},
|
||||
section: {
|
||||
backgroundColor: "#fff",
|
||||
padding: "20px",
|
||||
marginBottom: "12px"
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: "17px",
|
||||
fontWeight: "600",
|
||||
color: "#333",
|
||||
marginBottom: "16px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
},
|
||||
sectionMore: {
|
||||
fontSize: "14px",
|
||||
color: "#07c160"
|
||||
},
|
||||
todoItem: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "14px 0",
|
||||
borderBottom: "1px solid #f0f0f0"
|
||||
},
|
||||
todoItemLast: {
|
||||
borderBottom: "none"
|
||||
},
|
||||
todoLeft: {
|
||||
flex: 1
|
||||
},
|
||||
todoTitle: {
|
||||
fontSize: "15px",
|
||||
color: "#333",
|
||||
marginBottom: "4px"
|
||||
},
|
||||
todoMeta: {
|
||||
fontSize: "12px",
|
||||
color: "#999"
|
||||
},
|
||||
todoStatus: {
|
||||
fontSize: "12px",
|
||||
padding: "4px 10px",
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#e8f5e9",
|
||||
color: "#07c160"
|
||||
},
|
||||
todoStatusDoing: {
|
||||
fontSize: "12px",
|
||||
padding: "4px 10px",
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#fff3e0",
|
||||
color: "#ff9800"
|
||||
},
|
||||
bottomNav: {
|
||||
position: "fixed",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: "60px",
|
||||
backgroundColor: "#fff",
|
||||
display: "flex",
|
||||
borderTop: "1px solid #e5e5e5",
|
||||
boxSizing: "border-box"
|
||||
},
|
||||
navItem: {
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer"
|
||||
},
|
||||
navIcon: {
|
||||
fontSize: "24px",
|
||||
marginBottom: "4px"
|
||||
},
|
||||
navText: {
|
||||
fontSize: "11px",
|
||||
color: "#999"
|
||||
},
|
||||
navTextActive: {
|
||||
color: "#07c160"
|
||||
}
|
||||
};
|
||||
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ style: styles.page },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.header },
|
||||
React.createElement("div", { style: styles.headerTitle }, "工作台"),
|
||||
React.createElement("div", { style: styles.headerSub }, "欢迎使用 ONE-OS 运管平台")
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.statsRow },
|
||||
stats.map(function (stat, i) {
|
||||
var valueStyle = {
|
||||
fontSize: "24px",
|
||||
fontWeight: "600",
|
||||
marginBottom: "8px",
|
||||
color: stat.color
|
||||
};
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ key: i, style: styles.statCard },
|
||||
React.createElement("div", { style: valueStyle }, stat.value),
|
||||
React.createElement("div", { style: styles.statLabel }, stat.label)
|
||||
);
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.section },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.sectionTitle },
|
||||
React.createElement("span", null, "待办任务"),
|
||||
React.createElement("span", { style: styles.sectionMore }, "全部")
|
||||
),
|
||||
todos.map(function (todo, i) {
|
||||
var isLast = i === todos.length - 1;
|
||||
var statusStyle = todo.status === "进行中" ? styles.todoStatusDoing : styles.todoStatus;
|
||||
var itemStyle = isLast ? { display: "flex", alignItems: "center", padding: "14px 0", borderBottom: "none" } : styles.todoItem;
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ key: todo.id, style: itemStyle },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.todoLeft },
|
||||
React.createElement("div", { style: styles.todoTitle }, todo.title),
|
||||
React.createElement("div", { style: styles.todoMeta }, todo.time)
|
||||
),
|
||||
React.createElement("span", { style: statusStyle }, todo.status)
|
||||
);
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ style: styles.bottomNav },
|
||||
navItems.map(function (item) {
|
||||
var isActive = activeTab === item.key;
|
||||
var textStyle = isActive ? { fontSize: "11px", color: "#07c160" } : styles.navText;
|
||||
return React.createElement(
|
||||
"div",
|
||||
{
|
||||
key: item.key,
|
||||
style: styles.navItem,
|
||||
onClick: function () { handleNavClick(item.key); }
|
||||
},
|
||||
React.createElement("div", { style: styles.navIcon }, item.icon),
|
||||
React.createElement("div", { style: textStyle }, item.text)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Axhub 要求必须定义 Component 变量
|
||||
var Component = WorkbenchPage;
|
||||
20
小程序/login-preview.html
Normal file
20
小程序/login-preview.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ONE-OS运管平台</title>
|
||||
<link rel="stylesheet" href="https://res.wx.qq.com/t/wx_fed/weui-source/res/2.6.22/weui.min.css">
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="text/babel" src="App.jsx"></script>
|
||||
<script type="text/babel">
|
||||
var root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(React.createElement(Component));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
20
小程序/workbench-preview.html
Normal file
20
小程序/workbench-preview.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>工作台 - 数字化资产ONE-OS运管平台</title>
|
||||
<link rel="stylesheet" href="https://res.wx.qq.com/t/wx_fed/weui-source/res/2.6.22/weui.min.css">
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="text/babel" src="WorkbenchPage.jsx"></script>
|
||||
<script type="text/babel">
|
||||
var root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(React.createElement(WorkbenchPage));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user