feat(web): 租赁账单、安全培训扫码、提车应收款等模块更新与新增
Made-with: Cursor
This commit is contained in:
1221
web端/业务管理/租赁账单.jsx
1221
web端/业务管理/租赁账单.jsx
File diff suppressed because it is too large
Load Diff
163
web端/加氢站管理/加氢订单.jsx
Normal file
163
web端/加氢站管理/加氢订单.jsx
Normal file
@@ -0,0 +1,163 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 加氢记录(列表 + 筛选)(2026年3月版)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Card = antd.Card;
|
||||
var Table = antd.Table;
|
||||
var Button = antd.Button;
|
||||
var Select = antd.Select;
|
||||
var Input = antd.Input;
|
||||
var DatePicker = antd.DatePicker;
|
||||
var Space = antd.Space;
|
||||
var message = antd.message;
|
||||
|
||||
var Option = Select.Option;
|
||||
var RangePicker = DatePicker.RangePicker || DatePicker.RangePicker;
|
||||
|
||||
// 筛选状态
|
||||
var stationState = useState(undefined);
|
||||
var timeRangeState = useState([]);
|
||||
var plateNoState = useState('');
|
||||
var station = stationState[0];
|
||||
var setStation = stationState[1];
|
||||
var timeRange = timeRangeState[0];
|
||||
var setTimeRange = timeRangeState[1];
|
||||
var plateNo = plateNoState[0];
|
||||
var setPlateNo = plateNoState[1];
|
||||
|
||||
// 模拟:加氢站与加氢记录数据
|
||||
var stationList = useMemo(function () {
|
||||
return [
|
||||
{ value: 'JX-H2-001', label: '嘉兴加氢站(一期)' },
|
||||
{ value: 'HZ-H2-002', label: '杭州临平加氢站' },
|
||||
{ value: 'SH-H2-003', label: '上海宝山加氢站' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var rawData = useMemo(function () {
|
||||
return [
|
||||
{ id: 1, stationCode: 'JX-H2-001', stationName: '嘉兴加氢站(一期)', time: '2026-03-01 10:21', plateNo: '浙A12345', amountKg: 12.5, amountYuan: 625.00 },
|
||||
{ id: 2, stationCode: 'JX-H2-001', stationName: '嘉兴加氢站(一期)', time: '2026-03-01 14:08', plateNo: '浙A67890', amountKg: 10.0, amountYuan: 500.00 },
|
||||
{ id: 3, stationCode: 'HZ-H2-002', stationName: '杭州临平加氢站', time: '2026-03-02 09:30', plateNo: '浙B23456', amountKg: 15.3, amountYuan: 765.00 },
|
||||
{ id: 4, stationCode: 'SH-H2-003', stationName: '上海宝山加氢站', time: '2026-03-03 16:45', plateNo: '沪A88888', amountKg: 8.0, amountYuan: 400.00 },
|
||||
{ id: 5, stationCode: 'HZ-H2-002', stationName: '杭州临平加氢站', time: '2026-03-03 18:10', plateNo: '浙B99999', amountKg: 18.2, amountYuan: 910.00 }
|
||||
];
|
||||
}, []);
|
||||
|
||||
// 简单字符串时间过滤(示例用,实际可改为 dayjs 比较)
|
||||
function inRange(timeStr, range) {
|
||||
if (!range || !range.length || !range[0] || !range[1]) return true;
|
||||
// timeStr: 'YYYY-MM-DD HH:mm',只做字符串比较以示意
|
||||
var t = timeStr.replace(/[-:\\s]/g, '');
|
||||
var s = range[0].format ? range[0].format('YYYYMMDDHHmm') : '';
|
||||
var e = range[1].format ? range[1].format('YYYYMMDDHHmm') : '';
|
||||
if (!s || !e) return true;
|
||||
return t >= s && t <= e;
|
||||
}
|
||||
|
||||
var filteredData = useMemo(function () {
|
||||
var list = rawData.slice();
|
||||
if (station) {
|
||||
list = list.filter(function (r) { return r.stationCode === station; });
|
||||
}
|
||||
if (plateNo && plateNo.trim()) {
|
||||
var kw = plateNo.trim().toLowerCase();
|
||||
list = list.filter(function (r) { return (r.plateNo || '').toLowerCase().indexOf(kw) !== -1; });
|
||||
}
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
list = list.filter(function (r) { return inRange(r.time, timeRange); });
|
||||
}
|
||||
return list.map(function (r, idx) {
|
||||
return Object.assign({}, r, { seq: idx + 1 });
|
||||
});
|
||||
}, [rawData, station, plateNo, timeRange]);
|
||||
|
||||
var columns = [
|
||||
{ title: '序号', dataIndex: 'seq', key: 'seq', width: 70, align: 'center' },
|
||||
{ title: '加氢站', dataIndex: 'stationName', key: 'stationName', width: 200 },
|
||||
{ title: '加氢时间', dataIndex: 'time', key: 'time', width: 180 },
|
||||
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 120 },
|
||||
{ title: '加氢量(kg)', dataIndex: 'amountKg', key: 'amountKg', width: 120, align: 'right', render: function (v) { return (v != null ? v.toFixed(2) : '0.00'); } },
|
||||
{ title: '金额(元)', dataIndex: 'amountYuan', key: 'amountYuan', width: 120, align: 'right', render: function (v) { var n = typeof v === 'number' ? v : parseFloat(v); return (isNaN(n) ? '0.00' : n.toFixed(2)); } }
|
||||
];
|
||||
|
||||
var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
|
||||
var cardStyle = { marginBottom: 16 };
|
||||
var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
|
||||
var filterItemStyle = { marginBottom: 12 };
|
||||
|
||||
function handleReset() {
|
||||
setStation(undefined);
|
||||
setTimeRange([]);
|
||||
setPlateNo('');
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
// 实际项目中这里调用导出接口;此处仅做提示
|
||||
message.info('导出当前筛选条件下的加氢记录(示例)');
|
||||
}
|
||||
|
||||
return React.createElement('div', { style: layoutStyle },
|
||||
React.createElement(Card, { title: '筛选', style: cardStyle },
|
||||
React.createElement('div', {
|
||||
style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'flex-end' }
|
||||
},
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '站点筛选'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择加氢站',
|
||||
allowClear: true,
|
||||
style: { width: '100%' },
|
||||
value: station,
|
||||
onChange: function (v) { setStation(v); }
|
||||
},
|
||||
stationList.map(function (s) {
|
||||
return React.createElement(Option, { key: s.value, value: s.value }, s.label);
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '加氢时间'),
|
||||
React.createElement(RangePicker, {
|
||||
style: { width: '100%' },
|
||||
showTime: true,
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
value: timeRange,
|
||||
onChange: function (v) { setTimeRange(v || []); }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '车牌号'),
|
||||
React.createElement(Input, {
|
||||
placeholder: '请输入车牌号',
|
||||
value: plateNo,
|
||||
onChange: function (e) { setPlateNo(e.target.value); }
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', marginTop: 16 } },
|
||||
React.createElement(Space, null,
|
||||
React.createElement(Button, { type: 'primary' }, '查询'),
|
||||
React.createElement(Button, { onClick: handleReset }, '重置')
|
||||
),
|
||||
React.createElement(Button, { onClick: handleExport }, '导出')
|
||||
)
|
||||
),
|
||||
React.createElement(Card, { title: '加氢记录', style: cardStyle },
|
||||
React.createElement(Table, {
|
||||
rowKey: 'id',
|
||||
columns: columns,
|
||||
dataSource: filteredData,
|
||||
pagination: { pageSize: 10, showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } },
|
||||
bordered: true,
|
||||
size: 'middle',
|
||||
scroll: { x: 700 }
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
268
web端/安全培训扫码.jsx
Normal file
268
web端/安全培训扫码.jsx
Normal file
@@ -0,0 +1,268 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 安全培训扫码 - H5 分步表单(扫码链接进入)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useRef = React.useRef;
|
||||
var useEffect = React.useEffect;
|
||||
|
||||
var antd = window.antd;
|
||||
var Steps = antd.Steps;
|
||||
var Button = antd.Button;
|
||||
var Input = antd.Input;
|
||||
var message = antd.message;
|
||||
var Progress = antd.Progress;
|
||||
|
||||
// 当前步骤 1 | 2 | 3 | 4
|
||||
var stepState = useState(1);
|
||||
var step = stepState[0];
|
||||
var setStep = stepState[1];
|
||||
|
||||
// 第一步:手机号、验证码
|
||||
var phoneState = useState('');
|
||||
var verifyCodeState = useState('');
|
||||
var codeCountdownState = useState(0);
|
||||
var phone = phoneState[0];
|
||||
var setPhone = phoneState[1];
|
||||
var verifyCode = verifyCodeState[0];
|
||||
var setVerifyCode = verifyCodeState[1];
|
||||
var codeCountdown = codeCountdownState[0];
|
||||
var setCodeCountdown = codeCountdownState[1];
|
||||
|
||||
// 模拟:已完成培训的手机号,再次进入直接显示提车码
|
||||
var completedPhonesState = useState({ '13800138000': 'TC-2026-8888', '13900139000': 'TC-2026-9999' });
|
||||
var completedPhones = completedPhonesState[0];
|
||||
var setCompletedPhones = completedPhonesState[1];
|
||||
|
||||
// 第二步:证照上传(用文件名/已上传标记模拟)
|
||||
var idFrontState = useState(null);
|
||||
var idBackState = useState(null);
|
||||
var licenseState = useState(null);
|
||||
var qualificationState = useState(null);
|
||||
var needQualification = false; // 18吨以上车辆必填,此处为可选
|
||||
|
||||
// 第三步:视频进度(0-100),是否播放中
|
||||
var videoProgressState = useState(0);
|
||||
var videoPlayingState = useState(false);
|
||||
var videoProgress = videoProgressState[0];
|
||||
var setVideoProgress = videoProgressState[1];
|
||||
var videoPlaying = videoPlayingState[0];
|
||||
var setVideoPlaying = videoPlayingState[1];
|
||||
var videoTimerRef = useRef(null);
|
||||
|
||||
// 第四步:提车码(生成后保存,同一手机再次进入可直接显示)
|
||||
var pickupCodeState = useState('');
|
||||
var pickupCode = pickupCodeState[0];
|
||||
var setPickupCode = pickupCodeState[1];
|
||||
|
||||
// 获取验证码倒计时
|
||||
useEffect(function () {
|
||||
if (codeCountdown <= 0) return;
|
||||
var t = setTimeout(function () { setCodeCountdown(function (c) { return c - 1; }); }, 1000);
|
||||
return function () { clearTimeout(t); };
|
||||
}, [codeCountdown]);
|
||||
|
||||
// 视频模拟:播放时每 500ms 增加进度,到 100% 停止
|
||||
useEffect(function () {
|
||||
if (!videoPlaying || videoProgress >= 100) {
|
||||
if (videoProgress >= 100) setVideoPlaying(false);
|
||||
return;
|
||||
}
|
||||
var t = setInterval(function () {
|
||||
setVideoProgress(function (p) {
|
||||
var next = Math.min(100, p + 2);
|
||||
return next;
|
||||
});
|
||||
}, 500);
|
||||
return function () { clearInterval(t); };
|
||||
}, [videoPlaying, videoProgress]);
|
||||
|
||||
// 第一步:验证并进入
|
||||
var handleStep1Next = useCallback(function () {
|
||||
var p = (phone || '').trim();
|
||||
var c = (verifyCode || '').trim();
|
||||
if (!p) { message.warning('请输入手机号'); return; }
|
||||
if (!c) { message.warning('请输入验证码'); return; }
|
||||
// 已完成培训的手机号直接进入第四步
|
||||
if (completedPhones[p]) {
|
||||
setPickupCode(completedPhones[p]);
|
||||
setStep(4);
|
||||
return;
|
||||
}
|
||||
// 模拟验证成功(任意 4-6 位验证码)
|
||||
if (c.length < 4) { message.warning('请输入正确的验证码'); return; }
|
||||
setStep(2);
|
||||
}, [phone, verifyCode, completedPhones]);
|
||||
|
||||
var handleSendCode = useCallback(function () {
|
||||
var p = (phone || '').trim();
|
||||
if (!p || p.length < 11) { message.warning('请输入正确手机号'); return; }
|
||||
if (codeCountdown > 0) return;
|
||||
setCodeCountdown(60);
|
||||
message.success('验证码已发送');
|
||||
}, [phone, codeCountdown]);
|
||||
|
||||
// 第二步:上传(模拟点击即视为已上传)
|
||||
var uploadAreaStyle = {
|
||||
border: '1px dashed #d9d9d9',
|
||||
borderRadius: 8,
|
||||
padding: '24px 16px',
|
||||
textAlign: 'center',
|
||||
background: '#fafafa',
|
||||
color: 'rgba(0,0,0,0.65)',
|
||||
fontSize: 14,
|
||||
cursor: 'pointer'
|
||||
};
|
||||
var uploadDoneStyle = { borderColor: '#52c41a', background: '#f6ffed', color: '#52c41a' };
|
||||
|
||||
function renderUpload(label, value, setValue) {
|
||||
var done = value != null && value !== '';
|
||||
return React.createElement('div', {
|
||||
key: label,
|
||||
style: Object.assign({}, uploadAreaStyle, done ? uploadDoneStyle : {}),
|
||||
onClick: function () {
|
||||
// 模拟选择文件/拍照:点击即视为已上传
|
||||
setValue(done ? null : label + '-已上传.jpg');
|
||||
}
|
||||
}, done ? (value + ' ✓') : ('点击上传 ' + label + '(支持现场拍照/本地文件)'));
|
||||
}
|
||||
|
||||
var allRequiredUploaded = (idFrontState[0] != null && idFrontState[0] !== '') &&
|
||||
(idBackState[0] != null && idBackState[0] !== '') &&
|
||||
(licenseState[0] != null && licenseState[0] !== '') &&
|
||||
(!needQualification || (qualificationState[0] != null && qualificationState[0] !== ''));
|
||||
|
||||
var handleStep2WatchVideo = useCallback(function () {
|
||||
if (!allRequiredUploaded) { message.warning('请完成全部必填证照上传'); return; }
|
||||
setStep(3);
|
||||
setVideoProgress(0);
|
||||
setVideoPlaying(false);
|
||||
}, [allRequiredUploaded]);
|
||||
|
||||
// 第三步:播放/暂停,进度到 100% 后可生成提车码
|
||||
var handleVideoPlayPause = useCallback(function () {
|
||||
if (videoProgress >= 100) return;
|
||||
setVideoPlaying(function (v) { return !v; });
|
||||
}, [videoProgress]);
|
||||
|
||||
var handleStep3Generate = useCallback(function () {
|
||||
if (videoProgress < 100) { message.warning('请完整观看安全培训视频'); return; }
|
||||
var code = 'TC-' + new Date().getFullYear() + '-' + Math.floor(1000 + Math.random() * 9000);
|
||||
setPickupCode(code);
|
||||
setCompletedPhones(function (prev) {
|
||||
var next = {}; for (var k in prev) next[k] = prev[k]; next[phone.trim()] = code; return next;
|
||||
});
|
||||
setStep(4);
|
||||
message.success('提车码已生成');
|
||||
}, [videoProgress, phone]);
|
||||
|
||||
// H5 移动端布局
|
||||
var pageStyle = {
|
||||
maxWidth: 414,
|
||||
margin: '0 auto',
|
||||
minHeight: '100vh',
|
||||
background: '#f5f5f5',
|
||||
padding: '16px',
|
||||
boxSizing: 'border-box'
|
||||
};
|
||||
var stepIndicatorStyle = { marginBottom: 24 };
|
||||
var cardStyle = { marginBottom: 16, borderRadius: 12 };
|
||||
var labelStyle = { display: 'block', marginBottom: 8, fontSize: 15, color: 'rgba(0,0,0,0.85)' };
|
||||
var inputStyle = { width: '100%', height: 48, fontSize: 16, boxSizing: 'border-box' };
|
||||
var btnBlockStyle = { width: '100%', height: 48, fontSize: 16, marginTop: 8 };
|
||||
var stepTitleStyle = { fontSize: 17, fontWeight: 600, marginBottom: 16, color: 'rgba(0,0,0,0.85)' };
|
||||
|
||||
var stepsItems = [
|
||||
{ title: '验证' },
|
||||
{ title: '证照' },
|
||||
{ title: '视频' },
|
||||
{ title: '提车码' }
|
||||
];
|
||||
|
||||
// 第一步视图
|
||||
var step1Content = React.createElement('div', null,
|
||||
React.createElement('div', { style: stepTitleStyle }, '手机号验证'),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('label', { style: labelStyle }, '手机号'),
|
||||
React.createElement(Input, {
|
||||
placeholder: '请输入手机号',
|
||||
value: phone,
|
||||
onChange: function (e) { setPhone(e.target.value); },
|
||||
style: inputStyle,
|
||||
maxLength: 11,
|
||||
type: 'tel'
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement('label', { style: labelStyle }, '验证码'),
|
||||
React.createElement('div', { style: { display: 'flex', gap: 8 } },
|
||||
React.createElement(Input, {
|
||||
placeholder: '请输入验证码',
|
||||
value: verifyCode,
|
||||
onChange: function (e) { setVerifyCode(e.target.value); },
|
||||
style: Object.assign({}, inputStyle, { flex: 1 }),
|
||||
maxLength: 6
|
||||
}),
|
||||
React.createElement(Button, {
|
||||
disabled: codeCountdown > 0,
|
||||
onClick: handleSendCode,
|
||||
style: { height: 48, minWidth: 100 }
|
||||
}, codeCountdown > 0 ? codeCountdown + 's' : '获取验证码')
|
||||
)
|
||||
),
|
||||
React.createElement(Button, { type: 'primary', size: 'large', style: btnBlockStyle, onClick: handleStep1Next }, '下一步')
|
||||
);
|
||||
|
||||
// 第二步视图
|
||||
var step2Content = React.createElement('div', null,
|
||||
React.createElement('div', { style: stepTitleStyle }, '证照上传'),
|
||||
React.createElement('div', { style: { marginBottom: 12 } }, '请上传以下证照,支持现场拍照或手机本地文件。'),
|
||||
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } },
|
||||
renderUpload('身份证正面', idFrontState[0], idFrontState[1]),
|
||||
renderUpload('身份证反面', idBackState[0], idBackState[1]),
|
||||
renderUpload('驾驶证', licenseState[0], licenseState[1]),
|
||||
renderUpload('从业资格证(18吨以上车辆请上传,可选)', qualificationState[0], qualificationState[1])
|
||||
),
|
||||
React.createElement(Button, { type: 'primary', size: 'large', style: Object.assign({}, btnBlockStyle, { marginTop: 24 }), onClick: handleStep2WatchVideo, disabled: !allRequiredUploaded }, '观看安全培训视频')
|
||||
);
|
||||
|
||||
// 第三步视图:视频区域(不可快进快退,仅暂停/播放)
|
||||
var step3Content = React.createElement('div', null,
|
||||
React.createElement('div', { style: stepTitleStyle }, '安全培训视频'),
|
||||
React.createElement('div', { style: { marginBottom: 8, fontSize: 14, color: 'rgba(0,0,0,0.65)' } }, '请完整观看视频,不支持快进快退。'),
|
||||
React.createElement('div', {
|
||||
style: { background: '#000', borderRadius: 8, overflow: 'hidden', marginBottom: 16, position: 'relative', paddingBottom: '56.25%', height: 0 },
|
||||
onClick: handleVideoPlayPause
|
||||
},
|
||||
React.createElement('div', { style: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 14 } },
|
||||
videoProgress >= 100 ? '播放完成' : (videoPlaying ? '播放中... 点击暂停' : '点击播放')
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { marginBottom: 8 } },
|
||||
React.createElement(Progress, { percent: videoProgress, showInfo: true })
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', gap: 8 } },
|
||||
React.createElement(Button, { onClick: handleVideoPlayPause, disabled: videoProgress >= 100 }, videoPlaying ? '暂停' : '播放'),
|
||||
React.createElement(Button, { type: 'primary', disabled: videoProgress < 100, onClick: handleStep3Generate }, '生成提车码')
|
||||
)
|
||||
);
|
||||
|
||||
// 第四步视图
|
||||
var step4Content = React.createElement('div', { style: { textAlign: 'center', padding: '24px 0' } },
|
||||
React.createElement('div', { style: stepTitleStyle }, '提车码'),
|
||||
React.createElement('div', { style: { fontSize: 15, color: 'rgba(0,0,0,0.65)', marginBottom: 24 } }, '小程序扫描提车码后自动拉取司机证件信息。提车码在运维完成扫提车码并交车成功后失效。'),
|
||||
React.createElement('div', {
|
||||
style: { fontSize: 28, fontWeight: 700, letterSpacing: 4, padding: '20px', background: '#f0f0f0', borderRadius: 12, marginBottom: 16, userSelect: 'all' }
|
||||
}, pickupCode || '—'),
|
||||
React.createElement('div', { style: { width: 160, height: 160, margin: '0 auto 16px', background: '#f0f0f0', border: '2px dashed #d9d9d9', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#999', fontSize: 12 } }, '提车码二维码')
|
||||
);
|
||||
|
||||
var stepContents = [step1Content, step2Content, step3Content, step4Content];
|
||||
|
||||
return React.createElement('div', { style: pageStyle },
|
||||
React.createElement('div', { style: { padding: '12px 0', marginBottom: 8, fontSize: 18, fontWeight: 600 } }, '安全培训扫码'),
|
||||
React.createElement(Steps, { current: step - 1, style: stepIndicatorStyle, size: 'small', items: stepsItems }),
|
||||
React.createElement('div', { style: { background: '#fff', borderRadius: 12, padding: 20, boxShadow: '0 1px 2px rgba(0,0,0,0.05)' } }, stepContents[step - 1])
|
||||
);
|
||||
};
|
||||
397
web端/财务管理/提车应收款-审核.jsx
Normal file
397
web端/财务管理/提车应收款-审核.jsx
Normal file
File diff suppressed because one or more lines are too long
590
web端/财务管理/提车应收款-开票信息.jsx
Normal file
590
web端/财务管理/提车应收款-开票信息.jsx
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
305
web端/财务管理/提车应收款-查看.jsx
Normal file
305
web端/财务管理/提车应收款-查看.jsx
Normal file
@@ -0,0 +1,305 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 财务管理 - 提车应收款-查看(2026年3月10日版本)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var Button = antd.Button;
|
||||
var Popover = antd.Popover;
|
||||
var Steps = antd.Steps;
|
||||
var Tooltip = antd.Tooltip;
|
||||
var message = antd.message;
|
||||
|
||||
var servicePopoverOpen = useState(null);
|
||||
|
||||
// 模拟:项目信息
|
||||
var projectInfo = useMemo(function () {
|
||||
return {
|
||||
contractCode: 'HT-ZL-2025-001',
|
||||
contractType: '正式合同',
|
||||
projectName: '嘉兴氢能示范项目',
|
||||
customerName: '嘉兴某某物流有限公司',
|
||||
paymentMethod: '预付',
|
||||
paymentCycle: '6个月',
|
||||
contractStart: '2025-01-15',
|
||||
contractEnd: '2026-01-14',
|
||||
businessDept: '业务1部',
|
||||
businessPerson: '张经理'
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 模拟:本单提车收款单已选车辆及填写内容(只读展示)
|
||||
var vehicles = useMemo(function () {
|
||||
return [
|
||||
{ key: 'v1', index: 1, brand: '东风', model: 'DFH1180', plateNo: '浙A12345', receivableRent: 30000, actualRent: '29800.00', rentRemark: '首期六期一次性付清', receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 200, actual: '200.00', discount: '0.00', remark: '' }, { name: '保险上浮', receivable: 500, actual: '480.00', discount: '20.00', remark: '客户协商' }], receivableService: 700, actualService: '680.00', discountAmount: '200.00', discountRemark: '首月优惠', discountProof: [{ name: '优惠审批单.pdf' }] },
|
||||
{ key: 'v2', index: 2, brand: '福田', model: 'BJ1180', plateNo: '浙A23456', receivableRent: 27000, actualRent: '27000.00', rentRemark: '', receivableDeposit: 8000, serviceItems: [{ name: '保养费用', receivable: 300, actual: '300.00', discount: '0.00', remark: '含首保' }], receivableService: 300, actualService: '300.00', discountAmount: '0.00', discountRemark: '', discountProof: [] },
|
||||
{ key: 'v3', index: 3, brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567', receivableRent: 31200, actualRent: '31200.00', rentRemark: '按合同约定', receivableDeposit: 10000, serviceItems: [{ name: '代处理费用', receivable: 180, actual: '180.00', discount: '0.00', remark: '' }, { name: '上牌服务', receivable: 400, actual: '400.00', discount: '0.00', remark: '已含上牌' }], receivableService: 580, actualService: '580.00', discountAmount: '0.00', discountRemark: '', discountProof: [] },
|
||||
{ key: 'v4', index: 4, brand: '陕汽', model: 'SX1313', plateNo: '', receivableRent: 28800, actualRent: '28500.00', rentRemark: '待交车后补全', receivableDeposit: 9000, serviceItems: [{ name: '保险上浮', receivable: 350, actual: '350.00', discount: '0.00', remark: '' }], receivableService: 350, actualService: '350.00', discountAmount: '300.00', discountRemark: '客户协商减免', discountProof: [] }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var totals = useMemo(function () {
|
||||
var receivableRent = 0, actualRent = 0, receivableDeposit = 0, receivableService = 0, actualService = 0, discountTotal = 0;
|
||||
vehicles.forEach(function (v) {
|
||||
receivableRent += Number(v.receivableRent) || 0;
|
||||
actualRent += parseFloat(v.actualRent) || 0;
|
||||
receivableDeposit += Number(v.receivableDeposit) || 0;
|
||||
receivableService += Number(v.receivableService) || 0;
|
||||
actualService += parseFloat(v.actualService) || 0;
|
||||
discountTotal += parseFloat(v.discountAmount) || 0;
|
||||
});
|
||||
return {
|
||||
receivableRent: receivableRent.toFixed(2),
|
||||
actualRent: actualRent.toFixed(2),
|
||||
receivableDeposit: receivableDeposit.toFixed(2),
|
||||
receivableService: receivableService.toFixed(2),
|
||||
actualService: actualService.toFixed(2),
|
||||
discountTotal: discountTotal.toFixed(2)
|
||||
};
|
||||
}, [vehicles]);
|
||||
|
||||
var receivableTotal = (parseFloat(totals.receivableRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.receivableService)).toFixed(2);
|
||||
var actualTotal = (parseFloat(totals.actualRent) + parseFloat(totals.receivableDeposit) + parseFloat(totals.actualService) - parseFloat(totals.discountTotal)).toFixed(2);
|
||||
|
||||
var receivablePopoverContent = React.createElement('div', { style: { padding: 8, minWidth: 220 } },
|
||||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'),
|
||||
React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableRent + ' 元')),
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')),
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableService + ' 元'))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
var actualPopoverContent = React.createElement('div', { style: { padding: 8, minWidth: 220 } },
|
||||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '项目'),
|
||||
React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '金额')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收车辆月租金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualRent + ' 元')),
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计应收车辆保证金'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.receivableDeposit + ' 元')),
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计实收服务费'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.actualService + ' 元')),
|
||||
React.createElement('tr', null, React.createElement('td', { style: { padding: '6px 8px' } }, '总计减免金额'), React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, totals.discountTotal + ' 元'))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// 客户付款信息:到账/开票历史
|
||||
var paymentList = useMemo(function () {
|
||||
return [
|
||||
{ id: '1', arrivalTime: '2026-03-01 10:00', arrivalAmount: '50000.00', invoiceTime: '2026-03-02 14:30', invoiceFiles: [{ name: '发票-HT-ZL-2025-001-001.pdf' }], remark: '首笔到账' },
|
||||
{ id: '2', arrivalTime: '2026-03-10 09:00', arrivalAmount: '35280.00', invoiceTime: '2026-03-11 11:00', invoiceFiles: [{ name: '发票-HT-ZL-2025-001-002.pdf' }], remark: '第二笔' }
|
||||
];
|
||||
}, []);
|
||||
var sumArrival = paymentList.reduce(function (s, r) { return s + (parseFloat(r.arrivalAmount) || 0); }, 0);
|
||||
var unpaidAmount = (parseFloat(actualTotal) - sumArrival).toFixed(2);
|
||||
if (parseFloat(unpaidAmount) < 0) unpaidAmount = '0.00';
|
||||
|
||||
// 审批情况:竖向步骤条
|
||||
var approvalSteps = useMemo(function () {
|
||||
return [
|
||||
{ title: '业务部主管', department: '业务1部', status: '已通过', person: '张经理', approveTime: '2026-02-28 09:30' },
|
||||
{ title: '财务部', department: '财务部', status: '已通过', person: '李财务', approveTime: '2026-02-28 10:15' },
|
||||
{ title: '事业部主管', department: '事业部', status: '待审批', person: '-', approveTime: '-' }
|
||||
];
|
||||
}, []);
|
||||
|
||||
var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
|
||||
var cardStyle = { marginBottom: 16 };
|
||||
var labelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
|
||||
var formRowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', marginBottom: 16 };
|
||||
var formItemStyle = { marginBottom: 12 };
|
||||
var highlightStyle = { color: '#1890ff', fontWeight: 600, cursor: 'pointer' };
|
||||
var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, lineHeight: '22px', minHeight: 22 };
|
||||
var thBase = { padding: '10px 12px', border: '1px solid #f0f0f0', whiteSpace: 'nowrap', backgroundColor: '#fafafa' };
|
||||
var tdBase = { padding: '8px 12px', border: '1px solid #f0f0f0', fontSize: 13 };
|
||||
|
||||
function handleBack() {
|
||||
if (window.__receivableBack) window.__receivableBack();
|
||||
else message.info('返回提车应收款列表(原型)');
|
||||
}
|
||||
|
||||
return React.createElement('div', { style: layoutStyle },
|
||||
React.createElement('div', { style: { marginBottom: 16 } },
|
||||
React.createElement(Breadcrumb, {
|
||||
items: [
|
||||
{ title: '财务管理' },
|
||||
{ title: '提车应收款' },
|
||||
{ title: '提车收款单' }
|
||||
]
|
||||
})
|
||||
),
|
||||
React.createElement(Card, { title: '项目信息', style: cardStyle },
|
||||
React.createElement('div', { style: formRowStyle },
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同编码'), React.createElement('div', { style: valueStyle }, projectInfo.contractCode || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同类型'), React.createElement('div', { style: valueStyle }, projectInfo.contractType || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '项目名称'), React.createElement('div', { style: valueStyle }, projectInfo.projectName || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '客户名称'), React.createElement('div', { style: valueStyle }, projectInfo.customerName || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '付款方式'), React.createElement('div', { style: valueStyle }, projectInfo.paymentMethod || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '付款周期'), React.createElement('div', { style: valueStyle }, projectInfo.paymentCycle || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同生效时间'), React.createElement('div', { style: valueStyle }, projectInfo.contractStart || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '合同结束时间'), React.createElement('div', { style: valueStyle }, projectInfo.contractEnd || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '业务部门'), React.createElement('div', { style: valueStyle }, projectInfo.businessDept || '—')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '业务负责人'), React.createElement('div', { style: valueStyle }, projectInfo.businessPerson || '—'))
|
||||
)
|
||||
),
|
||||
React.createElement(Card, { title: '提车应收款信息', style: cardStyle },
|
||||
React.createElement(React.Fragment, null,
|
||||
React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'center' } },
|
||||
React.createElement(Popover, { content: receivablePopoverContent, title: '应收款明细', trigger: 'click' },
|
||||
React.createElement('span', { style: { cursor: 'pointer' } }, '应收款总额:', React.createElement('span', { style: highlightStyle }, receivableTotal, ' 元'))
|
||||
),
|
||||
React.createElement(Popover, { content: actualPopoverContent, title: '实收款明细', trigger: 'click' },
|
||||
React.createElement('span', { style: { cursor: 'pointer' } }, '实收款总额:', React.createElement('span', { style: highlightStyle }, actualTotal, ' 元'))
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { overflowX: 'auto', marginBottom: 0 } },
|
||||
React.createElement('table', { style: { width: '100%', minWidth: 1500, borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 50 }) }, '序号'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 80 }) }, '品牌'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '型号'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '车牌号'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 120 }) }, '应收车辆月租金'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '实收车辆月租金'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 130 }) }, '车辆租金备注'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '减免金额'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '减免金额备注'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 100 }) }, '减免证明'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 110 }) }, '应收车辆保证金'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 90 }) }, '服务费项目'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 90 }) }, '应收服务费'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { textAlign: 'right', width: 100 }) }, '实收服务费')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
vehicles.map(function (row) {
|
||||
var servicePopover = React.createElement('div', { style: { padding: 8, minWidth: 320 } },
|
||||
React.createElement('div', { style: { fontWeight: 600, marginBottom: 8 } }, '服务费项目'),
|
||||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '服务项目'),
|
||||
React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '应收费用'),
|
||||
React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '实收费用'),
|
||||
React.createElement('th', { style: { textAlign: 'right', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '减免费用'),
|
||||
React.createElement('th', { style: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #f0f0f0' } }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
(row.serviceItems || []).map(function (s, si) {
|
||||
return React.createElement('tr', { key: si },
|
||||
React.createElement('td', { style: { padding: '6px 8px' } }, s.name),
|
||||
React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.receivable != null ? s.receivable : '') + ' 元'),
|
||||
React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.actual != null ? s.actual : '') + ' 元'),
|
||||
React.createElement('td', { style: { textAlign: 'right', padding: '6px 8px' } }, (s.discount != null ? s.discount : '0.00') + ' 元'),
|
||||
React.createElement('td', { style: { padding: '6px 8px' } }, s.remark || '—')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
var proofNames = (row.discountProof || []).map(function (p) { return p.name; }).join('、') || '—';
|
||||
return React.createElement('tr', { key: row.key },
|
||||
React.createElement('td', { style: tdBase }, row.index),
|
||||
React.createElement('td', { style: tdBase }, row.brand),
|
||||
React.createElement('td', { style: tdBase }, row.model),
|
||||
React.createElement('td', { style: tdBase }, row.plateNo || '—'),
|
||||
React.createElement('td', { style: Object.assign({}, tdBase, { textAlign: 'right' }) }, (row.receivableRent || 0) + ' 元'),
|
||||
React.createElement('td', { style: tdBase }, (row.actualRent || '0.00') + ' 元'),
|
||||
React.createElement('td', { style: tdBase }, row.rentRemark || '—'),
|
||||
React.createElement('td', { style: tdBase }, (row.discountAmount || '0.00') + ' 元'),
|
||||
React.createElement('td', { style: tdBase }, row.discountRemark || '—'),
|
||||
React.createElement('td', { style: tdBase }, proofNames),
|
||||
React.createElement('td', { style: Object.assign({}, tdBase, { textAlign: 'right' }) }, (row.receivableDeposit || 0) + ' 元'),
|
||||
React.createElement('td', { style: tdBase },
|
||||
React.createElement(Popover, { content: servicePopover, title: null, trigger: 'click', open: servicePopoverOpen[0] === row.key, onOpenChange: function (open) { servicePopoverOpen[1](open ? row.key : null); } },
|
||||
React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 } }, '管理')
|
||||
)
|
||||
),
|
||||
React.createElement('td', { style: Object.assign({}, tdBase, { textAlign: 'right' }) }, (row.receivableService || 0) + ' 元'),
|
||||
React.createElement('td', { style: Object.assign({}, tdBase, { textAlign: 'right' }) }, (row.actualService || '0.00') + ' 元')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement('div', { style: { marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, maxWidth: 800 } },
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '氢费预付款应收金额'), React.createElement('div', { style: valueStyle }, '3580.00 元')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '氢费预付款实收金额'), React.createElement('div', { style: valueStyle }, '3500.00 元')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '减免金额'), React.createElement('div', { style: valueStyle }, '80.00 元')),
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '减免金额备注'), React.createElement('div', { style: valueStyle }, '预付款批量减免'))
|
||||
),
|
||||
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px 24px', marginTop: 16 } },
|
||||
React.createElement('div', { style: formItemStyle }, React.createElement('div', { style: labelStyle }, '开票方式'), React.createElement('div', { style: valueStyle }, '先开票后付款')),
|
||||
React.createElement('div', { style: Object.assign({}, formItemStyle, { gridColumn: '1 / -1' }) }, React.createElement('div', { style: labelStyle }, '开票备注'), React.createElement('div', { style: valueStyle }, '增值税专用发票,税率13%,开票项目:*现代服务*车辆租赁费;备注:嘉兴氢能示范项目-提车首付款'))
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(Card, { title: '客户付款信息', style: cardStyle },
|
||||
React.createElement('div', { style: { marginBottom: 12, fontSize: 14 } }, '未付金额:', React.createElement('span', { style: highlightStyle }, unpaidAmount, ' 元')),
|
||||
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 160 }) }, '到账时间'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 120 }) }, '到账金额'),
|
||||
React.createElement('th', { style: Object.assign({}, thBase, { width: 160 }) }, '开票时间'),
|
||||
React.createElement('th', { style: thBase }, '发票附件'),
|
||||
React.createElement('th', { style: thBase }, '备注')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
paymentList.map(function (r) {
|
||||
var files = (r.invoiceFiles || []).map(function (f, i) { return React.createElement(Button, { key: i, type: 'link', size: 'small', style: { padding: 0, height: 'auto' }, onClick: function () { message.info('预览:' + (f.name || '附件')); } }, f.name || '附件'); });
|
||||
var remarkEl = r.remark && r.remark.length > 20
|
||||
? React.createElement(Tooltip, { title: r.remark }, React.createElement('span', { style: { display: 'inline-block', maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, r.remark))
|
||||
: (r.remark || '—');
|
||||
return React.createElement('tr', { key: r.id },
|
||||
React.createElement('td', { style: tdBase }, r.arrivalTime || '—'),
|
||||
React.createElement('td', { style: tdBase }, r.arrivalAmount ? r.arrivalAmount + ' 元' : '—'),
|
||||
React.createElement('td', { style: tdBase }, r.invoiceTime || '—'),
|
||||
React.createElement('td', { style: tdBase }, files.length ? React.createElement('span', null, files) : '—'),
|
||||
React.createElement('td', { style: tdBase }, remarkEl)
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
React.createElement(Card, { title: '审批情况', style: cardStyle },
|
||||
React.createElement(Steps, {
|
||||
direction: 'vertical',
|
||||
current: approvalSteps.findIndex(function (s) { return s.status === '待审批'; }) >= 0 ? approvalSteps.findIndex(function (s) { return s.status === '待审批'; }) : approvalSteps.length,
|
||||
style: { paddingLeft: 8 },
|
||||
items: approvalSteps.map(function (step, idx) {
|
||||
return {
|
||||
title: step.department || step.title,
|
||||
description: React.createElement('div', { style: { marginTop: 4, fontSize: 13, color: 'rgba(0,0,0,0.65)' } },
|
||||
React.createElement('div', null, '审批状态:', step.status),
|
||||
React.createElement('div', null, '审批人:', step.person),
|
||||
React.createElement('div', null, '审批时间:', step.approveTime)
|
||||
),
|
||||
status: step.status === '已通过' ? 'finish' : step.status === '待审批' ? 'wait' : 'process'
|
||||
};
|
||||
})
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: { marginTop: 24 } },
|
||||
React.createElement(Button, { onClick: handleBack }, '返回')
|
||||
)
|
||||
);
|
||||
};
|
||||
465
web端/财务管理/提车应收款.jsx
Normal file
465
web端/财务管理/提车应收款.jsx
Normal file
@@ -0,0 +1,465 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 财务管理 - 提车应收款(2026年3月5日版本)
|
||||
|
||||
const Component = function () {
|
||||
var useState = React.useState;
|
||||
var useMemo = React.useMemo;
|
||||
|
||||
var antd = window.antd;
|
||||
var Breadcrumb = antd.Breadcrumb;
|
||||
var Card = antd.Card;
|
||||
var Table = antd.Table;
|
||||
var Button = antd.Button;
|
||||
var Select = antd.Select;
|
||||
var Input = antd.Input;
|
||||
var Space = antd.Space;
|
||||
var Popover = antd.Popover;
|
||||
var Tag = antd.Tag;
|
||||
var Modal = antd.Modal;
|
||||
var message = antd.message;
|
||||
|
||||
// 筛选条件
|
||||
var filterContractCode = useState(undefined);
|
||||
var filterProjectName = useState(undefined);
|
||||
var filterCustomerName = useState(undefined);
|
||||
var filterBusinessDept = useState(undefined);
|
||||
var filterBusinessPerson = useState(undefined);
|
||||
// 主表展开行:只允许同时展开一行,展开新的则收起其他
|
||||
var expandedRowKeysState = useState([]);
|
||||
var expandedRowKeys = expandedRowKeysState[0];
|
||||
var setExpandedRowKeys = expandedRowKeysState[1];
|
||||
var deleteConfirmVisible = useState(false);
|
||||
var deleteConfirmRecord = useState(null);
|
||||
var deleteConfirmParentRecord = useState(null);
|
||||
var requirementModalVisible = useState(false);
|
||||
var filterExpanded = useState(false);
|
||||
|
||||
// 主表数据:按合同编码聚合,每条主表下有多条提车应收款明细(子表)
|
||||
var mainListData = [
|
||||
{
|
||||
contractCode: 'HT-ZL-2025-001',
|
||||
contractType: '正式合同',
|
||||
projectName: '嘉兴氢能示范项目',
|
||||
customerName: '嘉兴某某物流有限公司',
|
||||
businessDept: '业务1部',
|
||||
businessPerson: '张经理',
|
||||
contractEffectiveDate: '2025-01-15',
|
||||
totalReceivable: 256800.00,
|
||||
totalActual: 255700.00,
|
||||
totalDiscount: 1100.00,
|
||||
totalFinanceReceived: 209900.00,
|
||||
children: [
|
||||
{ seq: 1, auditStatus: '审批通过', creator: '张三', chargeTime: '2026-02-20 10:00', deliveryCount: 3, deliveryVehicles: [{ brand: '东风', model: 'DFH1180', plateNo: '浙A12345' }, { brand: '福田', model: 'BJ1180', plateNo: '浙A23456' }, { brand: '重汽', model: 'ZZ1187', plateNo: '浙A34567' }], receivableTotal: 98700.00, actualTotal: 98200.00, discountTotal: 500.00, discountProof: '首月优惠审批单', arrivalAmount: 98200.00, arrivalTime: '2026-02-21 14:30', financeReceived: 98200.00, isInvoiced: '已开票', invoiceMethod: '先开票后付款', invoiceTime: '2026-02-22 11:00', invoicedAmount: 98200.00, invoiceAttachment: '发票-HT-ZL-2025-001-001.pdf' },
|
||||
{ seq: 2, auditStatus: '待审批', creator: '李四', chargeTime: '2026-03-01 09:30', deliveryCount: 2, deliveryVehicles: [{ brand: '陕汽', model: 'SX1313', plateNo: '浙A45678' }, { brand: '解放', model: 'J6P', plateNo: '浙A56789' }], receivableTotal: 57300.00, actualTotal: 57300.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 57300.00, arrivalTime: '2026-03-02 10:00', financeReceived: 57300.00, isInvoiced: '部分开票', invoiceMethod: '先付款后开票', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' },
|
||||
{ seq: 3, auditStatus: '审批中', creator: '王五', chargeTime: '2026-03-10 11:00', deliveryCount: 1, deliveryVehicles: [{ brand: '江淮', model: '格尔发K5', plateNo: '浙A67890' }], receivableTotal: 32800.00, actualTotal: 32800.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 32800.00, arrivalTime: '2026-03-11 09:00', financeReceived: 32800.00, isInvoiced: '未开票', invoiceMethod: '先付款后开票', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' },
|
||||
{ seq: 4, auditStatus: '已驳回', creator: '赵六', chargeTime: '2026-03-15 14:20', deliveryCount: 2, deliveryVehicles: [{ brand: '东风', model: 'DFH1250', plateNo: '浙A11111' }, { brand: '福田', model: '欧曼EST', plateNo: '浙A22222' }], receivableTotal: 45800.00, actualTotal: 45800.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 0.00, arrivalTime: '-', financeReceived: 0.00, isInvoiced: '未开票', invoiceMethod: '先付款后开票', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' },
|
||||
{ seq: 5, auditStatus: '审批通过', creator: '张三', chargeTime: '2026-03-20 09:00', deliveryCount: 2, deliveryVehicles: [{ brand: '重汽', model: '豪沃T7H', plateNo: '浙B33333' }, { brand: '陕汽', model: '德龙X3000', plateNo: '浙B44444' }], receivableTotal: 22200.00, actualTotal: 21600.00, discountTotal: 600.00, discountProof: '客户协商减免', arrivalAmount: 21600.00, arrivalTime: '2026-03-21 10:30', financeReceived: 21600.00, isInvoiced: '已开票', invoiceMethod: '先开票后付款', invoiceTime: '2026-03-22 14:00', invoicedAmount: 21600.00, invoiceAttachment: '发票-HT-ZL-2025-001-005.pdf' }
|
||||
]
|
||||
},
|
||||
{
|
||||
contractCode: 'HT-ZL-2025-002',
|
||||
contractType: '正式合同',
|
||||
projectName: '上海物流租赁项目',
|
||||
customerName: '上海某某运输公司',
|
||||
businessDept: '业务2部',
|
||||
businessPerson: '李专员',
|
||||
contractEffectiveDate: '2025-02-01',
|
||||
totalReceivable: 132600.00,
|
||||
totalActual: 132600.00,
|
||||
totalDiscount: 0.00,
|
||||
totalFinanceReceived: 132600.00,
|
||||
children: [
|
||||
{ seq: 1, auditStatus: '审批通过', creator: '李专员', chargeTime: '2026-02-28 14:00', deliveryCount: 2, deliveryVehicles: [{ brand: '江淮', model: '格尔发K5', plateNo: '沪B11111' }, { brand: '东风', model: 'DFH1250', plateNo: '沪B22222' }], receivableTotal: 65800.00, actualTotal: 65800.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 65800.00, arrivalTime: '2026-03-01 09:00', financeReceived: 65800.00, isInvoiced: '已开票', invoiceMethod: '先开票后付款', invoiceTime: '2026-03-02 15:20', invoicedAmount: 65800.00, invoiceAttachment: '发票-HT-ZL-2025-002.pdf' },
|
||||
{ seq: 2, auditStatus: '审批通过', creator: '李专员', chargeTime: '2026-03-08 10:30', deliveryCount: 1, deliveryVehicles: [{ brand: '解放', model: 'JH6', plateNo: '沪B33333' }], receivableTotal: 33400.00, actualTotal: 33400.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 33400.00, arrivalTime: '2026-03-09 11:00', financeReceived: 33400.00, isInvoiced: '部分开票', invoiceMethod: '先付款后开票', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' },
|
||||
{ seq: 3, auditStatus: '待审批', creator: '李专员', chargeTime: '2026-03-18 16:00', deliveryCount: 2, deliveryVehicles: [{ brand: '福田', model: 'BJ1180', plateNo: '沪B44444' }, { brand: '重汽', model: 'ZZ1187', plateNo: '沪B55555' }], receivableTotal: 33400.00, actualTotal: 33400.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 33400.00, arrivalTime: '2026-03-19 09:00', financeReceived: 33400.00, isInvoiced: '未开票', invoiceMethod: '先开票后付款', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' }
|
||||
]
|
||||
},
|
||||
{
|
||||
contractCode: 'HT-ZL-2025-003',
|
||||
contractType: '正式合同',
|
||||
projectName: '杭州城配租赁项目',
|
||||
customerName: '杭州某某租赁有限公司',
|
||||
businessDept: '业务3部',
|
||||
businessPerson: '王专员',
|
||||
contractEffectiveDate: '2025-02-10',
|
||||
totalReceivable: 82400.00,
|
||||
totalActual: 82400.00,
|
||||
totalDiscount: 0.00,
|
||||
totalFinanceReceived: 41200.00,
|
||||
children: [
|
||||
{ seq: 1, auditStatus: '已驳回', creator: '王专员', chargeTime: '2026-03-05 11:00', deliveryCount: 1, deliveryVehicles: [{ brand: '福田', model: '欧曼EST', plateNo: '浙C33333' }], receivableTotal: 41200.00, actualTotal: 41200.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 0.00, arrivalTime: '-', financeReceived: 0.00, isInvoiced: '未开票', invoiceMethod: '先付款后开票', invoiceTime: '-', invoicedAmount: '0.00', invoiceAttachment: '-' },
|
||||
{ seq: 2, auditStatus: '审批通过', creator: '王专员', chargeTime: '2026-03-12 09:15', deliveryCount: 1, deliveryVehicles: [{ brand: '东风', model: 'DFH1180', plateNo: '浙C44444' }], receivableTotal: 41200.00, actualTotal: 41200.00, discountTotal: 0.00, discountProof: '-', arrivalAmount: 41200.00, arrivalTime: '2026-03-13 14:00', financeReceived: 41200.00, isInvoiced: '已开票', invoiceMethod: '先开票后付款', invoiceTime: '2026-03-14 10:00', invoicedAmount: 41200.00, invoiceAttachment: '发票-HT-ZL-2025-003.pdf' }
|
||||
]
|
||||
}
|
||||
];
|
||||
var mainListState = useState(mainListData);
|
||||
var mainList = mainListState[0];
|
||||
var setMainList = mainListState[1];
|
||||
|
||||
var filterOptions = useMemo(function () {
|
||||
var list = mainList;
|
||||
var codes = [], projects = [], customers = [], depts = [], persons = [];
|
||||
list.forEach(function (r) {
|
||||
if (r.contractCode && codes.indexOf(r.contractCode) === -1) codes.push(r.contractCode);
|
||||
if (r.projectName && projects.indexOf(r.projectName) === -1) projects.push(r.projectName);
|
||||
if (r.customerName && customers.indexOf(r.customerName) === -1) customers.push(r.customerName);
|
||||
if (r.businessDept && depts.indexOf(r.businessDept) === -1) depts.push(r.businessDept);
|
||||
if (r.businessPerson && persons.indexOf(r.businessPerson) === -1) persons.push(r.businessPerson);
|
||||
});
|
||||
return {
|
||||
contractCode: codes.map(function (v) { return { value: v, label: v }; }),
|
||||
projectName: projects.map(function (v) { return { value: v, label: v }; }),
|
||||
customerName: customers.map(function (v) { return { value: v, label: v }; }),
|
||||
businessDept: depts.map(function (v) { return { value: v, label: v }; }),
|
||||
businessPerson: persons.map(function (v) { return { value: v, label: v }; })
|
||||
};
|
||||
}, [mainList]);
|
||||
var filteredMainList = useMemo(function () {
|
||||
var list = mainList;
|
||||
var code = filterContractCode[0];
|
||||
var project = filterProjectName[0];
|
||||
var customer = filterCustomerName[0];
|
||||
var dept = filterBusinessDept[0];
|
||||
var person = filterBusinessPerson[0];
|
||||
if (code) list = list.filter(function (r) { return r.contractCode === code; });
|
||||
if (project) list = list.filter(function (r) { return r.projectName === project; });
|
||||
if (customer) list = list.filter(function (r) { return r.customerName === customer; });
|
||||
if (dept) list = list.filter(function (r) { return r.businessDept === dept; });
|
||||
if (person) list = list.filter(function (r) { return r.businessPerson === person; });
|
||||
return list;
|
||||
}, [mainList, filterContractCode[0], filterProjectName[0], filterCustomerName[0], filterBusinessDept[0], filterBusinessPerson[0]]);
|
||||
|
||||
// 主表 dataSource 去掉 children,避免 Table 把子节点当树形数据渲染出多余空白行;子表数据通过 _detailList 在展开行内使用
|
||||
var mainTableDataSource = useMemo(function () {
|
||||
return filteredMainList.map(function (r) {
|
||||
var o = {};
|
||||
for (var k in r) if (k !== 'children') o[k] = r[k];
|
||||
o._detailList = r.children || [];
|
||||
return o;
|
||||
});
|
||||
}, [filteredMainList]);
|
||||
|
||||
function fmtMoney(v) {
|
||||
if (v === null || v === undefined) return '0.00元';
|
||||
var n = typeof v === 'number' ? v : parseFloat(v);
|
||||
return (isNaN(n) ? '0.00' : n.toFixed(2)) + '元';
|
||||
}
|
||||
|
||||
function goCollect(record) {
|
||||
if (window.__receivableToCollect) window.__receivableToCollect(record);
|
||||
else message.info('跳转至提车应收款-收款(原型)');
|
||||
}
|
||||
function goView(record) {
|
||||
message.info('查看提车收款单详情(原型)');
|
||||
}
|
||||
function goEdit(record) {
|
||||
if (window.__receivableToEdit) window.__receivableToEdit(record);
|
||||
else message.info('跳转至提车应收款-编辑(原型)');
|
||||
}
|
||||
function goInvoice(record) {
|
||||
if (window.__receivableToInvoice) window.__receivableToInvoice(record);
|
||||
else message.info('跳转至开票(原型)');
|
||||
}
|
||||
|
||||
var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
|
||||
var cardStyle = { marginBottom: 16 };
|
||||
var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
|
||||
var filterItemStyle = { marginBottom: 12 };
|
||||
var filterControlStyle = { width: '100%' };
|
||||
|
||||
// 主表列(宽度压缩在一屏内显示)
|
||||
var mainColumns = [
|
||||
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 120, ellipsis: true, render: function (v) { return v || '—'; } },
|
||||
{ title: '合同类型', dataIndex: 'contractType', key: 'contractType', width: 88, ellipsis: true, render: function (v) { return v || '—'; } },
|
||||
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 120, ellipsis: true, render: function (v) { return v || '—'; } },
|
||||
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 120, ellipsis: true, render: function (v) { return v || '—'; } },
|
||||
{ title: '合同生效日期', dataIndex: 'contractEffectiveDate', key: 'contractEffectiveDate', width: 110, render: function (v) { return v || '—'; } },
|
||||
{ title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 100, render: function (v) { return v || '—'; } },
|
||||
{ title: '业务负责人', dataIndex: 'businessPerson', key: 'businessPerson', width: 100, render: function (v) { return v || '—'; } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
render: function (_, record) {
|
||||
return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goCollect(record); } }, '提车收款单');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 子表:提车数量气泡用 state 控制当前展开的行
|
||||
var deliveryPopoverOpen = useState(null);
|
||||
|
||||
function renderDeliveryPopover(record) {
|
||||
var vehicles = record.deliveryVehicles || [];
|
||||
var listStyle = { width: '100%', borderCollapse: 'collapse', fontSize: 13 };
|
||||
var thStyle = { padding: '6px 10px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', backgroundColor: '#fafafa', fontWeight: 600 };
|
||||
var tdStyle = { padding: '6px 10px', borderBottom: '1px solid #f0f0f0' };
|
||||
var content = vehicles.length === 0 ? React.createElement('div', { style: { padding: 8 } }, '—') : React.createElement('div', { style: { padding: 0, minWidth: 200 } },
|
||||
React.createElement('table', { style: listStyle },
|
||||
React.createElement('thead', null,
|
||||
React.createElement('tr', null,
|
||||
React.createElement('th', { style: thStyle }, '品牌'),
|
||||
React.createElement('th', { style: thStyle }, '型号'),
|
||||
React.createElement('th', { style: thStyle }, '车牌号')
|
||||
)
|
||||
),
|
||||
React.createElement('tbody', null,
|
||||
vehicles.map(function (v, i) {
|
||||
return React.createElement('tr', { key: i },
|
||||
React.createElement('td', { style: tdStyle }, v.brand || '—'),
|
||||
React.createElement('td', { style: tdStyle }, v.model || '—'),
|
||||
React.createElement('td', { style: tdStyle }, v.plateNo || '—')
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
var popoverKey = (record._parentCode || '') + '-' + record.seq + '-' + (record.chargeTime || '');
|
||||
return React.createElement(Popover, {
|
||||
content: content,
|
||||
title: '车辆详情',
|
||||
trigger: 'click',
|
||||
open: deliveryPopoverOpen[0] === popoverKey,
|
||||
onOpenChange: function (open) { deliveryPopoverOpen[1](open ? popoverKey : null); }
|
||||
}, React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 } }, String(record.deliveryCount)));
|
||||
}
|
||||
|
||||
// 子表左缩进与主表展开列宽一致,使序号左侧边框与合同编码左侧边框对齐
|
||||
var expandColumnWidth = 48;
|
||||
var subColumns = [
|
||||
{ title: '序号', dataIndex: 'seq', key: 'seq', width: 52, align: 'center' },
|
||||
{
|
||||
title: '审批状态',
|
||||
dataIndex: 'auditStatus',
|
||||
key: 'auditStatus',
|
||||
width: 90,
|
||||
render: function (v, record) {
|
||||
var status = (record && record.auditStatus != null) ? String(record.auditStatus).trim() : (v != null ? String(v).trim() : '');
|
||||
var isRejected = status === '已驳回' || status === '审批驳回';
|
||||
var color = status === '审批通过' ? 'success' : status === '待审批' ? 'processing' : status === '审批中' ? 'warning' : isRejected ? 'error' : 'default';
|
||||
var text = isRejected ? '审批驳回' : (status || '—');
|
||||
return React.createElement(Tag, { color: color }, text);
|
||||
}
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'chargeTime', key: 'chargeTime', width: 128, render: function (v) { return v || '—'; } },
|
||||
{ title: '创建人', dataIndex: 'creator', key: 'creator', width: 88, render: function (v) { return v || '—'; } },
|
||||
{ title: '提车数量', key: 'deliveryCount', width: 76, render: function (_, record) { return renderDeliveryPopover(record); } },
|
||||
{ title: '应收款总额', dataIndex: 'receivableTotal', key: 'receivableTotal', width: 98, align: 'right', render: function (v) { return fmtMoney(v); } },
|
||||
{ title: '实收款总额', dataIndex: 'actualTotal', key: 'actualTotal', width: 98, align: 'right', render: function (v) { return fmtMoney(v); } },
|
||||
{ title: '减免总金额', dataIndex: 'discountTotal', key: 'discountTotal', width: 88, align: 'right', render: function (v) { return fmtMoney(v); } },
|
||||
{ title: '实际到账金额', dataIndex: 'arrivalAmount', key: 'arrivalAmount', width: 100, align: 'right', render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; return fmtMoney(v); } },
|
||||
{ title: '是否已开票', dataIndex: 'isInvoiced', key: 'isInvoiced', width: 88, render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; if (v === true || v === '已开票') return '已开票'; if (v === '部分开票') return '部分开票'; return '未开票'; } },
|
||||
{ title: '已开票金额', dataIndex: 'invoicedAmount', key: 'invoicedAmount', width: 100, align: 'right', render: function (v, record) { if (record && record.auditStatus === '待审批') return '—'; var n = typeof v === 'number' ? v : parseFloat(v); return fmtMoney(isNaN(n) ? 0 : n); } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
render: function (_, record) {
|
||||
var auditStatus = record && record.auditStatus;
|
||||
var isPending = auditStatus === '待审批';
|
||||
var isRejected = auditStatus === '已驳回' || auditStatus === '审批驳回';
|
||||
var isApproved = auditStatus === '审批通过';
|
||||
var canEdit = isPending || isRejected;
|
||||
var parentRecord = record && record._parentRecord;
|
||||
function handleDelete() {
|
||||
deleteConfirmRecord[1](record);
|
||||
deleteConfirmParentRecord[1](parentRecord);
|
||||
deleteConfirmVisible[1](true);
|
||||
}
|
||||
return React.createElement(Space, { size: 'small' },
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goView(record); } }, '查看'),
|
||||
canEdit ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goEdit(record); } }, '编辑') : null,
|
||||
(isPending || isRejected) ? React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: handleDelete }, '删除') : null,
|
||||
isApproved ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goInvoice(record); } }, '开票') : null
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
function confirmDeleteSub() {
|
||||
var parent = deleteConfirmParentRecord[0];
|
||||
var row = deleteConfirmRecord[0];
|
||||
var list = (parent && (parent._detailList || parent.children)) || [];
|
||||
if (parent && row && list.length >= 0) {
|
||||
var newChildren = list.filter(function (r) { return !(r.seq === row.seq && r.chargeTime === row.chargeTime); });
|
||||
// 删除后序号重新生成 1、2、3...
|
||||
newChildren = newChildren.map(function (r, index) { var o = {}; for (var k in r) o[k] = r[k]; o.seq = index + 1; return o; });
|
||||
setMainList(function (prev) {
|
||||
return prev.map(function (p) {
|
||||
if (p.contractCode !== parent.contractCode) return p;
|
||||
var next = {}; for (var k in p) next[k] = p[k]; next.children = newChildren; return next;
|
||||
});
|
||||
});
|
||||
}
|
||||
deleteConfirmVisible[1](false);
|
||||
deleteConfirmRecord[1](null);
|
||||
deleteConfirmParentRecord[1](null);
|
||||
message.success('已删除');
|
||||
}
|
||||
function expandRowRender(record) {
|
||||
var rows = (record._detailList || []).map(function (r) { var o = {}; for (var k in r) o[k] = r[k]; o._parentCode = record.contractCode; o._parentRecord = record; return o; });
|
||||
var sumReceivable = rows.reduce(function (acc, r) { return acc + (Number(r.receivableTotal) || 0); }, 0);
|
||||
var sumActual = rows.reduce(function (acc, r) { return acc + (Number(r.actualTotal) || 0); }, 0);
|
||||
var sumDiscount = rows.reduce(function (acc, r) { return acc + (Number(r.discountTotal) || 0); }, 0);
|
||||
var sumArrival = rows.reduce(function (acc, r) { return acc + (Number(r.arrivalAmount) || 0); }, 0);
|
||||
var sumInvoiced = rows.reduce(function (acc, r) { return acc + (Number(r.invoicedAmount) || 0); }, 0);
|
||||
var summaryStyle = { backgroundColor: '#fafafa', fontWeight: 600 };
|
||||
return React.createElement('div', {
|
||||
style: { marginBottom: 0, paddingLeft: expandColumnWidth, boxSizing: 'border-box' },
|
||||
draggable: false,
|
||||
onDragStart: function (e) { e.preventDefault(); }
|
||||
},
|
||||
React.createElement(Table, {
|
||||
rowKey: function (r) { return (record.contractCode || '') + '-' + (r.seq || r.chargeTime || Math.random()); },
|
||||
columns: subColumns,
|
||||
dataSource: rows,
|
||||
pagination: false,
|
||||
size: 'small',
|
||||
bordered: true,
|
||||
summary: rows.length > 0 ? function () {
|
||||
return React.createElement(Table.Summary, null,
|
||||
React.createElement(Table.Summary.Row, { style: summaryStyle },
|
||||
React.createElement(Table.Summary.Cell, { index: 0, align: 'center' }, '总计'),
|
||||
React.createElement(Table.Summary.Cell, { index: 1 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 2 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 3 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 4 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 5, align: 'right' }, fmtMoney(sumReceivable)),
|
||||
React.createElement(Table.Summary.Cell, { index: 6, align: 'right' }, fmtMoney(sumActual)),
|
||||
React.createElement(Table.Summary.Cell, { index: 7, align: 'right' }, fmtMoney(sumDiscount)),
|
||||
React.createElement(Table.Summary.Cell, { index: 8, align: 'right' }, fmtMoney(sumArrival)),
|
||||
React.createElement(Table.Summary.Cell, { index: 9 }, ''),
|
||||
React.createElement(Table.Summary.Cell, { index: 10, align: 'right' }, fmtMoney(sumInvoiced)),
|
||||
React.createElement(Table.Summary.Cell, { index: 11 }, '')
|
||||
)
|
||||
);
|
||||
} : null
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
var requirementContent = '提车应收款(2026年3月5日版本)\n一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」模块\n#面包屑:财务管理-提车应收款;\n\n页面分为2个卡片;\n1.筛选:\n#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式;\n1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;\n1.4.业务部门:选择器,支持选择所有业务部门;\n1.5.业务负责人:选择器,支持选择所有业务负责人;\n\n2.列表:\n#列表展示方式为:嵌套子表格,分为主表和子表,可点击主表展开子表,一个合同编码可以展开多个提车应收款明细;\n2.1.主表显示字段为:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人、操作;\n2.1.1.合同编码:显示该提车应收款租赁合同对应合同编码;\n2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同;\n2.1.3.项目名称:显示该租赁合同对应项目名称;\n2.1.4.客户名称:显示该租赁合同对应客户名称;\n2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD;\n2.1.6.业务部门:显示该租赁合同对应业务部门名称;\n2.1.7.业务负责人:显示该租赁合同对应业务负责人;\n2.1.8.操作:提车收款单;\n 2.1.8.1.提车收款单:点击提车收款单,跳转提车收款单页进行子表创建;\n主表右下角为分页符,支持选择单页显示多少条数据;\n\n2.2.子表显示字段为:序号、审批状态、创建时间、提车数量、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、已开票金额、操作;\n2.2.1.序号:按照提车首付款收费单据提交时间从最早开始显示第1条,按照顺序1.2.3....;\n2.2.2.审批状态:审批通过、待审批、审批中、审批驳回;\n 2.2.2.1.审批通过:该审批通过最终节点审批,显示为审批通过;\n 2.2.2.2.待审批:审批提车收款单仅保存,但未提交审批;\n 2.2.2.3.审批中:已发起审批流程,但未完成最终节点审批;\n 2.2.2.4.审批驳回:发起审批流程,但任意节点被驳回。被驳回的提车收款单支持重新修改后提交审批;\n2.2.3.创建时间:显示提车收款单创建时间,格式为:YYYY-MM-DD HH:MM;\n2.2.4.创建人:显示已提车收款单创建人姓名;\n2.2.5.提车数量:显示该提车收费单对应提车数量,点击提车数量弹出气泡卡片,卡片中列表显示品牌、型号、车牌号;\n2.2.6.应收款金额:按照该提车收费单据应收款金额,格式为:xx.xx元;\n2.2.7.实收款金额:按照该提车收费单据实收款金额,格式为:xx.xx元;\n2.2.8.减免金额:按照该提车收费单减免总金额,显示当前子表对应减免总额,格式为:xx.xx元;\n2.2.9.实际到账金额:显示该提车收费单财务填写的实际到账金额,格式为:xx.xx元;\n2.2.10.是否已开票:显示财务是否完成开票,已开票显示为:已开票,未开票显示为:未开票,部分开票显示为:部分开票;\n2.2.11.已开票金额:显示财务已开票金额,格式为:xx.xx元;\n2.2.12.总计:显示应收款总额、实收款总额、减免总金额、实际到账金额、已开票金额等所有子表求和数据;\n2.2.13.操作:查看、编辑、删除、开票;\n 2.2.12.1.查看:点击查看后,跳转提车应收款-查看页面,审批通过、待审批、审批中、审批驳回均可点击查看来查看提车收款单详情;\n 2.2.12.2.编辑:点击编辑后,跳转提车应收款-编辑页面,待审批、审批驳回均可进行编辑操作;\n 2.2.12.2.删除:待审批、审批驳回状态子表支持删除功能,点击弹出二次确认,点击确认后删除该记录;\n 2.2.12.3.开票:点击开票后,进入提车应收款-开票页面,审批通过状态子表才显示,开票由财务人员操作,需要做权限控制,其他用户没有开票入口;';
|
||||
|
||||
return 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 () { requirementModalVisible[1](true); } }, '查看需求说明')
|
||||
),
|
||||
React.createElement(Card, { title: '筛选', style: cardStyle },
|
||||
React.createElement('div', {
|
||||
style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' }
|
||||
},
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '合同编码'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入合同编码',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
style: filterControlStyle,
|
||||
value: filterContractCode[0],
|
||||
onChange: function (v) { filterContractCode[1](v); },
|
||||
options: filterOptions.contractCode,
|
||||
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '项目名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入项目名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
style: filterControlStyle,
|
||||
value: filterProjectName[0],
|
||||
onChange: function (v) { filterProjectName[1](v); },
|
||||
options: filterOptions.projectName,
|
||||
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
|
||||
})
|
||||
),
|
||||
React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '客户名称'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择或输入客户名称',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
style: filterControlStyle,
|
||||
value: filterCustomerName[0],
|
||||
onChange: function (v) { filterCustomerName[1](v); },
|
||||
options: filterOptions.customerName,
|
||||
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
|
||||
})
|
||||
),
|
||||
filterExpanded[0] ? React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '业务部门'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择业务部门',
|
||||
allowClear: true,
|
||||
style: filterControlStyle,
|
||||
value: filterBusinessDept[0],
|
||||
onChange: function (v) { filterBusinessDept[1](v); },
|
||||
options: filterOptions.businessDept
|
||||
})
|
||||
) : null,
|
||||
filterExpanded[0] ? React.createElement('div', { style: filterItemStyle },
|
||||
React.createElement('div', { style: filterLabelStyle }, '业务负责人'),
|
||||
React.createElement(Select, {
|
||||
placeholder: '请选择业务负责人',
|
||||
allowClear: true,
|
||||
style: filterControlStyle,
|
||||
value: filterBusinessPerson[0],
|
||||
onChange: function (v) { filterBusinessPerson[1](v); },
|
||||
options: filterOptions.businessPerson
|
||||
})
|
||||
) : null
|
||||
),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } },
|
||||
React.createElement(Button, { onClick: function () { filterContractCode[1](undefined); filterProjectName[1](undefined); filterCustomerName[1](undefined); filterBusinessDept[1](undefined); filterBusinessPerson[1](undefined); } }, '重置'),
|
||||
React.createElement(Button, { type: 'primary' }, '查询'),
|
||||
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { filterExpanded[1](!filterExpanded[0]); } }, filterExpanded[0] ? '收起' : '展开')
|
||||
)
|
||||
),
|
||||
React.createElement(Card, { title: '列表', style: cardStyle },
|
||||
React.createElement(Table, {
|
||||
rowKey: 'contractCode',
|
||||
columns: mainColumns,
|
||||
dataSource: mainTableDataSource,
|
||||
expandable: {
|
||||
expandedRowKeys: expandedRowKeys,
|
||||
onExpandedRowsChange: function (keys) {
|
||||
setExpandedRowKeys(keys.length ? [keys[keys.length - 1]] : []);
|
||||
},
|
||||
expandedRowRender: expandRowRender,
|
||||
rowExpandable: function (record) { return (record._detailList && record._detailList.length > 0); },
|
||||
columnWidth: expandColumnWidth
|
||||
},
|
||||
pagination: { pageSize: 10, showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } },
|
||||
size: 'middle',
|
||||
bordered: true,
|
||||
scroll: { x: 758 }
|
||||
})
|
||||
),
|
||||
React.createElement(Modal, {
|
||||
title: '确认删除',
|
||||
open: deleteConfirmVisible[0],
|
||||
onCancel: function () { deleteConfirmVisible[1](false); deleteConfirmRecord[1](null); deleteConfirmParentRecord[1](null); },
|
||||
onOk: confirmDeleteSub,
|
||||
okText: '确认',
|
||||
cancelText: '取消'
|
||||
}, '确定要删除该提车收款单记录吗?'),
|
||||
React.createElement(Modal, {
|
||||
title: '需求说明',
|
||||
open: requirementModalVisible[0],
|
||||
onCancel: function () { requirementModalVisible[1](false); },
|
||||
width: 720,
|
||||
footer: React.createElement(Button, { onClick: function () { requirementModalVisible[1](false); } }, '关闭'),
|
||||
bodyStyle: { maxHeight: '70vh', overflow: 'auto' }
|
||||
}, React.createElement('div', { style: { padding: '8px 0' } },
|
||||
React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent))
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -1,292 +0,0 @@
|
||||
// 【重要】必须使用 const Component 作为组件变量名
|
||||
// 提车应收款 - 收费明细(租赁费用管理-提车应收款,第二步由财务填写)
|
||||
|
||||
const Component = function() {
|
||||
var useState = React.useState;
|
||||
var useCallback = React.useCallback;
|
||||
var useMemo = React.useMemo;
|
||||
var antd = window.antd;
|
||||
var DatePicker = antd ? antd.DatePicker : null;
|
||||
var Input = antd ? antd.Input : null;
|
||||
var Upload = antd ? antd.Upload : null;
|
||||
var Button = antd ? antd.Button : null;
|
||||
|
||||
// 模拟:项目信息、车辆(业务提交后数据)、氢费(业务提交后)、开票基础信息
|
||||
var mockProject = useMemo(function() {
|
||||
return { contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '北京朝阳区租赁项目', customerName: '某某科技有限公司', paymentMethod: '预付', paymentCycle: '1个月', department: '运营部', responsible: '张三' };
|
||||
}, []);
|
||||
var mockVehicleList = useMemo(function() {
|
||||
return [
|
||||
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', payableRent: '8000.00', rentRemark: '首月', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }], serviceTotal: '500.00', discount: '0', discountRemark: '' },
|
||||
{ brand: '宝马', model: '530Li', plateNo: '京B67890', payableRent: '7000.00', rentRemark: '', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }], serviceTotal: '250.00', discount: '0', discountRemark: '' }
|
||||
];
|
||||
}, []);
|
||||
var mockHydrogen = useMemo(function() {
|
||||
return { paidTotal: '3580.00', discount: '0.00', discountRemark: '' };
|
||||
}, []);
|
||||
var mockInvoice = useMemo(function() {
|
||||
return { customerName: '某某科技有限公司', taxId: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', phone: '0571-88888888', account: '6222021234567890123', bank: '中国工商银行嘉兴分行', mailingAddress: '浙江省嘉兴市南湖区科技大道1号' };
|
||||
}, []);
|
||||
|
||||
var totalPayable = useMemo(function() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < mockVehicleList.length; i++) {
|
||||
var r = mockVehicleList[i];
|
||||
sum += parseFloat(r.payableRent) || 0;
|
||||
sum += parseFloat(r.deposit) || 0;
|
||||
sum += parseFloat(r.serviceTotal) || 0;
|
||||
sum -= parseFloat(r.discount) || 0;
|
||||
}
|
||||
return sum.toFixed(2);
|
||||
}, [mockVehicleList]);
|
||||
var hydrogenPayableTotal = useMemo(function() {
|
||||
var p = parseFloat(mockHydrogen.paidTotal) || 0;
|
||||
var d = parseFloat(mockHydrogen.discount) || 0;
|
||||
return (p - d >= 0 ? p - d : 0).toFixed(2);
|
||||
}, [mockHydrogen]);
|
||||
var invoiceAmount = useMemo(function() {
|
||||
return (parseFloat(totalPayable) + parseFloat(hydrogenPayableTotal)).toFixed(2);
|
||||
}, [totalPayable, hydrogenPayableTotal]);
|
||||
|
||||
var serviceModalRowState = useState(null);
|
||||
var serviceModalRow = serviceModalRowState[0];
|
||||
var setServiceModalRow = serviceModalRowState[1];
|
||||
var cancelConfirmVisibleState = useState(false);
|
||||
var cancelConfirmVisible = cancelConfirmVisibleState[0];
|
||||
var setCancelConfirmVisible = cancelConfirmVisibleState[1];
|
||||
var requirementVisibleState = useState(false);
|
||||
var requirementVisible = requirementVisibleState[0];
|
||||
var setRequirementVisible = requirementVisibleState[1];
|
||||
|
||||
var invoiceFormState = useState({ invoiceTime: '', remark: '', invoiceFiles: [] });
|
||||
var invoiceForm = invoiceFormState[0];
|
||||
var setInvoiceForm = invoiceFormState[1];
|
||||
|
||||
var handleSubmit = useCallback(function() {
|
||||
alert('提交成功,跳转至提车应收款列表页');
|
||||
}, []);
|
||||
var handleCancelClick = useCallback(function() { setCancelConfirmVisible(true); }, []);
|
||||
var handleCancelConfirm = useCallback(function(confirmed) {
|
||||
setCancelConfirmVisible(false);
|
||||
if (confirmed) alert('已返回提车应收款列表');
|
||||
}, []);
|
||||
|
||||
var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第二步由财务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成;\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期:显示付款周期,类型有1个月-12个月;\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额:显示总金额,格式为xx.xx元,计算方式为:所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.品牌:显示租赁合同中对应品牌;\n2.2.2.型号:显示租赁合同中对应型号;\n2.2.3.车牌号:显示租赁合同中对应车牌号,如无则显示为-;\n2.2.4.应收车辆租金:显示业务提交的应收车辆租金;\n2.2.5.车辆租金备注:显示业务提交的车辆租金备注;\n2.2.6.应收保证金:显示业务提交的应收车辆保证金;\n2.2.7.服务费项目:点击查看,弹出气泡卡片,卡片中为列表,列表显示租赁合同所有已填写服务项目,列表字段为:服务项目、应收费用、服务费用备注;\n 2.2.8.1.服务项目:显示所有租赁合同中已添加的服务项目名称;\n 2.2.8.2.应收费用:显示所有业务提交的费用金额;\n 2.2.8.3.服务费用备注:显示所有业务提交的备注信息;\n2.1.8.应收服务费:根据服务费项目所有服务项目应付费用计算总和得出,不可修改;\n2.1.9.减免金额:显示业务提交的减免金额,格式为xx.xx元;\n2.1.10.减免金额备注:显示业务提交的减免金额备注信息;\n\n3.氢费预付款:\n3.1.上方为氢费预付款应收总额/氢费预付款应收金额/减免金额/减免金额备注;\n3.1.1.氢费应收总额:显示总金额,格式为xx.xx元,计算方式为:氢费预付款实付总额-减免金额;\n3.1.2.氢费预付款应收金额:显示业务填写的氢费预付款应收金额,格式为xx.xx元;\n3.1.2.减免金额:显示业务填写的减免金额,格式为xx.xx元;\n3.1.3.减免金额备注:显示业务填写的减免金额备注;\n\n\n4.开票信息:\n4.1.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.之后为表单,显示客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间、发票附件、备注:\n4.2.1.客户名称:显示租赁合同客户名称;\n4.2.2.纳税人识别号:显示租赁合同对应客户纳税人识别号;\n4.2.3.地址:显示租赁合同对应客户地址;\n4.2.4.电话:显示租赁合同对应客户电话;\n4.2.5.账户:显示租赁合同对应客户账户;\n4.2.6.开户行:显示租赁合同对应客户开户行;\n4.2.7.邮寄地址:显示租赁合同对应客户邮寄地址;\n4.2.8.开票金额:显示总金额,格式为xx.xx元,计算方式为:提车应收款总额+氢费应收总额;\n4.2.9.开票时间:选填项,日期选择器,精确至天;;\n4.2.10.发票附件:选填项,附件上传按钮,点击后上传发票附件,支持多附件(满足可能会有多张发票组合的情况);\n4.2.11.备注:选填项,文本域,用于记录财务相关备注;\n\n5.页面底部为提交、取消;\n5.1.提交:点击提交,提示:提交成功,跳转至:提车应收款列表页;\n5.2.取消:点击取消,进行二次确认气泡提示,提示为:取消将会丢失所有已添加数据,是否确认取消,点击是则返回提车应收款列表页;点击否则关闭二次确认继续操作;';
|
||||
|
||||
var styles = useMemo(function() {
|
||||
return {
|
||||
page: { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' },
|
||||
breadcrumb: { marginBottom: 16, fontSize: 14, color: '#666' },
|
||||
card: { background: '#fff', borderRadius: 8, padding: 24, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' },
|
||||
cardTitle: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' },
|
||||
detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: 12, fontSize: 14 },
|
||||
formCol: { display: 'flex', flexDirection: 'column', gap: 4 },
|
||||
formLabel: { color: '#666', fontSize: 14 },
|
||||
formValue: { color: '#333', fontSize: 14 },
|
||||
table: { width: '100%', borderCollapse: 'collapse', fontSize: 14 },
|
||||
th: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' },
|
||||
td: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333' },
|
||||
linkBtn: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 },
|
||||
totalLine: { marginBottom: 16, fontSize: 14 },
|
||||
totalAmount: { color: '#1890ff', fontWeight: 600, fontSize: 16 },
|
||||
footer: { marginTop: 24, display: 'flex', justifyContent: 'center', gap: 12 },
|
||||
primaryBtn: { padding: '8px 24px', backgroundColor: '#1890ff', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 14 },
|
||||
defaultBtn: { padding: '8px 24px', backgroundColor: '#fff', color: '#666', border: '1px solid #d9d9d9', borderRadius: 4, cursor: 'pointer', fontSize: 14 },
|
||||
formInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, width: '100%', maxWidth: 200 },
|
||||
modalOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 },
|
||||
modalCard: { backgroundColor: '#fff', borderRadius: 8, padding: 24, maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' },
|
||||
modalHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 },
|
||||
modalTitle: { fontSize: 16, fontWeight: 600 },
|
||||
modalClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: 4, cursor: 'pointer' },
|
||||
modalBody: { maxHeight: '60vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6, color: '#333' },
|
||||
pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 8 },
|
||||
requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: 14, background: 'none', border: 'none', padding: 0 }
|
||||
};
|
||||
}, []);
|
||||
|
||||
var projectCard = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardTitle }, '项目信息'),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同编码'), React.createElement('div', { style: styles.formValue }, mockProject.contractCode)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同类型'), React.createElement('div', { style: styles.formValue }, mockProject.contractType)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '项目名称'), React.createElement('div', { style: styles.formValue }, mockProject.projectName))
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockProject.customerName)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款方式'), React.createElement('div', { style: styles.formValue }, mockProject.paymentMethod)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款周期'), React.createElement('div', { style: styles.formValue }, mockProject.paymentCycle))
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务部门'), React.createElement('div', { style: styles.formValue }, mockProject.department)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务负责人'), React.createElement('div', { style: styles.formValue }, mockProject.responsible)),
|
||||
React.createElement('div', { style: styles.formCol }, null)
|
||||
)
|
||||
);
|
||||
|
||||
var vehicleTableHeader = 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 }, '应收服务费'),
|
||||
React.createElement('th', { style: styles.th }, '减免金额'),
|
||||
React.createElement('th', { style: styles.th }, '减免金额备注')
|
||||
)
|
||||
);
|
||||
var vehicleTableBody = React.createElement('tbody', null,
|
||||
mockVehicleList.map(function(row, rowIndex) {
|
||||
return React.createElement('tr', { key: rowIndex },
|
||||
React.createElement('td', { style: styles.td }, row.brand),
|
||||
React.createElement('td', { style: styles.td }, row.model),
|
||||
React.createElement('td', { style: styles.td }, row.plateNo || '-'),
|
||||
React.createElement('td', { style: styles.td }, row.payableRent),
|
||||
React.createElement('td', { style: styles.td }, row.rentRemark || '-'),
|
||||
React.createElement('td', { style: styles.td }, row.deposit),
|
||||
React.createElement('td', { style: styles.td },
|
||||
React.createElement('button', { type: 'button', style: styles.linkBtn, onClick: function() { setServiceModalRow(rowIndex); } }, '查看')
|
||||
),
|
||||
React.createElement('td', { style: styles.td }, row.serviceTotal || '0.00'),
|
||||
React.createElement('td', { style: styles.td }, (function() { var v = parseFloat(row.discount); return (isNaN(v) ? '0.00' : v.toFixed(2)) + ' 元'; })()),
|
||||
React.createElement('td', { style: styles.td }, row.discountRemark || '-')
|
||||
);
|
||||
})
|
||||
);
|
||||
var vehicleBillCard = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardTitle }, '提车应收款'),
|
||||
React.createElement('div', { style: styles.totalLine },
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'),
|
||||
React.createElement('span', { style: styles.totalAmount }, totalPayable + ' 元')
|
||||
),
|
||||
React.createElement('table', { style: styles.table }, vehicleTableHeader, vehicleTableBody)
|
||||
);
|
||||
|
||||
var hydrogenCard = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardTitle }, '氢费预付款'),
|
||||
React.createElement('div', { style: styles.totalLine },
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'),
|
||||
React.createElement('span', { style: styles.totalAmount }, hydrogenPayableTotal + ' 元')
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '氢费预付款应收金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.paidTotal + ' 元')),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discount + ' 元')),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额备注'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discountRemark || '-'))
|
||||
)
|
||||
);
|
||||
|
||||
var dayjs = window.dayjs || null;
|
||||
var invoiceTimeValue = null;
|
||||
if (invoiceForm.invoiceTime && dayjs) {
|
||||
var d = dayjs(invoiceForm.invoiceTime, 'YYYY-MM-DD');
|
||||
invoiceTimeValue = d && d.isValid && d.isValid() ? d : null;
|
||||
}
|
||||
var datePickerEl = DatePicker ? React.createElement(DatePicker, {
|
||||
style: { width: '100%' },
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择开票日期',
|
||||
value: invoiceTimeValue,
|
||||
onChange: function(d, dateStr) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: dateStr || '' }); }); }
|
||||
}) : React.createElement('input', { type: 'date', style: styles.formInput, value: invoiceForm.invoiceTime, onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: e.target.value }); }); } });
|
||||
var uploadButton = Button ? React.createElement(Button, null, '上传附件') : React.createElement('button', { type: 'button', style: styles.defaultBtn }, '上传附件');
|
||||
var uploadEl = Upload ? React.createElement(Upload, {
|
||||
accept: '.pdf',
|
||||
multiple: true,
|
||||
fileList: invoiceForm.invoiceFiles || [],
|
||||
onChange: function(info) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceFiles: info.fileList }); }); },
|
||||
beforeUpload: function() { return false; },
|
||||
showUploadList: true
|
||||
}, uploadButton) : React.createElement('div', { style: { fontSize: 14, color: '#999' } }, '(需加载 antd Upload)');
|
||||
var remarkEl = Input && Input.TextArea ? React.createElement(Input.TextArea, {
|
||||
placeholder: '选填,用于记录财务相关备注',
|
||||
value: invoiceForm.remark || '',
|
||||
onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); },
|
||||
rows: 3,
|
||||
style: { width: '100%' }
|
||||
}) : React.createElement('textarea', { style: Object.assign({}, styles.formInput, { minHeight: 60, resize: 'vertical' }), value: invoiceForm.remark || '', onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); }, placeholder: '选填,用于记录财务相关备注' });
|
||||
var invoiceCard = React.createElement('div', { style: styles.card },
|
||||
React.createElement('div', { style: styles.cardTitle }, '开票信息'),
|
||||
React.createElement('div', { style: styles.totalLine },
|
||||
React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'),
|
||||
React.createElement('span', { style: styles.totalAmount }, invoiceAmount + ' 元')
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockInvoice.customerName)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '纳税人识别号'), React.createElement('div', { style: styles.formValue }, mockInvoice.taxId)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.address))
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '电话'), React.createElement('div', { style: styles.formValue }, mockInvoice.phone)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '账户'), React.createElement('div', { style: styles.formValue }, mockInvoice.account)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开户行'), React.createElement('div', { style: styles.formValue }, mockInvoice.bank))
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '邮寄地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.mailingAddress)),
|
||||
React.createElement('div', { style: styles.formCol }, null),
|
||||
React.createElement('div', { style: styles.formCol }, null)
|
||||
),
|
||||
React.createElement('div', { style: styles.detailRow },
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开票时间'), React.createElement('div', { style: { width: '100%' } }, datePickerEl)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '发票附件'), React.createElement('div', { style: { width: '100%' } }, uploadEl)),
|
||||
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '备注'), React.createElement('div', { style: { width: '100%' } }, remarkEl))
|
||||
)
|
||||
);
|
||||
|
||||
var serviceModal = null;
|
||||
if (serviceModalRow !== null && mockVehicleList[serviceModalRow]) {
|
||||
var r = mockVehicleList[serviceModalRow];
|
||||
var serviceList = r.serviceItems || [];
|
||||
serviceModal = React.createElement('div', { style: styles.modalOverlay, onClick: function() { setServiceModalRow(null); } },
|
||||
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
|
||||
React.createElement('div', { style: styles.modalHeader },
|
||||
React.createElement('span', { style: styles.modalTitle }, '服务费项目'),
|
||||
React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setServiceModalRow(null); } }, '关闭')
|
||||
),
|
||||
React.createElement('table', { style: styles.table },
|
||||
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('tbody', null, serviceList.map(function(item, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.td }, item.name), React.createElement('td', { style: styles.td }, item.fee), React.createElement('td', { style: styles.td }, item.remark || '-')); }))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var footer = React.createElement('div', { style: styles.footer },
|
||||
React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: handleSubmit }, '提交'),
|
||||
React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: handleCancelClick }, '取消')
|
||||
);
|
||||
|
||||
var requirementModal = requirementVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setRequirementVisible(false); } },
|
||||
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
|
||||
React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '需求说明'), React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setRequirementVisible(false); } }, '关闭')),
|
||||
React.createElement('div', { style: styles.modalBody }, requirementContent)
|
||||
)
|
||||
) : null;
|
||||
|
||||
var cancelConfirmModal = cancelConfirmVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setCancelConfirmVisible(false); } },
|
||||
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
|
||||
React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '提示'), null),
|
||||
React.createElement('div', { style: { marginBottom: 20, fontSize: 14, color: '#333' } }, '取消将会丢失所有已添加数据,是否确认取消?'),
|
||||
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 12 } },
|
||||
React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: function() { handleCancelConfirm(false); } }, '否'),
|
||||
React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: function() { handleCancelConfirm(true); } }, '是')
|
||||
)
|
||||
)
|
||||
) : null;
|
||||
|
||||
return React.createElement('div', { style: styles.page },
|
||||
React.createElement('div', { style: styles.pageHeader },
|
||||
React.createElement('div', { style: styles.breadcrumb }, '租赁费用管理 > 提车应收款 > 收费明细 > 财务提交'),
|
||||
React.createElement('button', { type: 'button', style: styles.requirementLink, onClick: function() { setRequirementVisible(true); } }, '查看需求说明')
|
||||
),
|
||||
projectCard,
|
||||
vehicleBillCard,
|
||||
hydrogenCard,
|
||||
invoiceCard,
|
||||
footer,
|
||||
serviceModal,
|
||||
requirementModal,
|
||||
cancelConfirmModal
|
||||
);
|
||||
};
|
||||
532
web端/财务管理/提车收款单-编辑.jsx
Normal file
532
web端/财务管理/提车收款单-编辑.jsx
Normal file
File diff suppressed because one or more lines are too long
200
web端/需求说明/Antd设计规范(v5).md
Normal file
200
web端/需求说明/Antd设计规范(v5).md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Ant Design(AntD)设计规范导出(v5)
|
||||
|
||||
> 说明:本文档基于 Ant Design v5 官方文档与设计令牌(Design Token)整理,用于项目内统一 UI 口径。若与线上文档不一致,以官方文档与实际 `token` 输出为准。
|
||||
>
|
||||
> 参考:
|
||||
> - Ant Design 文档:`https://ant.design/docs/react/introduce`
|
||||
> - 主题定制(Design Token):`https://ant.design/docs/react/customize-theme`
|
||||
> - CSS Variables:`https://ant.design/docs/react/css-variables`
|
||||
|
||||
## 1. 设计原则(适用于业务后台)
|
||||
|
||||
- **清晰**:信息层级明确(标题/正文/辅助信息/弱提示),减少不必要装饰。
|
||||
- **一致**:同类型组件在同场景下外观与交互一致(按钮语义、表单校验、弹窗页脚等)。
|
||||
- **高效**:高频任务优先(表格筛选、快捷操作、批量处理),减少路径长度。
|
||||
- **可控**:可预期的反馈(加载/成功/失败/空态),避免“无响应”与不可逆操作。
|
||||
|
||||
## 2. 视觉基础(Design Token 口径)
|
||||
|
||||
### 2.1 色彩(Color)
|
||||
|
||||
#### 语义色(常用)
|
||||
|
||||
- **主色(Primary)**:品牌/主要操作按钮/高亮状态
|
||||
- AntD v5 默认:`colorPrimary = #1677ff`
|
||||
- **成功(Success)**:成功态、完成态、正向结果
|
||||
- **警告(Warning)**:需要注意但不阻断流程
|
||||
- **错误(Error)**:失败、阻断、危险操作
|
||||
- **信息(Info)**:中性提示(一般与 Primary 同系或更弱)
|
||||
|
||||
#### 文本与背景(建议层级)
|
||||
|
||||
- **正文主文本**:用于标题、正文关键值
|
||||
- **次级文本**:用于说明、辅助信息
|
||||
- **弱文本**:用于占位、不可用、次要信息
|
||||
- **容器背景**:页面背景、卡片/表格容器背景
|
||||
- **分割/边框**:分割线、边框、表格网格线
|
||||
|
||||
> 落地建议:**不要直接手写颜色值**,优先使用 AntD token(如 `colorText` / `colorBgContainer` / `colorBorder` / `colorPrimary`),避免暗色模式或主题切换时失控。
|
||||
|
||||
### 2.2 字体(Typography)
|
||||
|
||||
- **基础字号**:AntD v5 默认 `fontSize = 14px`
|
||||
- **字号体系**:在正文 14 的基础上,常用 `12/14/16/20/24`(具体以 token 为准)
|
||||
- **字重**:正文 400,标题/强调 500/600(按设计与字体实际支持)
|
||||
- **行高**:正文建议 1.4–1.6 区间;表格/表单密集场景可略收紧但要保证可读性
|
||||
|
||||
落地建议:
|
||||
- 表单 label、辅助说明、错误提示使用 **次级/弱** 文本层级,避免与正文抢占注意力。
|
||||
- 数字与金额在表格中建议 **右对齐**,并用等宽数字特性(如字体支持 `font-variant-numeric: tabular-nums`)提升可扫读性。
|
||||
|
||||
### 2.3 间距(Spacing)
|
||||
|
||||
推荐用 8px 网格作为基础节奏(token 会覆盖实际值):
|
||||
- **4/8/12/16/24/32**:常用间距档位
|
||||
- 表单项上下间距、卡片内边距、区块间距尽量统一
|
||||
|
||||
落地建议:
|
||||
- 页面区块:24(区块间)
|
||||
- 卡片/容器内边距:16 或 24
|
||||
- 表单项间距:16(常规)/ 12(密集)
|
||||
|
||||
### 2.4 圆角(Radius)
|
||||
|
||||
- AntD v5 默认 `borderRadius = 6px`
|
||||
常见分层:`2/4/6/8`(不同组件会使用不同层级)
|
||||
|
||||
落地建议:
|
||||
- 不要在同一页面混用多个“视觉上相近但不一致”的圆角(例如 4 与 6 随意混用)。
|
||||
|
||||
### 2.5 阴影与层级(Shadow & Elevation)
|
||||
|
||||
典型层级:
|
||||
- **容器浮层**:Popover/Dropdown/Tooltip
|
||||
- **对话框**:Modal
|
||||
- **抽屉**:Drawer(靠近边缘)
|
||||
|
||||
落地建议:
|
||||
- 阴影仅用于表达“悬浮/层级”,不要把阴影当分割线使用。
|
||||
|
||||
### 2.6 动效(Motion)
|
||||
|
||||
- **有意义**:动效用于表达状态变化(展开/收起/加载/反馈),避免纯装饰。
|
||||
- **短而稳**:后台系统建议偏短时长,降低等待感;重要确认可以稍长但不要拖沓。
|
||||
- **可中断**:用户快速操作时动效不应叠加导致卡顿或错位。
|
||||
|
||||
## 3. 布局与栅格(Layout & Grid)
|
||||
|
||||
- 使用 `Row/Col` 或 `Space/Flex` 组织布局,避免大量手写 margin 拼接。
|
||||
- 表单与筛选区建议固定结构:
|
||||
- **筛选区**(可折叠)
|
||||
- **操作区**(主按钮 + 次按钮 + 批量操作)
|
||||
- **表格区**
|
||||
- **分页/汇总**
|
||||
- 页面建议采用“标题 + 关键指标/说明 + 主体内容”层级,减少首屏信息噪声。
|
||||
|
||||
## 4. 组件使用规范(后台高频)
|
||||
|
||||
### 4.1 Button(按钮)
|
||||
|
||||
- **主按钮(Primary)**:每个视图/弹窗**尽量只有一个**主按钮(最主要动作)。
|
||||
- **次按钮(Default)**:辅助动作、返回、取消。
|
||||
- **危险(Danger)**:删除/作废/清空等不可逆操作,必须配确认(Popconfirm/Modal.confirm)。
|
||||
- **禁用态**:禁用必须有原因(提示或辅助文案),避免“为什么点不了”。
|
||||
|
||||
弹窗页脚建议顺序(从右到左):**主操作** → 次操作(取消/关闭)。
|
||||
|
||||
### 4.2 Form(表单)
|
||||
|
||||
- 校验提示就近展示,错误文案可读且可执行(告诉用户怎么改)。
|
||||
- 必填项统一标识;不要同页混用不同必填标记策略。
|
||||
- 长表单建议分组(Card/Collapse/Steps),并提供保存草稿/分步提交(视业务复杂度)。
|
||||
|
||||
### 4.3 Table(表格)
|
||||
|
||||
- 列宽:关键列固定宽度,描述列可自适应;避免全自适应导致抖动。
|
||||
- 对齐:金额/数量 **右对齐**;状态/标签可居中;文本默认左对齐。
|
||||
- 操作列:避免超过 3 个直出操作;其余收纳到 Dropdown/More。
|
||||
- 空态:提供原因与下一步(调整筛选/新建/刷新)。
|
||||
|
||||
### 4.4 Modal / Drawer(弹窗/抽屉)
|
||||
|
||||
- **Modal**:用于强打断、需要确认的任务;内容不宜过长。
|
||||
- **Drawer**:用于不中断上下文的编辑/详情;适合较长内容或分区信息。
|
||||
- 关闭方式:提供右上角关闭;重要内容关闭需二次确认(有未保存变更时)。
|
||||
|
||||
### 4.5 Message / Notification / Alert(反馈)
|
||||
|
||||
- **Message**:短反馈(保存成功/复制成功),不承载复杂信息。
|
||||
- **Notification**:较长信息、异步结果、可操作提示(带按钮/链接)。
|
||||
- **Alert**:页面内静态提示/规则说明/风险提示。
|
||||
|
||||
落地建议:错误信息尽量包含 **定位信息 + 原因 + 建议动作**(例如“金额格式不合法,请输入最多两位小数”)。
|
||||
|
||||
## 5. 主题与 Token “导出”(工程落地)
|
||||
|
||||
### 5.1 通过 `ConfigProvider` 统一主题
|
||||
|
||||
在应用入口用 `ConfigProvider` 设置全局 token(示意):
|
||||
|
||||
```tsx
|
||||
import { ConfigProvider, theme } from 'antd';
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
algorithm: theme.defaultAlgorithm,
|
||||
token: {
|
||||
colorPrimary: '#1677ff',
|
||||
borderRadius: 6,
|
||||
fontSize: 14,
|
||||
},
|
||||
components: {
|
||||
// 组件级 token 覆盖(按需)
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* routes */}
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 在页面/组件中打印当前 token(用于“导出当前主题规范”)
|
||||
|
||||
你可以在任意页面临时输出(开发态):
|
||||
|
||||
```tsx
|
||||
import { theme } from 'antd';
|
||||
|
||||
export function DebugTokens() {
|
||||
const { token } = theme.useToken();
|
||||
// 控制台复制即可作为“当前工程实际 token 导出”
|
||||
console.log('antd token =>', token);
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
如果需要生成 JSON 文件,可以把 `token` 序列化后下载(浏览器端):
|
||||
|
||||
```ts
|
||||
export function downloadJson(filename: string, data: unknown) {
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 默认关键值速查(AntD v5)
|
||||
|
||||
- **主色**:`#1677ff`
|
||||
- **基础字号**:`14px`
|
||||
- **默认圆角**:`6px`
|
||||
|
||||
> 注:完整 token 以 `theme.useToken()` 输出为准(因为你的工程可能有覆盖)。
|
||||
|
||||
49
web端/需求说明/财务管理/提车应收款
Normal file
49
web端/需求说明/财务管理/提车应收款
Normal file
@@ -0,0 +1,49 @@
|
||||
提车应收款(2026年3月10日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」模块
|
||||
#面包屑:财务管理-提车应收款;
|
||||
|
||||
页面分为2个卡片;
|
||||
1.筛选:
|
||||
#支持合同编码/项目名称/客户名称/业务部门/业务负责人等筛选方式;
|
||||
1.1.合同编码:选择器,支持输入框内手动输入下拉模糊匹配对应项;
|
||||
1.2.项目名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;
|
||||
1.3.客户名称:选择器,支持输入框内手动输入下拉模糊匹配对应项;
|
||||
1.4.业务部门:选择器,支持选择所有业务部门;
|
||||
1.5.业务负责人:选择器,支持选择所有业务负责人;
|
||||
|
||||
2.列表:
|
||||
#列表展示方式为:嵌套子表格,分为主表和子表,可点击主表展开子表,一个合同编码可以展开多个提车应收款明细;
|
||||
2.1.主表显示字段为:合同编码、合同类型、项目名称、客户名称、合同生效日期、业务部门、业务负责人、操作;
|
||||
2.1.1.合同编码:显示该提车应收款租赁合同对应合同编码;
|
||||
2.1.2.合同类型:显示该租赁合同对应合同类型,显示该合同为试用合同还是正式合同;
|
||||
2.1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
2.1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
2.1.5.合同生效日期:显示该租赁合同生效日期,格式为:YYYY-MM-DD;
|
||||
2.1.6.业务部门:显示该租赁合同对应业务部门名称;
|
||||
2.1.7.业务负责人:显示该租赁合同对应业务负责人;
|
||||
2.1.8.操作:提车收款单;
|
||||
2.1.8.1.提车收款单:点击提车收款单,跳转提车收款单页进行子表创建;
|
||||
主表右下角为分页符,支持选择单页显示多少条数据;
|
||||
|
||||
2.2.子表显示字段为:序号、审批状态、创建时间、提车数量、应收款总额、实收款总额、减免总金额、实际到账金额、是否已开票、已开票金额、操作;
|
||||
2.2.1.序号:按照提车首付款收费单据提交时间从最早开始显示第1条,按照顺序1.2.3....;
|
||||
2.2.2.审批状态:审批通过、待审批、审批中、已驳回;
|
||||
2.2.2.1.审批通过:该审批通过最终节点审批,显示为审批通过;
|
||||
2.2.2.2.待审批:审批提车收款单仅保存,但未提交审批;
|
||||
2.2.2.3.审批中:已发起审批流程,但未完成最终节点审批;
|
||||
2.2.2.4.审批驳回:发起审批流程,但任意节点被驳回。被驳回的提车收款单支持重新修改后提交审批;
|
||||
2.2.3.创建时间:显示提车收款单创建时间,格式为:YYYY-MM-DD HH:MM;
|
||||
2.2.4.创建人:显示已提车收款单创建人姓名;
|
||||
2.2.5.提车数量:显示该提车收费单对应提车数量,点击提车数量弹出气泡卡片,卡片中列表显示品牌、型号、车牌号;
|
||||
2.2.6.应收款金额:按照该提车收费单据应收款金额,格式为:xx.xx元;
|
||||
2.2.7.实收款金额:按照该提车收费单据实收款金额,格式为:xx.xx元;
|
||||
2.2.8.减免金额:按照该提车收费单减免总金额,显示当前子表对应减免总额,格式为:xx.xx元;
|
||||
2.2.9.实际到账金额:显示该提车收费单财务填写的实际到账金额,格式为:xx.xx元;
|
||||
2.2.10.是否已开票:显示财务是否完成开票,已开票显示为:已开票,未开票显示为:未开票,部分开票显示为:部分开票;
|
||||
2.2.11.已开票金额:显示财务已开票金额,格式为:xx.xx元;
|
||||
2.2.12.总计:显示应收款总额、实收款总额、减免总金额、实际到账金额、已开票金额等所有子表求和数据;
|
||||
2.2.13.操作:查看、编辑、删除、开票;
|
||||
2.2.12.1.查看:点击查看后,跳转提车应收款-查看页面,审批通过、待审批、审批中、审批驳回均可点击查看来查看提车收款单详情;
|
||||
2.2.12.2.编辑:点击编辑后,跳转提车应收款-编辑页面,待审批、审批驳回均可进行编辑操作;
|
||||
2.2.12.2.删除:待审批、审批驳回状态子表支持删除功能,点击弹出二次确认,点击确认后删除该记录;
|
||||
2.2.12.3.开票:点击开票后,进入提车应收款-开票页面,审批通过状态子表才显示,开票由财务人员操作,需要做权限控制,其他用户没有开票入口;
|
||||
69
web端/需求说明/财务管理/提车应收款-审批
Normal file
69
web端/需求说明/财务管理/提车应收款-审批
Normal file
@@ -0,0 +1,69 @@
|
||||
提车应收款-审批(2026年3月4日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「审批」模块
|
||||
#面包屑:财务管理-提车应收款-审批;
|
||||
|
||||
页面分为4个卡片,业务流程请参考流程图;
|
||||
1.项目信息:
|
||||
#显示项目详细信息,包括:
|
||||
1.1.合同编码:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同编码;
|
||||
1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;
|
||||
1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;
|
||||
1.7.合同生效时间:显示该租赁合同对应合同生效时间;
|
||||
1.8.合同结束时间:显示该租赁合同对应合同结束时间;
|
||||
1.9.业务部门:显示该租赁合同对应业务部门名称;
|
||||
1.10.业务负责人:显示该租赁合同对应业务负责人;
|
||||
|
||||
2.提车应收款信息:
|
||||
#上方显示应收款总额、实收款总额;
|
||||
2.1.应收款总额:显示提交审核时的应收款总额,格式为:xx.xx元,点击金额弹出气泡卡片,卡片中显示列表,字段为:
|
||||
2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;
|
||||
2.1.2.金额:显示该项目对应应收金额;
|
||||
2.2.实收款总额:显示提交审核时的实收款总额,格式为:xx.xx元,点击金额弹出气泡卡片,卡片中显示列表,字段为:
|
||||
2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;
|
||||
2.2.2.金额:显示该项目对应实收金额;
|
||||
#中间为车辆应收款明细,以列表展示提车应收款已选中所有车辆明细,包括以下字段:
|
||||
2.1.序号:根据租赁合同中车辆对应序号顺序展示;
|
||||
2.2.品牌:显示租赁合同中车辆对应品牌;
|
||||
2.3.型号:显示租赁合同中车辆对应型号;
|
||||
2.4.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);
|
||||
2.5.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;
|
||||
2.6.实收车辆月租金:显示提交审核时的实收车辆月租金,格式为:xx.xx元;
|
||||
2.7.车辆租金备注:显示提交审核时的车辆租金备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.8.减免金额:显示提交审核时的减免金额,格式为:xx.xx元;
|
||||
2.9.减免金额备注:显示提交审核时的减免金额备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.10.减免证明:显示所有附件,可点击预览;
|
||||
2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额,格式为:xx.xx元;
|
||||
2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.12.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.12.3.实收费用:显示提交审核时的实收费用金额,格式为:xx.xx元;
|
||||
2.12.4.减免费用:显示提交审核时的减免费用金额,格式为:xx.xx元;
|
||||
2.12.4.备注:显示提交审核时的服务费减免备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.15.列表下方对应字段下方显示总计数据,包括:总计应收车辆月租金、总计实收车辆月租金、总计应收车辆保证金、总计应收服务费、总计实收服务费、总计减免金额;
|
||||
列表不支持分页功能;
|
||||
#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:
|
||||
2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.20.氢费预付款实收金额:显示提交审核时的氢费预付款实收金额,格式为:xx.xx元;
|
||||
2.21.减免金额:显示提交审核时的减免金额,格式为:xx.xx元;
|
||||
2.22.减免金额备注:显示提交审核时的氢费预付款减免备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
#最底部为开票方式、开票备注;
|
||||
2.23.开票方式:显示提交审核时的开票方式;
|
||||
2.24.开票备注:显示提交审核时的开票备注;
|
||||
|
||||
3.审核情况:
|
||||
#上方为纵向步骤条,显示所有审批步骤
|
||||
3.1.步骤中显示部门名称,部门名称后方显示审批状态、审批人、审批时间;
|
||||
3.1.1.部门名称:当前节点部门名称;
|
||||
3.1.2.审批状态:分为待审批、审批通过、审批驳回;
|
||||
3.1.3.审批人:步骤节点对应审批人姓名;
|
||||
3.1.4.审批时间:显示审批通过/驳回时间,格式为:YYYY-MM-DD HH:MM;
|
||||
3.2.当前步骤的审批人下方为审批说明:非必填,文本域,支持自定义输入;
|
||||
3.3.底部为审批通过、驳回、取消;
|
||||
3.3.1.审批通过:点击提示:审批完成,同时下个流程节点审批人工作台待办任务中收到审批任务和消息提示;如当前已是最后节点,则完成审批,同时进入财务开票环节;
|
||||
4.3.2.驳回:点击进行二次确认,二次确认后提示:驳回成功,同时该任务在列表中显示为审核驳回,发起人可重新编辑;
|
||||
4.3.3.取消:点击返回提车应收款列表页;
|
||||
82
web端/需求说明/财务管理/提车应收款-开票信息
Normal file
82
web端/需求说明/财务管理/提车应收款-开票信息
Normal file
@@ -0,0 +1,82 @@
|
||||
提车应收款-开票信息(2026年3月5日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「开票信息」模块
|
||||
#面包屑:财务管理-提车应收款-开票信息;
|
||||
|
||||
页面分为3个卡片,业务流程请参考流程图;
|
||||
1.项目信息:
|
||||
#显示项目详细信息,包括:
|
||||
1.1.合同编码:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同编码;
|
||||
1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;
|
||||
1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;
|
||||
1.7.合同生效时间:显示该租赁合同对应合同生效时间;
|
||||
1.8.合同结束时间:显示该租赁合同对应合同结束时间;
|
||||
1.9.业务部门:显示该租赁合同对应业务部门名称;
|
||||
1.10.业务负责人:显示该租赁合同对应业务负责人;
|
||||
|
||||
22.提车应收款信息:
|
||||
#上方显示应收款总额、实收款总额;
|
||||
2.1.应收款总额:显示提交审核时的应收款总额,格式为:xx.xx元,点击金额弹出气泡卡片,卡片中显示列表,字段为:
|
||||
2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;
|
||||
2.1.2.金额:显示该项目对应应收金额;
|
||||
2.2.实收款总额:显示提交审核时的实收款总额,格式为:xx.xx元,点击金额弹出气泡卡片,卡片中显示列表,字段为:
|
||||
2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;
|
||||
2.2.2.金额:显示该项目对应实收金额;
|
||||
#中间为车辆应收款明细,以列表展示提车应收款已选中所有车辆明细,包括以下字段:
|
||||
2.1.序号:根据租赁合同中车辆对应序号顺序展示;
|
||||
2.2.品牌:显示租赁合同中车辆对应品牌;
|
||||
2.3.型号:显示租赁合同中车辆对应型号;
|
||||
2.4.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);
|
||||
2.5.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;
|
||||
2.6.实收车辆月租金:显示提交审核时的实收车辆月租金,格式为:xx.xx元;
|
||||
2.7.车辆租金备注:显示提交审核时的车辆租金备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.8.减免金额:显示提交审核时的减免金额,格式为:xx.xx元;
|
||||
2.9.减免金额备注:显示提交审核时的减免金额备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.10.减免证明:显示所有附件,可点击预览;
|
||||
2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额,格式为:xx.xx元;
|
||||
2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.12.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.12.3.实收费用:显示提交审核时的实收费用金额,格式为:xx.xx元;
|
||||
2.12.4.减免费用:显示提交审核时的减免费用金额,格式为:xx.xx元;
|
||||
2.12.4.备注:显示提交审核时的服务费减免备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.15.列表下方对应字段下方显示总计数据,包括:总计应收车辆月租金、总计实收车辆月租金、总计应收车辆保证金、总计应收服务费、总计实收服务费、总计减免金额;
|
||||
列表不支持分页功能;
|
||||
#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:
|
||||
2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.20.氢费预付款实收金额:显示提交审核时的氢费预付款实收金额,格式为:xx.xx元;
|
||||
2.21.减免金额:显示提交审核时的减免金额,格式为:xx.xx元;
|
||||
2.22.减免金额备注:显示提交审核时的氢费预付款减免备注,超长则显示...,悬浮时显示全部详细内容;
|
||||
#最底部为开票方式、开票备注;
|
||||
2.23.开票方式:显示提交审核时的开票方式;
|
||||
2.24.开票备注:显示提交审核时的开票备注;
|
||||
|
||||
3.开票信息:
|
||||
#上方显示该租赁合同客户信息(如果该合同转为三方合同,则未开票部分开票信息修改为丙方开票信息),包括:
|
||||
3.1.客户名称:显示租赁合同客户名称;
|
||||
3.2.纳税人识别号:显示该客户对应纳税人识别号;
|
||||
3.3.地址:显示该客户对应地址;
|
||||
3.4.电话:显示该客户对应电话;
|
||||
3.5.账户:显示该客户对应账户;
|
||||
3.6.开户行:显示该客户对应开户行;
|
||||
3.7.邮寄地址:显示该客户对应邮寄地址;
|
||||
#下方显示列表,包括以下字段,列表上方显示未付金额,格式为xx.xx元(计算方式为:实收款总额-(所有到账金额记录总和)),列表显示过往提交的历史记录,同时默认一行可编辑,支持继续新增一行和删除行操作(过往提交的历史记录无法删除):
|
||||
3.8.到账时间:时间选择器,格式为:YYYY-MM-DD HH:MM,新增未提交的记录支持删除,但之前已完成提交的记录无法删除;
|
||||
3.9.到账金额:输入框,支持2位小数,格式为:xx.xx元,新增未提交的记录支持删除,但之前已完成提交的记录无法删除;;
|
||||
3.10.开票时间:时间选择器,格式为:YYYY-MM-DD HH:MM,默认为打开页面的时间,新增未提交的记录支持删除,但之前已完成提交的记录无法删除;
|
||||
3.11.发票附件:附件上传按钮,支持多附件上传,上传后显示附件名称,新增未提交的记录支持删除,但之前已完成提交的记录无法删除;
|
||||
3.12.备注:输入框,新增未提交的记录支持删除,但之前已完成提交的记录无法删除;;
|
||||
3.13.开票人:显示上传发票的操作用户姓名;
|
||||
3.14.支持新增一行、删除一行(新增未提交的记录可以删除,已完成提交的历史记录不能删除);
|
||||
|
||||
4.审批情况:
|
||||
#上方为纵向步骤条,显示所有审批步骤
|
||||
4.1.步骤中显示部门名称,部门名称后方显示审批状态、审批人、审批时间,所有审批记录已完成;
|
||||
|
||||
5.底部为提交,取消;
|
||||
5.1.提交:点击提交,进行二次确认,文案为:请仔细核对提车首付款到账和开票信息,提交后无法修改;
|
||||
5.2.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;
|
||||
68
web端/需求说明/财务管理/提车应收款-提车收款单-编辑
Normal file
68
web端/需求说明/财务管理/提车应收款-提车收款单-编辑
Normal file
@@ -0,0 +1,68 @@
|
||||
提车应收款-提车收款单-编辑(2026年3月10日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「提车收款单」「编辑」模块
|
||||
#面包屑:财务管理-提车应收款-提车收款单-编辑;
|
||||
|
||||
页面分为3个卡片,业务流程请参考流程图;
|
||||
1.项目信息:
|
||||
#显示项目详细信息,包括:
|
||||
1.1.合同编码:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同类型;
|
||||
1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;
|
||||
1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;
|
||||
1.7.合同生效时间:显示该租赁合同对应合同生效时间;
|
||||
1.8.合同结束时间:显示该租赁合同对应合同结束时间;
|
||||
1.9.业务部门:显示该租赁合同对应业务部门名称;
|
||||
1.10.业务负责人:显示该租赁合同对应业务负责人;
|
||||
|
||||
2.提车应收款信息:
|
||||
#上方显示应收款总额、实收款总额;
|
||||
2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」;
|
||||
点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;
|
||||
2.1.2.金额:显示该项目对应应收金额;
|
||||
2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」;
|
||||
点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;
|
||||
2.2.2.金额:显示该项目对应实收金额;
|
||||
|
||||
#中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段:
|
||||
2.1.全选/多选:多选按钮组,支持表头点击全选,编辑页面显示此前保存时已选中相关车辆,支持重新选择车辆;
|
||||
提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款;
|
||||
2.2.序号:根据租赁合同中车辆对应序号顺序展示;
|
||||
2.3.品牌:显示租赁合同中车辆对应品牌;
|
||||
2.4.型号:显示租赁合同中车辆对应型号;
|
||||
2.5.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);
|
||||
2.6.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;
|
||||
2.7.实收车辆月租金:必填项,输入框,默认反写实收车辆月租金,支持修改,由业务员自行输入车辆月租金金额,精确至2位小数,后缀为元;
|
||||
2.8.车辆租金备注:选填项,输入框,由业务员自行输入;
|
||||
2.9.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.10.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额;
|
||||
2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.12.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.12.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;
|
||||
2.12.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;
|
||||
2.12.4.备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.15.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.16.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.17.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式,支持多附件上传;
|
||||
|
||||
列表不支持分页功能;
|
||||
#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:
|
||||
2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.20.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;
|
||||
2.21.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;
|
||||
2.22.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;
|
||||
#最底部为开票方式、开票备注;
|
||||
2.23.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;
|
||||
2.24.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;
|
||||
|
||||
3.底部为提交审核,保存,取消;
|
||||
3.1.提交审核:点击提交审核,进行二次确认,文案为:请仔细核对提车首付款实收金额,点击确认则进入工作流;
|
||||
3.2.保存:点击保存,提示保存成功,跳转至提车应收款列表页,同时该条数据审核状态为:待提交:
|
||||
3.3.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;
|
||||
@@ -1,12 +1,12 @@
|
||||
提车应收款-收款(2026年3月4日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「收款」模块
|
||||
#面包屑:财务管理-提车应收款-收款;
|
||||
提车应收款-提车收款单(2026年3月10日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「提车收款单」模块
|
||||
#面包屑:财务管理-提车应收款-提车收款单;
|
||||
|
||||
页面分为3个卡片,业务流程请参考流程图;
|
||||
1.项目信息:
|
||||
#显示项目详细信息,包括:
|
||||
1.1.合同编码:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同类型;
|
||||
1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;
|
||||
@@ -18,17 +18,19 @@
|
||||
|
||||
2.提车应收款信息:
|
||||
#上方显示应收款总额、实收款总额;
|
||||
2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「总计应收车辆月租金」+「总计应收车辆保证金」+「总计应收服务费」;
|
||||
2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」;
|
||||
点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;
|
||||
2.1.2.金额:显示该项目对应应收金额;
|
||||
2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「总计实收车辆月租金」+「总计应收车辆保证金」+「总计实收服务费」-「总计减免金额」;
|
||||
2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」;
|
||||
点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;
|
||||
2.2.2.金额:显示该项目对应实收金额;
|
||||
|
||||
#中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段:
|
||||
2.1.全选/多选:多选按钮组,支持表头点击全选;
|
||||
2.1.全选/多选:多选按钮组,支持表头点击全选,默认为取消勾选(整个车辆清单);
|
||||
选择代表本次提车应收款只收取该部分车辆费用,可以编辑以下相应字段,不选择则代表本次提车应收款不收取该部分车辆费用,不能编辑车辆相应字段;
|
||||
提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款;
|
||||
2.2.序号:根据租赁合同中车辆对应序号顺序展示;
|
||||
2.3.品牌:显示租赁合同中车辆对应品牌;
|
||||
2.4.型号:显示租赁合同中车辆对应型号;
|
||||
@@ -36,44 +38,32 @@
|
||||
2.6.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;
|
||||
2.7.实收车辆月租金:必填项,输入框,默认反写实收车辆月租金,支持修改,由业务员自行输入车辆月租金金额,精确至2位小数,后缀为元;
|
||||
2.8.车辆租金备注:选填项,输入框,由业务员自行输入;
|
||||
2.9.应收车辆保证金:显示租赁合同中当前车辆保证金金额;
|
||||
2.10.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.10.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.10.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.10.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;
|
||||
2.10.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;
|
||||
2.10.4.备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.11.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.12.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.13.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.14.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.15.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式;
|
||||
2.16.列表下方对应字段下方显示总计数据,包括:总计应收车辆月租金、总计实收车辆月租金、总计应收车辆保证金、总计应收服务费、总计实收服务费、总计减免金额;
|
||||
2.9.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.10.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额;
|
||||
2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.12.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.12.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;
|
||||
2.12.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;
|
||||
2.12.4.备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.15.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.16.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.17.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式,支持多附件上传;
|
||||
|
||||
列表不支持分页功能;
|
||||
#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:
|
||||
2.17.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.18.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;
|
||||
2.19.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;
|
||||
2.20.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;
|
||||
2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.20.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;
|
||||
2.21.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;
|
||||
2.22.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;
|
||||
#最底部为开票方式、开票备注;
|
||||
2.21.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;
|
||||
2.22.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;
|
||||
2.23.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;
|
||||
2.24.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;
|
||||
|
||||
3.开票信息:
|
||||
#上方显示该租赁合同客户信息(如果该合同转为三方合同,则未开票部分开票信息修改为丙方开票信息),包括:
|
||||
3.1.客户名称:显示租赁合同客户名称;
|
||||
3.2.纳税人识别号:显示该客户对应纳税人识别号;
|
||||
3.3.地址:显示该客户对应地址;
|
||||
3.4.电话:显示该客户对应电话;
|
||||
3.5.账户:显示该客户对应账户;
|
||||
3.6.开户行:显示该客户对应开户行;
|
||||
3.7.邮寄地址:显示该客户对应邮寄地址;
|
||||
#下方显示
|
||||
3.8.开票时间:格式为:YYYY-MM-DD HH:MM ,如未上传发票则显示未上传;
|
||||
3.9.发票附件:显示已上传发票附件,格式为:发票文件名称.文件类型,如未上传发票附件,则显示未上传;
|
||||
3.10.开票人:显示上传发票的操作用户姓名,如未上传则显示-;
|
||||
|
||||
4.底部为提交审核,保存,取消;
|
||||
4.1.提交审核:点击提交审核,进行二次确认,文案为:请仔细核对提车首付款实收金额,点击确认则进入工作流;
|
||||
4.2.保存:点击保存,提示保存成功,跳转至提车应收款列表页,同时该条数据审核状态为:待提交:
|
||||
4.3.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;
|
||||
3.底部为提交审核,保存,取消;
|
||||
3.1.提交审核:点击提交审核,进行二次确认,文案为:请仔细核对提车首付款实收金额,点击确认则进入工作流;
|
||||
3.2.保存:点击保存,提示保存成功,跳转至提车应收款列表页,同时该条数据审核状态为:待提交:
|
||||
3.3.取消:点击取消,进行二次确认,文案为:取消将会丢失所有已填写数据,是否确认,点击确认则跳转至提车应收款列表页;
|
||||
83
web端/需求说明/财务管理/提车应收款-查看
Normal file
83
web端/需求说明/财务管理/提车应收款-查看
Normal file
@@ -0,0 +1,83 @@
|
||||
提车应收款-查看(2026年3月10日版本)
|
||||
一个「数字化资产ONEOS运管平台」中的「财务管理」「提车应收款」「提车收款单」模块
|
||||
#面包屑:财务管理-提车应收款-提车收款单;
|
||||
|
||||
页面分为3个卡片,业务流程请参考流程图;
|
||||
1.项目信息:
|
||||
#显示项目详细信息,包括:
|
||||
1.1.合同编码:显示该租赁合同对应合同编码;
|
||||
1.2.合同类型:显示该租赁合同对应合同类型;
|
||||
1.3.项目名称:显示该租赁合同对应项目名称;
|
||||
1.4.客户名称:显示该租赁合同对应客户名称;
|
||||
1.5.付款方式:显示该租赁合同对应付款方式,分为:预付、后付两种,根据实际合同反写;
|
||||
1.6.付款周期:显示该租赁合同对应付款周期,格式为:x个月;
|
||||
1.7.合同生效时间:显示该租赁合同对应合同生效时间;
|
||||
1.8.合同结束时间:显示该租赁合同对应合同结束时间;
|
||||
1.9.业务部门:显示该租赁合同对应业务部门名称;
|
||||
1.10.业务负责人:显示该租赁合同对应业务负责人;
|
||||
|
||||
2.提车应收款信息:
|
||||
#上方显示应收款总额、实收款总额;
|
||||
2.1.应收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆应收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆应收服务费总和」;
|
||||
点击应收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.1.1.项目:包括总计应收车辆月租金、总计应收车辆保证金、总计应收服务费三项;
|
||||
2.1.2.金额:显示该项目对应应收金额;
|
||||
2.2.实收款总额:格式为:xx.xx元,重点色,计算方式为:「选中车辆实收车辆月租金总和」+「选中车辆应收车辆保证金总和」+「选中车辆实收服务费总和」-「选中车辆减免金额总和」;
|
||||
点击实收款总额金额,弹出气泡卡片,气泡卡片列表展示:项目、金额;
|
||||
2.2.1.项目:包括总计实收车辆月租金、总计应收车辆保证金、总计实收服务费、总计减免金额四项;
|
||||
2.2.2.金额:显示该项目对应实收金额;
|
||||
|
||||
#中间为车辆应收款明细,以列表展示租赁合同中所有车辆明细,包括以下字段:
|
||||
2.1.全选/多选:多选按钮组,支持表头点击全选,默认为取消勾选(整个车辆清单);
|
||||
选择代表本次提车应收款只收取该部分车辆费用,可以编辑以下相应字段,不选择则代表本次提车应收款不收取该部分车辆费用,不能编辑车辆相应字段;
|
||||
提车应收款支持分批次进行收款,如果该车辆之前已提交提车应收款流程,则多选框不能勾选,悬浮多选框时提示:该车辆已完成提车应收款;
|
||||
2.2.序号:根据租赁合同中车辆对应序号顺序展示;
|
||||
2.3.品牌:显示租赁合同中车辆对应品牌;
|
||||
2.4.型号:显示租赁合同中车辆对应型号;
|
||||
2.5.车牌号:显示租赁合同中车辆对应车牌号,如果车牌号为空则显示为-(后续如果交车成功,则车牌号会反显在该处);
|
||||
2.6.应收车辆月租金:根据租赁合同付款周期自动计算,例如:付款周期为6个月,则这里显示金额为:车辆月租金*6;
|
||||
2.7.实收车辆月租金:必填项,输入框,默认反写实收车辆月租金,支持修改,由业务员自行输入车辆月租金金额,精确至2位小数,后缀为元;
|
||||
2.8.车辆租金备注:选填项,输入框,由业务员自行输入;
|
||||
2.9.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.10.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.11.应收车辆保证金:显示租赁合同中当前车辆保证金金额;
|
||||
2.12.服务费项目:点击管理,弹出气泡卡片,气泡卡片标题为服务费项目,下方为列表,显示服务项目、应收费用、实收费用、备注;
|
||||
2.12.1.服务项目:显示租赁合同中所有服务项目名称;
|
||||
2.12.2.应收费用:显示租赁合同中所有服务项目对应费用;
|
||||
2.12.3.实收费用:必填项,输入框,默认反写应收费用,支持修改,由业务员自行输入实收费用金额,精确至2位小数,后缀为元;
|
||||
2.12.4.减免费用:选填项,输入框,默认为:0.00,精确至2位小数,后缀为元;
|
||||
2.12.4.备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.13.应收服务费:显示当前车辆所有服务费应收费用总和,格式为:xx.xx元;
|
||||
2.14.实收服务费:显示当前车辆所有服务费实收费用总和,格式为:xx.xx元;
|
||||
2.15.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为0.00,支持2位小数,后缀为元;
|
||||
2.16.减免金额备注:选填项,输入框,由业务员自行输入备注信息;
|
||||
2.17.减免证明:选填项,附件上传按钮,点击后上传本地文件,支持:jpg、png、pdf等格式,支持多附件上传;
|
||||
2.18.列表下方对应字段下方显示总计数据,包括:总计应收车辆月租金、总计实收车辆月租金、总计应收车辆保证金、总计应收服务费、总计实收服务费、总计减免金额;
|
||||
列表不支持分页功能;
|
||||
#下方为氢费预付款情况,以列表展示租赁合同中所有氢费预付款明细,包括以下字段,如租赁合同氢费非预付款模式,则不显示该部分内容:
|
||||
2.19.氢费预付款应收金额:显示租赁合同中氢费预付款金额,格式为:xx.xx元,支持2位小数;
|
||||
2.20.氢费预付款实收金额:必填项,输入框,默认反写氢费预付款应收金额,支持修改,由业务员自行输入金额,格式为:xx.xx元,支持2位小数;
|
||||
2.21.减免金额:选填项,输入框,由业务员自行输入减免金额,默认为:0.00元,格式为:xx.xx元,支持2位小数;
|
||||
2.22.减免金额备注:选填项,输入框,由业务员自行输入减免金额备注信息;
|
||||
#最底部为开票方式、开票备注;
|
||||
2.23.开票方式:选择器,选项包括:先开票后付款、先付款后开票,默认为先开票后付款;
|
||||
2.24.开票备注:必填项,文本域,默认提示文本为:请输入开票项目、税率以及其他备注信息,财务将以此进行开票;
|
||||
|
||||
3.客户付款信息:
|
||||
#显示列表,包括以下字段,列表上方显示未付金额,格式为xx.xx元(计算方式为:实收款总额-(所有到账金额记录总和)),列表显示过往提交的历史记录;
|
||||
该部分数据由V1.2版本从YS系统对接获取;
|
||||
3.1.到账时间:显示实际到账时间,格式为:YYYY-MM-DD HH:MM;
|
||||
3.2.到账金额:显示实际到账金额,支持2位小数,格式为:xx.xx元;
|
||||
3.3.开票时间:显示实际开票时间,格式为:YYYY-MM-DD HH:MM;
|
||||
3.4.发票附件:显示附件名称,支持点击预览,支持多附件;
|
||||
3.5.备注:显示实际开票备注信息,超长则显示...,悬浮时显示全部详细内容;
|
||||
|
||||
4.审批情况:
|
||||
#显示竖向步骤条,显示各流程节点部门、审批状态、审批人、审批时间;
|
||||
4.1.部门:显示部门名称;
|
||||
4.2.审批状态:显示当前节点审批状态;
|
||||
4.3.审批人:显示当前节点审批人;
|
||||
4.4.审批时间:显示当前节点审批时间,格式为:YYYY-MM-DD HH:MM;
|
||||
|
||||
5.底部为返回;
|
||||
5.1.返回:点击跳转提车应收款列表页;
|
||||
Reference in New Issue
Block a user