Initial commit: ONE-OS project
Made-with: Cursor
This commit is contained in:
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 }, '取消'))
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user